From 6ebeb4bdb58b098bc4b9da1b124f14f224c45d4f Mon Sep 17 00:00:00 2001 From: Ivan Mincik Date: Wed, 10 Jul 2024 16:29:25 +0000 Subject: [PATCH 001/471] CI: automatic update of nix lock file (#4016) --- .github/workflows/test-nix.yml | 6 ++++++ renovate.json5 | 10 +++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-nix.yml b/.github/workflows/test-nix.yml index 42b5eee93a5..f9d5297bef6 100644 --- a/.github/workflows/test-nix.yml +++ b/.github/workflows/test-nix.yml @@ -7,6 +7,12 @@ on: push: tags: - '*' + pull_request: + paths: + - 'flake.nix' + - 'flake.lock' + - 'package.nix' + - '.github/workflows/test-nix.yml' workflow_dispatch: concurrency: diff --git a/renovate.json5 b/renovate.json5 index f0d45a3e2eb..76c6cf5cf13 100644 --- a/renovate.json5 +++ b/renovate.json5 @@ -19,5 +19,13 @@ // Renovate's pre-commit support is still opt-in ":enablePreCommit", - ] + + // weekly update of lock files (flake.lock) + ":maintainLockFilesWeekly", + ], + + // enable Nix lock file update (flake.lock) + "nix": { + "enabled": true + } } From 152f4930e4d8fda2652e5fbc8ac4e188d9927c0e Mon Sep 17 00:00:00 2001 From: Ivan Mincik Date: Wed, 10 Jul 2024 16:29:39 +0000 Subject: [PATCH 002/471] nix: pin python version to 3.11 (#4017) * nix: pin python version to 3.11 * nix: update flake.lock file * nix: use default wxpython version for package --- flake.lock | 20 ++++++++-------- flake.nix | 67 ++++++++++++++++++++++++++++------------------------- package.nix | 12 ++++++---- 3 files changed, 54 insertions(+), 45 deletions(-) diff --git a/flake.lock b/flake.lock index ff511760ebb..2237ffc975b 100644 --- a/flake.lock +++ b/flake.lock @@ -5,11 +5,11 @@ "nixpkgs-lib": "nixpkgs-lib" }, "locked": { - "lastModified": 1717285511, - "narHash": "sha256-iKzJcpdXih14qYVcZ9QC9XuZYnPc6T8YImb6dX166kw=", + "lastModified": 1719994518, + "narHash": "sha256-pQMhCCHyQGRzdfAkdJ4cIWiw+JNuWsTX7f0ZYSyz0VY=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "2a55567fcf15b1b1c7ed712a2c6fadaec7412ea8", + "rev": "9227223f6d922fee3c7b190b2cc238a99527bbb7", "type": "github" }, "original": { @@ -19,11 +19,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1718428119, - "narHash": "sha256-WdWDpNaq6u1IPtxtYHHWpl5BmabtpmLnMAx0RdJ/vo8=", + "lastModified": 1720571246, + "narHash": "sha256-nkUXwunTck+hNMt2wZuYRN+jf2ySRjKTzI0fo5TDH78=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "e6cea36f83499eb4e9cd184c8a8e823296b50ad5", + "rev": "16e401f01842c5bb2499e78c1fe227f939c0c474", "type": "github" }, "original": { @@ -35,14 +35,14 @@ }, "nixpkgs-lib": { "locked": { - "lastModified": 1717284937, - "narHash": "sha256-lIbdfCsf8LMFloheeE6N31+BMIeixqyQWbSr2vk79EQ=", + "lastModified": 1719876945, + "narHash": "sha256-Fm2rDDs86sHy0/1jxTOKB1118Q0O3Uc7EC0iXvXKpbI=", "type": "tarball", - "url": "https://github.com/NixOS/nixpkgs/archive/eb9ceca17df2ea50a250b6b27f7bf6ab0186f198.tar.gz" + "url": "https://github.com/NixOS/nixpkgs/archive/5daf0514482af3f97abaefc78a6606365c9108e2.tar.gz" }, "original": { "type": "tarball", - "url": "https://github.com/NixOS/nixpkgs/archive/eb9ceca17df2ea50a250b6b27f7bf6ab0186f198.tar.gz" + "url": "https://github.com/NixOS/nixpkgs/archive/5daf0514482af3f97abaefc78a6606365c9108e2.tar.gz" } }, "root": { diff --git a/flake.nix b/flake.nix index 49bd16561b7..51174e1da74 100644 --- a/flake.nix +++ b/flake.nix @@ -21,37 +21,42 @@ packages.grass = pkgs.callPackage ./package.nix { }; - devShells.default = pkgs.mkShell { - inputsFrom = [ self'.packages.grass ]; - - # additional packages - buildInputs = with pkgs.python3Packages; [ - pytest - ]; - - shellHook = '' - function dev-help { - echo -e "\nWelcome to a GRASS development environment !" - echo "Build GRASS using following commands:" - echo - echo " 1. ./configure --prefix=\$(pwd)/app" - echo " 2. make -j$(nproc)" - echo " 3. make install" - echo - echo "Run tests:" - echo - echo " 1. export PYTHONPATH=\$(app/bin/grass --config python_path):\$PYTHONPATH" - echo " 2. export LD_LIBRARY_PATH=\$(app/bin/grass --config path)/lib:\$LD_LIBRARY_PATH" - echo " 3. pytest" - echo - echo "Note: run 'nix flake update' from time to time to update dependencies." - echo - echo "Run 'dev-help' to see this message again." - } - - dev-help - ''; - }; + devShells.default = + let + pyPackages = pkgs.python311Packages; + + in + pkgs.mkShell { + inputsFrom = [ self'.packages.grass ]; + + # additional packages + buildInputs = with pyPackages; [ + pytest + ]; + + shellHook = '' + function dev-help { + echo -e "\nWelcome to a GRASS development environment !" + echo "Build GRASS using following commands:" + echo + echo " 1. ./configure --prefix=\$(pwd)/app" + echo " 2. make -j$(nproc)" + echo " 3. make install" + echo + echo "Run tests:" + echo + echo " 1. export PYTHONPATH=\$(app/bin/grass --config python_path):\$PYTHONPATH" + echo " 2. export LD_LIBRARY_PATH=\$(app/bin/grass --config path)/lib:\$LD_LIBRARY_PATH" + echo " 3. pytest" + echo + echo "Note: run 'nix flake update' from time to time to update dependencies." + echo + echo "Run 'dev-help' to see this message again." + } + + dev-help + ''; + }; }; flake = { }; diff --git a/package.nix b/package.nix index b1b33492163..7abbb15275d 100644 --- a/package.nix +++ b/package.nix @@ -26,15 +26,19 @@ , pkg-config , postgresql , proj -, python3Packages +, python311Packages , readline , sqlite , wxGTK32 , zlib , zstd - }: + +let + pyPackages = python311Packages; + +in stdenv.mkDerivation (finalAttrs: { pname = "grass"; version = "dev"; @@ -61,7 +65,7 @@ stdenv.mkDerivation (finalAttrs: { geos # for `geos-config` netcdf # for `nc-config` pkg-config - ] ++ (with python3Packages; [ python-dateutil numpy wxPython_4_2 ]); + ] ++ (with pyPackages; [ python-dateutil numpy wxpython ]); buildInputs = [ blas @@ -137,7 +141,7 @@ stdenv.mkDerivation (finalAttrs: { postInstall = '' wrapProgram $out/bin/grass \ --set PYTHONPATH $PYTHONPATH \ - --set GRASS_PYTHON ${python3Packages.python.interpreter} \ + --set GRASS_PYTHON ${pyPackages.python.interpreter} \ --suffix LD_LIBRARY_PATH ':' '${gdal}/lib' ln -s $out/grass*/lib $out/lib ln -s $out/grass*/include $out/include From 2186ec5208a890829f41ebbfb2246d5db1e3b017 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 10 Jul 2024 12:36:21 -0400 Subject: [PATCH 003/471] CI(deps): Update actions/setup-python action to v5.1.1 (#4019) --- .github/workflows/additional_checks.yml | 2 +- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/create_release_draft.yml | 2 +- .github/workflows/pytest.yml | 2 +- .github/workflows/python-code-quality.yml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/additional_checks.yml b/.github/workflows/additional_checks.yml index 98ceb8cab8c..74602e7910b 100644 --- a/.github/workflows/additional_checks.yml +++ b/.github/workflows/additional_checks.yml @@ -43,7 +43,7 @@ jobs: exclude: mswindows .*\.bat .*/testsuite/data/.* - name: Set up Python - uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0 + uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 with: python-version: '3.10' diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 491f9c2f170..0cff48db3f2 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -42,7 +42,7 @@ jobs: - name: Checkout repository uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Set up Python - uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0 + uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 with: python-version: '3.x' - name: Install non-Python dependencies diff --git a/.github/workflows/create_release_draft.yml b/.github/workflows/create_release_draft.yml index 11f806e2f55..dcaa6d337b1 100644 --- a/.github/workflows/create_release_draft.yml +++ b/.github/workflows/create_release_draft.yml @@ -35,7 +35,7 @@ jobs: ref: ${{ github.ref }} fetch-depth: 0 - name: Set up Python - uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0 + uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 with: python-version: '3.11' - name: Create output directory diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 3a91534a80b..1600be8fda6 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -34,7 +34,7 @@ jobs: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Set up Python - uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0 + uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 with: python-version: ${{ matrix.python-version }} cache: pip diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index 802dc18b227..bf068a86377 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -57,7 +57,7 @@ jobs: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Set up Python - uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0 + uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 with: python-version: ${{ env.PYTHON_VERSION }} cache: pip From d41987cf44a1546bf75c9ccc1732ce79c738c855 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Wed, 10 Jul 2024 16:40:45 -0400 Subject: [PATCH 004/471] style: Fix repeated-append (FURB113) (#4014) --- gui/wxpython/core/settings.py | 17 +++--- gui/wxpython/datacatalog/dialogs.py | 36 +++++++----- gui/wxpython/gcp/manager.py | 12 ++-- gui/wxpython/gui_core/query.py | 10 +++- gui/wxpython/image2target/ii2t_manager.py | 16 ++++-- gui/wxpython/iscatt/plots.py | 5 +- gui/wxpython/mapwin/buffered.py | 14 +++-- gui/wxpython/modules/extensions.py | 3 +- gui/wxpython/photo2image/ip2i_manager.py | 12 ++-- gui/wxpython/psmap/frame.py | 3 +- gui/wxpython/timeline/frame.py | 12 ++-- gui/wxpython/tplot/frame.py | 5 +- gui/wxpython/vnet/dialogs.py | 8 ++- gui/wxpython/vnet/vnet_core.py | 17 +++--- gui/wxpython/vnet/vnet_data.py | 3 +- man/parser_standard_options.py | 20 +++---- pyproject.toml | 1 - python/grass/gunittest/runner.py | 44 +++++++------- .../pygrass/vector/testsuite/test_geometry.py | 18 +++--- python/grass/temporal/temporal_algebra.py | 3 +- python/grass/temporal/temporal_granularity.py | 57 +++++++++++-------- .../temporal/temporal_raster_base_algebra.py | 3 +- scripts/g.extension/g.extension.py | 12 ++-- scripts/r.in.wms/wms_drv.py | 22 +++---- scripts/r.tileset/r.tileset.py | 17 ++++-- utils/mkhtml.py | 1 - 26 files changed, 209 insertions(+), 162 deletions(-) diff --git a/gui/wxpython/core/settings.py b/gui/wxpython/core/settings.py index a4f2b60e3fd..f98e9bddd38 100644 --- a/gui/wxpython/core/settings.py +++ b/gui/wxpython/core/settings.py @@ -1221,14 +1221,15 @@ def GetDisplayVectSettings(): else: settings.append("fcolor=none") - settings.append( - "width=%s" % UserSettings.Get(group="vectorLayer", key="line", subkey="width") - ) - settings.append( - "icon=%s" % UserSettings.Get(group="vectorLayer", key="point", subkey="symbol") - ) - settings.append( - "size=%s" % UserSettings.Get(group="vectorLayer", key="point", subkey="size") + settings.extend( + ( + "width=%s" + % UserSettings.Get(group="vectorLayer", key="line", subkey="width"), + "icon=%s" + % UserSettings.Get(group="vectorLayer", key="point", subkey="symbol"), + "size=%s" + % UserSettings.Get(group="vectorLayer", key="point", subkey="size"), + ) ) types = [] for ftype in ["point", "line", "boundary", "centroid", "area", "face"]: diff --git a/gui/wxpython/datacatalog/dialogs.py b/gui/wxpython/datacatalog/dialogs.py index 5e463e83543..aef9aead7bd 100644 --- a/gui/wxpython/datacatalog/dialogs.py +++ b/gui/wxpython/datacatalog/dialogs.py @@ -210,13 +210,17 @@ def _estimateResampling(self): def OnReproject(self, event): cmd = [] if self.etype == "raster": - cmd.append("r.proj") - cmd.append("dbase=" + self.iGisdbase) - cmd.append("project=" + self.iLocation) - cmd.append("mapset=" + self.iMapset) - cmd.append("input=" + self.iLayer) - cmd.append("output=" + self.oLayer) - cmd.append("method=" + self.resampling.GetStringSelection()) + cmd.extend( + ( + "r.proj", + "dbase=" + self.iGisdbase, + "project=" + self.iLocation, + "mapset=" + self.iMapset, + "input=" + self.iLayer, + "output=" + self.oLayer, + "method=" + self.resampling.GetStringSelection(), + ) + ) self.oEnv["GRASS_REGION"] = region_env( n=self.params["n"], @@ -228,13 +232,17 @@ def OnReproject(self, event): env=self.oEnv, ) else: - cmd.append("v.proj") - cmd.append("dbase=" + self.iGisdbase) - cmd.append("project=" + self.iLocation) - cmd.append("mapset=" + self.iMapset) - cmd.append("input=" + self.iLayer) - cmd.append("output=" + self.oLayer) - cmd.append("smax=" + self.vsplit.GetValue()) + cmd.extend( + ( + "v.proj", + "dbase=" + self.iGisdbase, + "project=" + self.iLocation, + "mapset=" + self.iMapset, + "input=" + self.iLayer, + "output=" + self.oLayer, + "smax=" + self.vsplit.GetValue(), + ) + ) self._giface.RunCmd( cmd, diff --git a/gui/wxpython/gcp/manager.py b/gui/wxpython/gcp/manager.py index cee0ff416f3..a938ae49f6c 100644 --- a/gui/wxpython/gcp/manager.py +++ b/gui/wxpython/gcp/manager.py @@ -2909,10 +2909,14 @@ def GetValues(self, columns=None): except ValueError: return valuelist - valuelist.append(self.xcoord.GetValue()) - valuelist.append(self.ycoord.GetValue()) - valuelist.append(self.ecoord.GetValue()) - valuelist.append(self.ncoord.GetValue()) + valuelist.extend( + ( + self.xcoord.GetValue(), + self.ycoord.GetValue(), + self.ecoord.GetValue(), + self.ncoord.GetValue(), + ) + ) return valuelist diff --git a/gui/wxpython/gui_core/query.py b/gui/wxpython/gui_core/query.py index 3a250af4d42..0dfd21d8765 100644 --- a/gui/wxpython/gui_core/query.py +++ b/gui/wxpython/gui_core/query.py @@ -127,9 +127,13 @@ def ShowContextMenu(self, node): col1 = "\n".join([val[1] for val in values if val[1]]) col2 = "\n".join([val[0] for val in values if val[0]]) table = "\n".join([val[0] + ": " + val[1] for val in values]) - texts.append((_("Copy from '%s' column") % self._colNames[1], col1)) - texts.append((_("Copy from '%s' column") % self._colNames[0], col2)) - texts.append((_("Copy selected lines"), table)) + texts.extend( + ( + (_("Copy from '%s' column") % self._colNames[1], col1), + (_("Copy from '%s' column") % self._colNames[0], col2), + (_("Copy selected lines"), table), + ) + ) else: label1 = nodes[0].label texts.append((_("Copy '%s'" % self._cutLabel(label1)), label1)) diff --git a/gui/wxpython/image2target/ii2t_manager.py b/gui/wxpython/image2target/ii2t_manager.py index 2367c053748..6b543ec2356 100644 --- a/gui/wxpython/image2target/ii2t_manager.py +++ b/gui/wxpython/image2target/ii2t_manager.py @@ -2855,12 +2855,16 @@ def GetValues(self, columns=None): except ValueError: return valuelist - valuelist.append(self.xcoord.GetValue()) - valuelist.append(self.ycoord.GetValue()) - valuelist.append(self.zcoord.GetValue()) - valuelist.append(self.ecoord.GetValue()) - valuelist.append(self.ncoord.GetValue()) - valuelist.append(self.hcoord.GetValue()) + valuelist.extend( + ( + self.xcoord.GetValue(), + self.ycoord.GetValue(), + self.zcoord.GetValue(), + self.ecoord.GetValue(), + self.ncoord.GetValue(), + self.hcoord.GetValue(), + ) + ) return valuelist diff --git a/gui/wxpython/iscatt/plots.py b/gui/wxpython/iscatt/plots.py index 9930364c1b3..ba951e0eec3 100644 --- a/gui/wxpython/iscatt/plots.py +++ b/gui/wxpython/iscatt/plots.py @@ -241,8 +241,9 @@ def Plot(self, cats_order, scatts, ellipses, styles): aspect="equal", ) - callafter_list.append([self.axes.draw_artist, [img]]) - callafter_list.append([gs.try_remove, [merged_img.filename]]) + callafter_list.extend( + ([self.axes.draw_artist, [img]], [gs.try_remove, [merged_img.filename]]) + ) for cat_id in cats_order: if cat_id == 0: diff --git a/gui/wxpython/mapwin/buffered.py b/gui/wxpython/mapwin/buffered.py index ad7bcd986a1..d8d7a3fb667 100644 --- a/gui/wxpython/mapwin/buffered.py +++ b/gui/wxpython/mapwin/buffered.py @@ -1072,11 +1072,15 @@ def DrawCompRegionExtent(self): reg = dispReg if utils.isInRegion(dispReg, compReg) else compReg regionCoords = [] - regionCoords.append((reg["w"], reg["n"])) - regionCoords.append((reg["e"], reg["n"])) - regionCoords.append((reg["e"], reg["s"])) - regionCoords.append((reg["w"], reg["s"])) - regionCoords.append((reg["w"], reg["n"])) + regionCoords.extend( + ( + (reg["w"], reg["n"]), + (reg["e"], reg["n"]), + (reg["e"], reg["s"]), + (reg["w"], reg["s"]), + (reg["w"], reg["n"]), + ) + ) # draw region extent self.polypen = wx.Pen( diff --git a/gui/wxpython/modules/extensions.py b/gui/wxpython/modules/extensions.py index 2b83bd8e040..ed6159a5f41 100644 --- a/gui/wxpython/modules/extensions.py +++ b/gui/wxpython/modules/extensions.py @@ -84,8 +84,7 @@ def __init__( task = gtask.parse_interface("g.extension") ignoreFlags = ["l", "c", "g", "a", "f", "t", "help", "quiet"] if sys.platform == "win32": - ignoreFlags.append("d") - ignoreFlags.append("i") + ignoreFlags.extend(("d", "i")) for f in task.get_options()["flags"]: name = f.get("name", "") diff --git a/gui/wxpython/photo2image/ip2i_manager.py b/gui/wxpython/photo2image/ip2i_manager.py index 10439b7b390..35744401366 100644 --- a/gui/wxpython/photo2image/ip2i_manager.py +++ b/gui/wxpython/photo2image/ip2i_manager.py @@ -1977,10 +1977,14 @@ def GetValues(self, columns=None): except ValueError: return valuelist - valuelist.append(self.xcoord.GetValue()) - valuelist.append(self.ycoord.GetValue()) - valuelist.append(self.ecoord.GetValue()) - valuelist.append(self.ncoord.GetValue()) + valuelist.extend( + ( + self.xcoord.GetValue(), + self.ycoord.GetValue(), + self.ecoord.GetValue(), + self.ncoord.GetValue(), + ) + ) return valuelist diff --git a/gui/wxpython/psmap/frame.py b/gui/wxpython/psmap/frame.py index 650d970b10d..4166ea63676 100644 --- a/gui/wxpython/psmap/frame.py +++ b/gui/wxpython/psmap/frame.py @@ -368,8 +368,7 @@ def PSFile(self, filename=None, pdf=False): cmd.append("-e") if self.instruction[self.pageId]["Orientation"] == "Landscape": cmd.append("-r") - cmd.append("input=%s" % instrFile) - cmd.append("output=%s" % filename) + cmd.extend(("input=%s" % instrFile, "output=%s" % filename)) if pdf: self.SetStatusText(_("Generating PDF..."), 0) elif not temp: diff --git a/gui/wxpython/timeline/frame.py b/gui/wxpython/timeline/frame.py index b8b4fa3c2fd..a90fb44ffdb 100644 --- a/gui/wxpython/timeline/frame.py +++ b/gui/wxpython/timeline/frame.py @@ -630,10 +630,14 @@ def InfoFormat(timeData, datasetName, mapIndex): elif etype == "str3ds": text.append(_("Space time 3D raster dataset: %s") % name) - text.append(_("Mapset: %s") % mapset) - text.append(_("Map name: %s") % timeData[datasetName]["names"][mapIndex]) - text.append(_("Start time: %s") % timeData[datasetName]["start_datetime"][mapIndex]) - text.append(_("End time: %s") % timeData[datasetName]["end_datetime"][mapIndex]) + text.extend( + ( + _("Mapset: %s") % mapset, + _("Map name: %s") % timeData[datasetName]["names"][mapIndex], + _("Start time: %s") % timeData[datasetName]["start_datetime"][mapIndex], + _("End time: %s") % timeData[datasetName]["end_datetime"][mapIndex], + ) + ) if not timeData[datasetName]["validTopology"]: text.append(_("WARNING: invalid topology")) diff --git a/gui/wxpython/tplot/frame.py b/gui/wxpython/tplot/frame.py index 5a035657455..48f8008412f 100755 --- a/gui/wxpython/tplot/frame.py +++ b/gui/wxpython/tplot/frame.py @@ -1368,8 +1368,9 @@ def InfoFormat(timeData, values): elif etype == "str3ds": text.append(_("Space time 3D raster dataset: %s") % key) - text.append(_("Value for {date} is {val}".format(date=val[0], val=val[1]))) - text.append("\n") + text.extend( + (_("Value for {date} is {val}".format(date=val[0], val=val[1])), "\n") + ) text.append(_("Press Del to dismiss.")) return "\n".join(text) diff --git a/gui/wxpython/vnet/dialogs.py b/gui/wxpython/vnet/dialogs.py index c82bc25a157..d7b3bb05849 100644 --- a/gui/wxpython/vnet/dialogs.py +++ b/gui/wxpython/vnet/dialogs.py @@ -582,8 +582,12 @@ def OnPageChanged(self, event): """Tab switched""" if event.GetEventObject() == self.notebook: dbMgrIndxs = [] - dbMgrIndxs.append(self.notebook.GetPageIndexByName("inputDbMgr")) - dbMgrIndxs.append(self.notebook.GetPageIndexByName("resultDbMgr")) + dbMgrIndxs.extend( + ( + self.notebook.GetPageIndexByName("inputDbMgr"), + self.notebook.GetPageIndexByName("resultDbMgr"), + ) + ) if self.notebook.GetSelection() in dbMgrIndxs: self.stBar.AddStatusItem( text=_("Loading tables..."), diff --git a/gui/wxpython/vnet/vnet_core.py b/gui/wxpython/vnet/vnet_core.py index 97f40458008..deaf11ff670 100644 --- a/gui/wxpython/vnet/vnet_core.py +++ b/gui/wxpython/vnet/vnet_core.py @@ -485,11 +485,13 @@ def _vnetPathRunAn(self, analysis, output, params, flags, catPts): else: cmdParams.append("input=" + params["input"]) - cmdParams.append("file=" + self.coordsTmpFile) - - cmdParams.append("dmax=" + str(params["max_dist"])) - - cmdParams.append("--overwrite") + cmdParams.extend( + ( + "file=" + self.coordsTmpFile, + "dmax=" + str(params["max_dist"]), + "--overwrite", + ) + ) self._prepareCmd(cmd=cmdParams) if flags["t"]: @@ -697,8 +699,9 @@ def _runAn(self, analysis, output, params, flags, catPts): if not self.tmpInPtsConnected: return False - cmdParams.append("input=" + self.tmpInPtsConnected.GetVectMapName()) - cmdParams.append("--overwrite") + cmdParams.extend( + ("input=" + self.tmpInPtsConnected.GetVectMapName(), "--overwrite") + ) self._setCmdForSpecificAn(cmdParams) diff --git a/gui/wxpython/vnet/vnet_data.py b/gui/wxpython/vnet/vnet_data.py index f7803439e60..0d84fe6f46a 100644 --- a/gui/wxpython/vnet/vnet_data.py +++ b/gui/wxpython/vnet/vnet_data.py @@ -562,8 +562,7 @@ def GetColumns(self, only_relevant=True): cols_data = deepcopy(self.cols) hidden_cols = [] - hidden_cols.append(self.cols["name"].index("e")) - hidden_cols.append(self.cols["name"].index("n")) + hidden_cols.extend((self.cols["name"].index("e"), self.cols["name"].index("n"))) analysis, valid = self.an_params.GetParam("analysis") if only_relevant and len(self.an_data[analysis]["cmdParams"]["cats"]) <= 1: diff --git a/man/parser_standard_options.py b/man/parser_standard_options.py index 554c0846203..dd9c7dd6a35 100644 --- a/man/parser_standard_options.py +++ b/man/parser_standard_options.py @@ -138,22 +138,22 @@ def html(self, endline="\n", indent=" ", toptions="border=1"): """Return a HTML table with the options""" html = ["".format(" " + toptions if toptions else "")] # write headers - html.append(indent + "") - html.append(indent + "") - html.append(indent * 2 + "{0}".format("option")) + html.extend( + ( + indent + "", + indent + "", + indent * 2 + "{0}".format("option"), + ) + ) for col in self.columns: html.append(indent * 2 + "{0}".format(col)) - html.append(indent + "") - html.append(indent + "") - html.append(indent + "") + html.extend((indent + "", indent + "", indent + "")) for optname, options in self.options: - html.append(indent + "") - html.append(indent * 2 + "{0}".format(optname)) + html.extend((indent + "", indent * 2 + "{0}".format(optname))) for col in self.columns: html.append(indent * 2 + "{0}".format(options.get(col, ""))) html.append(indent + "") - html.append(indent + "") - html.append("") + html.extend((indent + "", "")) return endline.join(html) def _repr_html_(self): diff --git a/pyproject.toml b/pyproject.toml index f7cc89a7257..7bce17922b2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -159,7 +159,6 @@ ignore = [ "FURB103", # write-whole-file. "FURB105", # print-empty-string "FURB110", # if-exp-instead-of-or-operator - "FURB113", # repeated-append "FURB116", # f-string-number-format "FURB116", # f-string-number-format "FURB118", # reimplemented-operator diff --git a/python/grass/gunittest/runner.py b/python/grass/gunittest/runner.py index 891e1b65a5a..f1758e2e08b 100644 --- a/python/grass/gunittest/runner.py +++ b/python/grass/gunittest/runner.py @@ -269,28 +269,28 @@ def stopTestRun(self): # write test details and just write status=failed if not run: run = errored + failed + succeeded - infos.append("total=%d" % (run)) - - infos.append("failures=%d" % failed) - infos.append("errors=%d" % errored) - infos.append("successes=%d" % succeeded) - infos.append("skipped=%d" % skipped) - - # TODO: document this: if not supported by view, - # expected_failures should be counted as failures and vice versa - # or both add to skipped as unclear? - infos.append("expected_failures=%d" % expectedFails) - infos.append("unexpected_successes=%d" % unexpectedSuccesses) - - # TODO: include each module just once? list good and bad modules? - infos.append("tested_modules=%s" % ",".join(self._grass_modules)) - infos.append("supplementary_files=%s" % ",".join(self._supplementary_files)) - - # module, modules?, c, c++?, python - # TODO: include also type modules? - # TODO: include also C++ code? - # TODO: distinguish C and Python modules? - infos.append("test_type=%s" % (self.test_type)) + infos.extend( + ( + "total=%d" % (run), + "failures=%d" % failed, + "errors=%d" % errored, + "successes=%d" % succeeded, + "skipped=%d" % skipped, + # TODO: document this: if not supported by view, + # expected_failures should be counted as failures and vice versa + # or both add to skipped as unclear? + "expected_failures=%d" % expectedFails, + "unexpected_successes=%d" % unexpectedSuccesses, + # TODO: include each module just once? list good and bad modules? + "tested_modules=%s" % ",".join(self._grass_modules), + "supplementary_files=%s" % ",".join(self._supplementary_files), + # module, modules?, c, c++?, python + # TODO: include also type modules? + # TODO: include also C++ code? + # TODO: distinguish C and Python modules? + "test_type=%s" % (self.test_type), + ) + ) self._stream.write("\n".join(infos)) self._stream.write("\n") diff --git a/python/grass/pygrass/vector/testsuite/test_geometry.py b/python/grass/pygrass/vector/testsuite/test_geometry.py index d4ad7431278..c3ad5b6cd63 100644 --- a/python/grass/pygrass/vector/testsuite/test_geometry.py +++ b/python/grass/pygrass/vector/testsuite/test_geometry.py @@ -336,17 +336,13 @@ def test_boundaries_1(self): self.assertEqual(len(boundaries), 4) string_list = [] - string_list.append( - "LINESTRING (0.0000000000000000 0.0000000000000000, 0.0000000000000000 4.0000000000000000)" - ) - string_list.append( - "LINESTRING (0.0000000000000000 4.0000000000000000, 4.0000000000000000 4.0000000000000000)" - ) - string_list.append( - "LINESTRING (4.0000000000000000 4.0000000000000000, 4.0000000000000000 0.0000000000000000)" - ) - string_list.append( - "LINESTRING (4.0000000000000000 0.0000000000000000, 0.0000000000000000 0.0000000000000000)" + string_list.extend( + ( + "LINESTRING (0.0000000000000000 0.0000000000000000, 0.0000000000000000 4.0000000000000000)", + "LINESTRING (0.0000000000000000 4.0000000000000000, 4.0000000000000000 4.0000000000000000)", + "LINESTRING (4.0000000000000000 4.0000000000000000, 4.0000000000000000 0.0000000000000000)", + "LINESTRING (4.0000000000000000 0.0000000000000000, 0.0000000000000000 0.0000000000000000)", + ) ) for boundary, i in zip(boundaries, range(4)): diff --git a/python/grass/temporal/temporal_algebra.py b/python/grass/temporal/temporal_algebra.py index 6bf96eea68e..3c209f97207 100644 --- a/python/grass/temporal/temporal_algebra.py +++ b/python/grass/temporal/temporal_algebra.py @@ -1728,8 +1728,7 @@ def compare_bool_value( is True ): if count == 0: - condition_value_list.append(compop[0]) - condition_value_list.append("(") + condition_value_list.extend((compop[0], "(")) for boolean in relationmap.condition_value: if isinstance(boolean, bool): if count > 0: diff --git a/python/grass/temporal/temporal_granularity.py b/python/grass/temporal/temporal_granularity.py index c23dd701c5c..94aa3b124aa 100644 --- a/python/grass/temporal/temporal_granularity.py +++ b/python/grass/temporal/temporal_granularity.py @@ -1000,14 +1000,19 @@ def compute_common_absolute_time_granularity_simple(gran_list): seconds.append(days[0] * 60 * 60 * 24) if has_months: months.sort() - seconds.append(months[0] * 60 * 60 * 24 * 28) - seconds.append(months[0] * 60 * 60 * 24 * 29) - seconds.append(months[0] * 60 * 60 * 24 * 30) - seconds.append(months[0] * 60 * 60 * 24 * 31) + seconds.extend( + ( + months[0] * 60 * 60 * 24 * 28, + months[0] * 60 * 60 * 24 * 29, + months[0] * 60 * 60 * 24 * 30, + months[0] * 60 * 60 * 24 * 31, + ) + ) if has_years: years.sort() - seconds.append(years[0] * 60 * 60 * 24 * 365) - seconds.append(years[0] * 60 * 60 * 24 * 366) + seconds.extend( + (years[0] * 60 * 60 * 24 * 365, years[0] * 60 * 60 * 24 * 366) + ) num = gcd_list(seconds) gran = "second" @@ -1024,14 +1029,17 @@ def compute_common_absolute_time_granularity_simple(gran_list): minutes.append(days[0] * 60 * 24) if has_months: months.sort() - minutes.append(months[0] * 60 * 24 * 28) - minutes.append(months[0] * 60 * 24 * 29) - minutes.append(months[0] * 60 * 24 * 30) - minutes.append(months[0] * 60 * 24 * 31) + minutes.extend( + ( + months[0] * 60 * 24 * 28, + months[0] * 60 * 24 * 29, + months[0] * 60 * 24 * 30, + months[0] * 60 * 24 * 31, + ) + ) if has_years: years.sort() - minutes.append(years[0] * 60 * 24 * 365) - minutes.append(years[0] * 60 * 24 * 366) + minutes.extend((years[0] * 60 * 24 * 365, years[0] * 60 * 24 * 366)) num = gcd_list(minutes) gran = "minute" if num > 1: @@ -1044,14 +1052,17 @@ def compute_common_absolute_time_granularity_simple(gran_list): hours.append(days[0] * 24) if has_months: months.sort() - hours.append(months[0] * 24 * 28) - hours.append(months[0] * 24 * 29) - hours.append(months[0] * 24 * 30) - hours.append(months[0] * 24 * 31) + hours.extend( + ( + months[0] * 24 * 28, + months[0] * 24 * 29, + months[0] * 24 * 30, + months[0] * 24 * 31, + ) + ) if has_years: years.sort() - hours.append(years[0] * 24 * 365) - hours.append(years[0] * 24 * 366) + hours.extend((years[0] * 24 * 365, years[0] * 24 * 366)) num = gcd_list(hours) gran = "hour" if num > 1: @@ -1061,14 +1072,12 @@ def compute_common_absolute_time_granularity_simple(gran_list): if has_days: if has_months: months.sort() - days.append(months[0] * 28) - days.append(months[0] * 29) - days.append(months[0] * 30) - days.append(months[0] * 31) + days.extend( + (months[0] * 28, months[0] * 29, months[0] * 30, months[0] * 31) + ) if has_years: years.sort() - days.append(years[0] * 365) - days.append(years[0] * 366) + days.extend((years[0] * 365, years[0] * 366)) num = gcd_list(days) gran = "day" if num > 1: diff --git a/python/grass/temporal/temporal_raster_base_algebra.py b/python/grass/temporal/temporal_raster_base_algebra.py index 4207e251b38..f9333d79044 100644 --- a/python/grass/temporal/temporal_raster_base_algebra.py +++ b/python/grass/temporal/temporal_raster_base_algebra.py @@ -464,8 +464,7 @@ def compare_cmd_value( if topo.upper() in temporal_relations.keys(): relationmaplist = temporal_relations[topo.upper()] if count == 0 and "cmd_list" in dir(map_i): - cmd_value_list.append(compop) - cmd_value_list.append("(") + cmd_value_list.extend((compop, "(")) for relationmap in relationmaplist: if ( self._check_spatial_topology_relation( diff --git a/scripts/g.extension/g.extension.py b/scripts/g.extension/g.extension.py index 0e7d9296b57..9ca0202a9fe 100644 --- a/scripts/g.extension/g.extension.py +++ b/scripts/g.extension/g.extension.py @@ -722,11 +722,13 @@ def get_installed_modules(force=False): for tnode in tree.findall("task"): if flags["g"]: desc, keyw = get_optional_params(tnode) - ret.append("name={0}".format(tnode.get("name").strip())) - ret.append("description={0}".format(desc)) - ret.append("keywords={0}".format(keyw)) - ret.append( - "executables={0}".format(",".join(get_module_executables(tnode))) + ret.extend( + ( + "name={0}".format(tnode.get("name").strip()), + "description={0}".format(desc), + "keywords={0}".format(keyw), + "executables={0}".format(",".join(get_module_executables(tnode))), + ) ) else: ret.append(tnode.get("name").strip()) diff --git a/scripts/r.in.wms/wms_drv.py b/scripts/r.in.wms/wms_drv.py index e88b2fe1886..2f92ec45380 100644 --- a/scripts/r.in.wms/wms_drv.py +++ b/scripts/r.in.wms/wms_drv.py @@ -708,17 +708,17 @@ def _findTileMats(self, tile_mats, region, bbox): """!Find best tile matrix set for requested resolution.""" scale_dens = [] - scale_dens.append( - (bbox["maxy"] - bbox["miny"]) - / region["rows"] - * self._getMetersPerUnit() - / self.pixel_size - ) - scale_dens.append( - (bbox["maxx"] - bbox["minx"]) - / region["cols"] - * self._getMetersPerUnit() - / self.pixel_size + scale_dens.extend( + ( + (bbox["maxy"] - bbox["miny"]) + / region["rows"] + * self._getMetersPerUnit() + / self.pixel_size, + (bbox["maxx"] - bbox["minx"]) + / region["cols"] + * self._getMetersPerUnit() + / self.pixel_size, + ) ) scale_den = min(scale_dens) diff --git a/scripts/r.tileset/r.tileset.py b/scripts/r.tileset/r.tileset.py index ac5cd5c07a0..f2049385a94 100644 --- a/scripts/r.tileset/r.tileset.py +++ b/scripts/r.tileset/r.tileset.py @@ -120,10 +120,14 @@ def bboxToPoints(bbox): """Make points that are the corners of a bounding box""" points = [] - points.append((bbox["w"], bbox["s"])) - points.append((bbox["w"], bbox["n"])) - points.append((bbox["e"], bbox["n"])) - points.append((bbox["e"], bbox["s"])) + points.extend( + ( + (bbox["w"], bbox["s"]), + (bbox["w"], bbox["n"]), + (bbox["e"], bbox["n"]), + (bbox["e"], bbox["s"]), + ) + ) return points @@ -344,8 +348,9 @@ def main(): # points later. bigger = [] - bigger.append(max(source_bbox_dest_lengths["x"])) - bigger.append(max(source_bbox_dest_lengths["y"])) + bigger.extend( + (max(source_bbox_dest_lengths["x"]), max(source_bbox_dest_lengths["y"])) + ) maxdim = (max_cols, max_rows) # Compute the number and size of tiles to use in each direction diff --git a/utils/mkhtml.py b/utils/mkhtml.py index 737f4698ddd..bc2c8d9b2de 100644 --- a/utils/mkhtml.py +++ b/utils/mkhtml.py @@ -423,7 +423,6 @@ def get_last_git_commit(src_dir, addon_path, is_addon): if os.getenv("HTML_PAGE_FOOTER_PAGES_PATH") else "" ) - pgm = sys.argv[1] src_file = "%s.html" % pgm From c9a1a17c5266f9c01da8be4daf4500aeafbb7ba7 Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Wed, 10 Jul 2024 20:11:20 -0400 Subject: [PATCH 005/471] i.segment: Fix uninitialized variable issue (#4023) --- imagery/i.segment/region_growing.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/imagery/i.segment/region_growing.c b/imagery/i.segment/region_growing.c index f2c416b77de..dc5b5bd2157 100644 --- a/imagery/i.segment/region_growing.c +++ b/imagery/i.segment/region_growing.c @@ -696,7 +696,7 @@ static int find_best_neighbor(struct ngbr_stats *Ri, struct reg_stats *Ri_rs, int clear_cand, struct globals *globals) { int n, n_ngbrs, no_check, cmp; - struct rc ngbr_rc, next, *pngbr_rc; + struct rc ngbr_rc = {0}, next = {0}, *pngbr_rc = NULL; struct rclist rilist; double tempsim; int neighbors[8][2]; @@ -893,7 +893,7 @@ double calculate_shape(struct reg_stats *rsi, struct reg_stats *rsk, double smooth, compact; int pl, pbbox, count; double bboxdiag; - int pl1, pl2, count1, count2; + int pl1 = 0, pl2 = 0, count1 = 0, count2 = 0; int e1, n1, s1, w1, e2, n2, s2, w2, ns_extent, ew_extent; pl = pl1 + pl2 - nshared; @@ -989,7 +989,7 @@ static int search_neighbors(struct ngbr_stats *Ri, struct reg_stats *Ri_rs, int update_band_vals(int row, int col, struct reg_stats *rs, struct globals *globals) { - struct rc next, ngbr_rc; + struct rc next = {0}, ngbr_rc = {0}; int neighbors[8][2]; int rid, count, n; @@ -1498,7 +1498,7 @@ static int calculate_reg_stats(int row, int col, struct reg_stats *rs, ret = 1; else if (globals->min_reg_size == 3) { int n, rid; - struct rc ngbr_rc; + struct rc ngbr_rc = {0}; int neighbors[8][2]; globals->find_neighbors(row, col, neighbors); @@ -1541,7 +1541,7 @@ static int calculate_reg_stats(int row, int col, struct reg_stats *rs, /* rs->id must be set */ struct pavl_table *rc_check_tree; /* cells already checked */ int n, rid; - struct rc ngbr_rc, *pngbr_rc, next; + struct rc ngbr_rc = {0}, *pngbr_rc = NULL, next = {0}; struct rclist rilist; int neighbors[8][2]; int no_check; From a3543cd83886835268fc43b39ac1c9748b493faf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Wed, 10 Jul 2024 22:51:15 -0400 Subject: [PATCH 006/471] style: Fix if-exp-instead-of-or-operator (FURB110) (#4013) --- gui/wxpython/core/treemodel.py | 4 ++-- gui/wxpython/datacatalog/tree.py | 2 +- gui/wxpython/gui_core/dialogs.py | 2 +- gui/wxpython/history/tree.py | 2 +- gui/wxpython/rdigit/g.gui.rdigit.py | 2 +- gui/wxpython/startup/guiutils.py | 2 +- man/parser_standard_options.py | 2 +- pyproject.toml | 1 - python/grass/pygrass/modules/grid/grid.py | 2 +- python/grass/pygrass/modules/grid/split.py | 6 +++--- python/grass/pygrass/modules/shortcuts.py | 2 +- python/grass/pygrass/raster/__init__.py | 8 ++++---- python/grass/pygrass/raster/abstract.py | 2 +- python/grass/pygrass/shell/conversion.py | 6 +++--- python/grass/pygrass/utils.py | 2 +- python/grass/pygrass/vector/__init__.py | 8 ++++---- python/grass/pygrass/vector/abstract.py | 8 ++++---- python/grass/pygrass/vector/basic.py | 10 +++------- python/grass/pygrass/vector/find.py | 8 ++++---- python/grass/pygrass/vector/geometry.py | 20 +++++++++---------- python/grass/pygrass/vector/table.py | 14 ++++++------- .../tests/grass_script_core_location_test.py | 2 +- scripts/db.in.ogr/db.in.ogr.py | 2 +- scripts/g.extension/g.extension.py | 2 +- utils/mkhtml.py | 9 +++------ 25 files changed, 60 insertions(+), 68 deletions(-) diff --git a/gui/wxpython/core/treemodel.py b/gui/wxpython/core/treemodel.py index 4c5de36f701..c891dafe25c 100644 --- a/gui/wxpython/core/treemodel.py +++ b/gui/wxpython/core/treemodel.py @@ -95,7 +95,7 @@ def AppendNode(self, parent, **kwargs): def SearchNodes(self, parent=None, **kwargs): """Search nodes according to specified attributes.""" nodes = [] - parent = parent if parent else self.root + parent = parent or self.root self._searchNodes(node=parent, foundNodes=nodes, **kwargs) return nodes @@ -291,7 +291,7 @@ class ModuleNode(DictNode): def __init__(self, label=None, data=None): super().__init__(data=data) - self._label = label if label else "" + self._label = label or "" if not data: self.data = {} diff --git a/gui/wxpython/datacatalog/tree.py b/gui/wxpython/datacatalog/tree.py index 542282802be..0d8b0563c47 100644 --- a/gui/wxpython/datacatalog/tree.py +++ b/gui/wxpython/datacatalog/tree.py @@ -181,7 +181,7 @@ def __init__(self, data=None): def label(self): data = self.data if data["type"] == "mapset": - owner = data["owner"] if data["owner"] else _("name unknown") + owner = data["owner"] or _("name unknown") if data["current"]: return _("{name} (current)").format(**data) elif data["is_different_owner"] and data["lock"]: diff --git a/gui/wxpython/gui_core/dialogs.py b/gui/wxpython/gui_core/dialogs.py index 6c0c75f5507..167a48ce6bb 100644 --- a/gui/wxpython/gui_core/dialogs.py +++ b/gui/wxpython/gui_core/dialogs.py @@ -2334,7 +2334,7 @@ def __init__( label = StaticText(self, label=message) sizer.Add(label, proportion=0, flag=wx.ALIGN_CENTRE | wx.ALL, border=10) - hyperlinkLabel = hyperlinkLabel if hyperlinkLabel else hyperlink + hyperlinkLabel = hyperlinkLabel or hyperlink hyperlinkCtrl = HyperlinkCtrl( self, id=wx.ID_ANY, diff --git a/gui/wxpython/history/tree.py b/gui/wxpython/history/tree.py index 2ab158c45c5..0e9d75870aa 100644 --- a/gui/wxpython/history/tree.py +++ b/gui/wxpython/history/tree.py @@ -279,7 +279,7 @@ def _initHistoryModel(self): data={ "type": COMMAND, "name": entry["command"].strip(), - "timestamp": timestamp if timestamp else None, + "timestamp": timestamp or None, "status": status, }, ) diff --git a/gui/wxpython/rdigit/g.gui.rdigit.py b/gui/wxpython/rdigit/g.gui.rdigit.py index 0a9a72e4acb..ac68525cd6e 100755 --- a/gui/wxpython/rdigit/g.gui.rdigit.py +++ b/gui/wxpython/rdigit/g.gui.rdigit.py @@ -116,7 +116,7 @@ def __init__( self._mapObj = self.GetMap() # load raster map - self._addLayer(name=new_map if new_map else edit_map) + self._addLayer(name=new_map or edit_map) # switch toolbar self.AddToolbar("rdigit", fixed=True) diff --git a/gui/wxpython/startup/guiutils.py b/gui/wxpython/startup/guiutils.py index baa74fd6b68..8bf4debef66 100644 --- a/gui/wxpython/startup/guiutils.py +++ b/gui/wxpython/startup/guiutils.py @@ -586,7 +586,7 @@ def can_switch_mapset_interactive(guiparent, grassdb, location, mapset): if is_mapset_locked(mapset_path): info = get_mapset_lock_info(mapset_path) - user = info["owner"] if info["owner"] else _("unknown") + user = info["owner"] or _("unknown") lockpath = info["lockpath"] timestamp = info["timestamp"] diff --git a/man/parser_standard_options.py b/man/parser_standard_options.py index dd9c7dd6a35..87ffc6c4979 100644 --- a/man/parser_standard_options.py +++ b/man/parser_standard_options.py @@ -218,7 +218,7 @@ def _repr_html_(self): ) args = parser.parse_args() - cfile = args.text if args.text else urlopen(args.url, proxies=None) + cfile = args.text or urlopen(args.url, proxies=None) options = OptTable(parse_options(cfile.readlines(), startswith=args.startswith)) outform = args.format diff --git a/pyproject.toml b/pyproject.toml index 7bce17922b2..268996612b7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -158,7 +158,6 @@ ignore = [ "FURB101", # read-whole-file "FURB103", # write-whole-file. "FURB105", # print-empty-string - "FURB110", # if-exp-instead-of-or-operator "FURB116", # f-string-number-format "FURB116", # f-string-number-format "FURB118", # reimplemented-operator diff --git a/python/grass/pygrass/modules/grid/grid.py b/python/grass/pygrass/modules/grid/grid.py index 911ecf21be1..1163a8f3b6d 100644 --- a/python/grass/pygrass/modules/grid/grid.py +++ b/python/grass/pygrass/modules/grid/grid.py @@ -469,7 +469,7 @@ def __init__( self.height = height self.overlap = overlap self.processes = processes - self.region = region if region else Region() + self.region = region or Region() self.start_row = start_row self.start_col = start_col self.out_prefix = out_prefix diff --git a/python/grass/pygrass/modules/grid/split.py b/python/grass/pygrass/modules/grid/split.py index ea20a216e8e..afd747090b0 100644 --- a/python/grass/pygrass/modules/grid/split.py +++ b/python/grass/pygrass/modules/grid/split.py @@ -93,7 +93,7 @@ def split_region_in_overlapping_tiles(region=None, width=100, height=100, overla [[Bbox(1350.0, 640.0, 1010.0, 0.0), Bbox(1350.0, 640.0, 1500.0, 990.0)], [Bbox(660.0, 0.0, 1010.0, 0.0), Bbox(660.0, 0.0, 1500.0, 990.0)]] """ - reg = region if region else Region() + reg = region or Region() ncols = (reg.cols + width - 1) // width nrows = (reg.rows + height - 1) // height box_list = [] @@ -118,7 +118,7 @@ def split_region_tiles(region=None, width=100, height=100): :param height: the width of tiles :type height: int """ - reg = region if region else Region() + reg = region or Region() ncols = (reg.cols + width - 1) // width nrows = (reg.rows + height - 1) // height box_list = [] @@ -142,7 +142,7 @@ def get_overlap_region_tiles(region=None, width=100, height=100, overlap=0): :param overlap: the value of overlap between tiles :type overlap: int """ - reg = region if region else Region() + reg = region or Region() ncols = (reg.cols + width - 1) // width nrows = (reg.rows + height - 1) // height box_list = [] diff --git a/python/grass/pygrass/modules/shortcuts.py b/python/grass/pygrass/modules/shortcuts.py index bde3142e54c..f7fbdf7f04c 100644 --- a/python/grass/pygrass/modules/shortcuts.py +++ b/python/grass/pygrass/modules/shortcuts.py @@ -59,7 +59,7 @@ class MetaModule: def __init__(self, prefix, cls=None): self.prefix = prefix - self.cls = cls if cls else Module + self.cls = cls or Module def __dir__(self): return [ diff --git a/python/grass/pygrass/raster/__init__.py b/python/grass/pygrass/raster/__init__.py index c10140483d5..2fe95602fdd 100644 --- a/python/grass/pygrass/raster/__init__.py +++ b/python/grass/pygrass/raster/__init__.py @@ -187,8 +187,8 @@ def open(self, mode=None, mtype=None, overwrite=None): * self._rows and self._cols """ - self.mode = mode if mode else self.mode - self.mtype = mtype if mtype else self.mtype + self.mode = mode or self.mode + self.mtype = mtype or self.mtype self.overwrite = overwrite if overwrite is not None else self.overwrite if self.mode == "r": @@ -509,8 +509,8 @@ def open(self, mode=None, mtype=None, overwrite=None): self._rows = libraster.Rast_window_rows() self._cols = libraster.Rast_window_cols() - self.mode = mode if mode else self.mode - self.mtype = mtype if mtype else self.mtype + self.mode = mode or self.mode + self.mtype = mtype or self.mtype self.overwrite = overwrite if overwrite is not None else self.overwrite if self.exist(): diff --git a/python/grass/pygrass/raster/abstract.py b/python/grass/pygrass/raster/abstract.py index fa29a662500..616964d8f7b 100644 --- a/python/grass/pygrass/raster/abstract.py +++ b/python/grass/pygrass/raster/abstract.py @@ -431,7 +431,7 @@ def exist(self): if self.name: if self.mapset == "": mapset = utils.get_mapset_raster(self.name, self.mapset) - self.mapset = mapset if mapset else "" + self.mapset = mapset or "" return bool(mapset) return bool(utils.get_mapset_raster(self.name, self.mapset)) else: diff --git a/python/grass/pygrass/shell/conversion.py b/python/grass/pygrass/shell/conversion.py index 76802c08ce1..b30acc3b545 100644 --- a/python/grass/pygrass/shell/conversion.py +++ b/python/grass/pygrass/shell/conversion.py @@ -83,12 +83,12 @@ def dict2html( def fun(x): return x - keys = keys if keys else sorted(dic.keys()) + keys = keys or sorted(dic.keys()) header = "" % border if border else "
" kd = "<%s>%s" % (kdec, kfmt, kdec) if kdec else kfmt vd = "<%s>%s" % (vdec, vfmt, vdec) if vdec else vfmt - kfun = kfun if kfun else fun - vfun = vfun if vfun else fun + kfun = kfun or fun + vfun = vfun or fun content = [dcont.format(key=kd % kfun(k), value=vd % vfun(dic[k])) for k in keys] return "\n".join( [ diff --git a/python/grass/pygrass/utils.py b/python/grass/pygrass/utils.py index 02719c3730f..9727ed26791 100644 --- a/python/grass/pygrass/utils.py +++ b/python/grass/pygrass/utils.py @@ -348,7 +348,7 @@ def r_export(rast, output="", fmt="png", **kargs): from grass.pygrass.modules import Module if rast.exist(): - output = output if output else "%s_%s.%s" % (rast.name, rast.mapset, fmt) + output = output or "%s_%s.%s" % (rast.name, rast.mapset, fmt) Module( "r.out.%s" % fmt, input=rast.fullname(), diff --git a/python/grass/pygrass/vector/__init__.py b/python/grass/pygrass/vector/__init__.py index c298391cc90..7869b1a29b5 100644 --- a/python/grass/pygrass/vector/__init__.py +++ b/python/grass/pygrass/vector/__init__.py @@ -310,9 +310,9 @@ def __getitem__(self, key): return [ self.read(indx) for indx in range( - key.start if key.start else 1, - key.stop if key.stop else len(self), - key.step if key.step else 1, + key.start or 1, + key.stop or len(self), + key.step or 1, ) ] elif isinstance(key, int): @@ -509,7 +509,7 @@ def cat(self, cat_id, vtype, layer=None, generator=False, geo=None): ilist = Ilist() libvect.Vect_cidx_find_all( self.c_mapinfo, - layer if layer else self.layer, + layer or self.layer, Obj.gtype, cat_id, ilist.c_ilist, diff --git a/python/grass/pygrass/vector/abstract.py b/python/grass/pygrass/vector/abstract.py index 9f8d5dc90b2..de4f74b9460 100644 --- a/python/grass/pygrass/vector/abstract.py +++ b/python/grass/pygrass/vector/abstract.py @@ -299,7 +299,7 @@ def exist(self): if self.name: if self.mapset == "": mapset = utils.get_mapset_vector(self.name, self.mapset) - self.mapset = mapset if mapset else "" + self.mapset = mapset or "" return bool(mapset) return bool(utils.get_mapset_vector(self.name, self.mapset)) else: @@ -358,7 +358,7 @@ def open( See more examples in the documentation of the ``read`` and ``write`` methods """ - self.mode = mode if mode else self.mode + self.mode = mode or self.mode with_z = libvect.WITH_Z if with_z else libvect.WITHOUT_Z # check if map exists or not if not self.exist() and self.mode != "w": @@ -396,8 +396,8 @@ def open( # create a link link = Link( layer, - link_name if link_name else self.name, - tab_name if tab_name else self.name, + link_name or self.name, + tab_name or self.name, link_key, link_db, link_driver, diff --git a/python/grass/pygrass/vector/basic.py b/python/grass/pygrass/vector/basic.py index 2b8c013b433..73eb5bb5557 100644 --- a/python/grass/pygrass/vector/basic.py +++ b/python/grass/pygrass/vector/basic.py @@ -133,9 +133,7 @@ def contains(self, point): """ return bool( - libvect.Vect_point_in_box( - point.x, point.y, point.z if point.z else 0, self.c_bbox - ) + libvect.Vect_point_in_box(point.x, point.y, point.z or 0, self.c_bbox) ) def items(self): @@ -424,7 +422,7 @@ def n_cats(self): return self.c_cats.contents.n_cats def __init__(self, c_cats=None): - self.c_cats = c_cats if c_cats else ctypes.pointer(libvect.line_cats()) + self.c_cats = c_cats or ctypes.pointer(libvect.line_cats()) def reset(self): """Reset the C cats struct from previous values.""" @@ -541,9 +539,7 @@ def max(self): return [max_values[i] for i in range(self.n_ranges)] def __init__(self, c_cat_list=None): - self.c_cat_list = ( - c_cat_list if c_cat_list else ctypes.pointer(libvect.cat_list()) - ) + self.c_cat_list = c_cat_list or ctypes.pointer(libvect.cat_list()) def from_string(self, string): """Converts string of categories and cat ranges separated by commas diff --git a/python/grass/pygrass/vector/find.py b/python/grass/pygrass/vector/find.py index 50599af5ab5..9333497f110 100644 --- a/python/grass/pygrass/vector/find.py +++ b/python/grass/pygrass/vector/find.py @@ -101,7 +101,7 @@ def node(self, point, maxdist): self.c_mapinfo, point.x, point.y, - point.z if point.z else 0, + point.z or 0, float(maxdist), int(not point.is2D), ) @@ -166,7 +166,7 @@ def geo(self, point, maxdist, type="all", exclude=0): self.c_mapinfo, point.x, point.y, - point.z if point.z else 0, + point.z or 0, self.vtype[type], float(maxdist), int(not point.is2D), @@ -257,7 +257,7 @@ def geos(self, point, maxdist, type="all", exclude=None): self.c_mapinfo, point.x, point.y, - point.z if point.z else 0, + point.z or 0, self.vtype[type], float(maxdist), int(not point.is2D), @@ -586,7 +586,7 @@ def areas(self, bbox, boxlist=None, bboxlist_only=False): >>> test_vect.close() """ - boxlist = boxlist if boxlist else BoxList() + boxlist = boxlist or BoxList() if libvect.Vect_select_areas_by_box( self.c_mapinfo, bbox.c_bbox, boxlist.c_boxlist ): diff --git a/python/grass/pygrass/vector/geometry.py b/python/grass/pygrass/vector/geometry.py index 8812ad77782..5b39d547bec 100644 --- a/python/grass/pygrass/vector/geometry.py +++ b/python/grass/pygrass/vector/geometry.py @@ -792,7 +792,7 @@ def bbox(self, bbox=None): .. """ - bbox = bbox if bbox else Bbox() + bbox = bbox or Bbox() libvect.Vect_line_box(self.c_points, bbox.c_bbox) return bbox @@ -1376,7 +1376,7 @@ def __repr__(self): def _centroid(self, side, idonly=False): if side > 0: v_id = libvect.Vect_get_area_centroid(self.c_mapinfo, side) - v_id = v_id if v_id else None + v_id = v_id or None if idonly: return v_id else: @@ -1497,7 +1497,7 @@ def boundaries(self): @mapinfo_must_be_set def bbox(self, bbox=None): """Return bounding box of Isle""" - bbox = bbox if bbox else Bbox() + bbox = bbox or Bbox() libvect.Vect_get_isle_box(self.c_mapinfo, self.id, bbox.c_bbox) return bbox @@ -1700,7 +1700,7 @@ def bbox(self, bbox=None): :param bbox: a Bbox object to fill with info from bounding box of area :type bbox: a Bbox object """ - bbox = bbox if bbox else Bbox() + bbox = bbox or Bbox() libvect.Vect_get_area_box(self.c_mapinfo, self.id, bbox.c_bbox) return bbox @@ -1798,7 +1798,7 @@ def cats(self, cats=None): :param cats: a Cats object to fill with info with area categories :type cats: a Cats object """ - cats = cats if cats else Cats() + cats = cats or Cats() libvect.Vect_get_area_cats(self.c_mapinfo, self.id, cats.c_cats) return cats @@ -1820,7 +1820,7 @@ def contains_point(self, point, bbox=None): :param bbox: the bounding box where run the analysis :type bbox: a Bbox object """ - bbox = bbox if bbox else self.bbox() + bbox = bbox or self.bbox() return bool( libvect.Vect_point_in_area( point.x, point.y, self.c_mapinfo, self.id, bbox.c_bbox @@ -1897,8 +1897,8 @@ def read_next_line( if c_cats is None: free_cats = True - c_points = c_points if c_points else ctypes.pointer(libvect.line_pnts()) - c_cats = c_cats if c_cats else ctypes.pointer(libvect.line_cats()) + c_points = c_points or ctypes.pointer(libvect.line_pnts()) + c_cats = c_cats or ctypes.pointer(libvect.line_cats()) ftype, v_id, c_points, c_cats = c_read_next_line(c_mapinfo, c_points, c_cats) return GV_TYPE[ftype]["obj"]( v_id=v_id, @@ -1945,8 +1945,8 @@ def read_line( if c_cats is None: free_cats = True - c_points = c_points if c_points else ctypes.pointer(libvect.line_pnts()) - c_cats = c_cats if c_cats else ctypes.pointer(libvect.line_cats()) + c_points = c_points or ctypes.pointer(libvect.line_pnts()) + c_cats = c_cats or ctypes.pointer(libvect.line_cats()) feature_id, ftype, c_points, c_cats = c_read_line( feature_id, c_mapinfo, c_points, c_cats ) diff --git a/python/grass/pygrass/vector/table.py b/python/grass/pygrass/vector/table.py index 3cca5689132..dec3ffbba08 100644 --- a/python/grass/pygrass/vector/table.py +++ b/python/grass/pygrass/vector/table.py @@ -1135,7 +1135,7 @@ def drop(self, cursor=None, force=False): :type force: bool """ - cur = cursor if cursor else self.conn.cursor() + cur = cursor or self.conn.cursor() if self.exist(cursor=cur): used = db_table_in_vector(self.name) if used is not None and len(used) > 0 and not force: @@ -1194,8 +1194,8 @@ def execute(self, sql_code=None, cursor=None, many=False, values=None): """ try: - sqlc = sql_code if sql_code else self.filters.get_sql() - cur = cursor if cursor else self.conn.cursor() + sqlc = sql_code or self.filters.get_sql() + cur = cursor or self.conn.cursor() if many and values: return cur.executemany(sqlc, values) return cur.execute(sqlc, values) if values else cur.execute(sqlc) @@ -1212,7 +1212,7 @@ def exist(self, cursor=None): :param cursor: the cursor to connect, if None it use the cursor of connection table object """ - cur = cursor if cursor else self.conn.cursor() + cur = cursor or self.conn.cursor() return table_exist(cur, self.name) def insert(self, values, cursor=None, many=False): @@ -1227,7 +1227,7 @@ def insert(self, values, cursor=None, many=False): :param many: True to run executemany function :type many: bool """ - cur = cursor if cursor else self.conn.cursor() + cur = cursor or self.conn.cursor() if many: return cur.executemany(self.columns.insert_str, values) return cur.execute(self.columns.insert_str, values) @@ -1246,7 +1246,7 @@ def update(self, key, values, cursor=None): of connection table object :type cursor: Cursor object """ - cur = cursor if cursor else self.conn.cursor() + cur = cursor or self.conn.cursor() vals = list(values) + [ key, ] @@ -1266,7 +1266,7 @@ def create(self, cols, name=None, overwrite=False, cursor=None): :type cursor: Cursor object """ - cur = cursor if cursor else self.conn.cursor() + cur = cursor or self.conn.cursor() coldef = ",\n".join(["%s %s" % col for col in cols]) if name: newname = name diff --git a/python/grass/script/tests/grass_script_core_location_test.py b/python/grass/script/tests/grass_script_core_location_test.py index fdffa340b83..b389d41ba8d 100644 --- a/python/grass/script/tests/grass_script_core_location_test.py +++ b/python/grass/script/tests/grass_script_core_location_test.py @@ -167,7 +167,7 @@ def set_and_test_description(tmp_path, text): gs.core._set_location_description(tmp_path, name, text) description_file = tmp_path / name / "PERMANENT" / "MYNAME" assert description_file.exists() - text = text if text else "" # None and empty should both yield empty. + text = text or "" # None and empty should both yield empty. assert description_file.read_text(encoding="utf-8").strip() == text diff --git a/scripts/db.in.ogr/db.in.ogr.py b/scripts/db.in.ogr/db.in.ogr.py index 0137a41d62c..a95919f4936 100755 --- a/scripts/db.in.ogr/db.in.ogr.py +++ b/scripts/db.in.ogr/db.in.ogr.py @@ -119,7 +119,7 @@ def main(): gs.fatal(_("Table <%s> already exists") % output) # treat DB as real vector map... - layer = db_table if db_table else None + layer = db_table or None vopts = {} if options["encoding"]: diff --git a/scripts/g.extension/g.extension.py b/scripts/g.extension/g.extension.py index 9ca0202a9fe..50e8a97d2bb 100644 --- a/scripts/g.extension/g.extension.py +++ b/scripts/g.extension/g.extension.py @@ -2595,7 +2595,7 @@ def resolve_known_host_service(url, name, branch): if "branch" in match["url_end"]: suffix = match["url_end"].format( name=name, - branch=branch if branch else get_default_branch(url), + branch=branch or get_default_branch(url), ) else: suffix = match["url_end"].format(name=name) diff --git a/utils/mkhtml.py b/utils/mkhtml.py index bc2c8d9b2de..69a1ba1db76 100644 --- a/utils/mkhtml.py +++ b/utils/mkhtml.py @@ -418,11 +418,8 @@ def get_last_git_commit(src_dir, addon_path, is_addon): return get_git_commit_from_file(src_dir=src_dir) -html_page_footer_pages_path = ( - os.getenv("HTML_PAGE_FOOTER_PAGES_PATH") - if os.getenv("HTML_PAGE_FOOTER_PAGES_PATH") - else "" -) +html_page_footer_pages_path = os.getenv("HTML_PAGE_FOOTER_PAGES_PATH") or "" + pgm = sys.argv[1] src_file = "%s.html" % pgm @@ -927,7 +924,7 @@ def to_title(name): git_commit = get_last_git_commit( src_dir=curdir, - addon_path=addon_path if addon_path else None, + addon_path=addon_path or None, is_addon=bool(addon_path), ) if git_commit["commit"] == "unknown": From 4aa61d74177244441471ee29809aa9a2f03a3f62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Wed, 10 Jul 2024 23:02:52 -0400 Subject: [PATCH 007/471] style: Fix unnecessary-map (C417) (#4012) * style: Fix unnecessary-map (C417) * Apply suggestions from code review * style: Change outer quotes to avoid escaping inner quotes for Q003 --- gui/wxpython/core/render.py | 2 +- gui/wxpython/core/utils.py | 4 ++-- gui/wxpython/gmodeler/dialogs.py | 10 +++------- gui/wxpython/gmodeler/model.py | 18 +++++++----------- gui/wxpython/gui_core/forms.py | 2 +- gui/wxpython/gui_core/gselect.py | 8 ++++---- gui/wxpython/lmgr/frame.py | 4 ++-- gui/wxpython/main_window/frame.py | 4 ++-- gui/wxpython/modules/colorrules.py | 2 +- gui/wxpython/timeline/frame.py | 4 +--- gui/wxpython/tplot/frame.py | 4 ++-- pyproject.toml | 1 - python/grass/script/core.py | 6 ++---- python/grass/semantic_label/reader.py | 2 +- 14 files changed, 29 insertions(+), 42 deletions(-) diff --git a/gui/wxpython/core/render.py b/gui/wxpython/core/render.py index 40cd82defc2..77f4fcbdb94 100644 --- a/gui/wxpython/core/render.py +++ b/gui/wxpython/core/render.py @@ -882,7 +882,7 @@ def _projInfo(self): for line in ret.splitlines(): if ":" in line: - key, val = map(lambda x: x.strip(), line.split(":", 1)) + key, val = (x.strip() for x in line.split(":", 1)) if key in {"units"}: val = val.lower() projinfo[key] = val diff --git a/gui/wxpython/core/utils.py b/gui/wxpython/core/utils.py index fd6803ecf74..3eedc619772 100644 --- a/gui/wxpython/core/utils.py +++ b/gui/wxpython/core/utils.py @@ -659,7 +659,7 @@ def _parseFormats(output, writableOnly=False): patt = re.compile(r"\(rw\+?\)$", re.IGNORECASE) for line in output.splitlines(): - key, name = map(lambda x: x.strip(), line.strip().split(":", 1)) + key, name = (x.strip() for x in line.strip().split(":", 1)) if writableOnly and not patt.search(key): continue @@ -843,7 +843,7 @@ def StoreEnvVariable(key, value=None, envFile=None): for line in fd.readlines(): line = line.rstrip(os.linesep) try: - k, v = map(lambda x: x.strip(), line.split(" ", 1)[1].split("=", 1)) + k, v = (x.strip() for x in line.split(" ", 1)[1].split("=", 1)) except Exception as e: sys.stderr.write( _("%s: line skipped - unable to parse '%s'\nReason: %s\n") diff --git a/gui/wxpython/gmodeler/dialogs.py b/gui/wxpython/gmodeler/dialogs.py index 044dedff680..10729b83289 100644 --- a/gui/wxpython/gmodeler/dialogs.py +++ b/gui/wxpython/gmodeler/dialogs.py @@ -944,15 +944,11 @@ def Populate(self, data): items = self.frame.GetModel().GetItems(objType=ModelAction) if isinstance(self.shape, ModelCondition): if self.GetLabel() == "ElseBlockList": - shapeItems = map( - lambda x: x.GetId(), self.shape.GetItems(items)["else"] - ) + shapeItems = (x.GetId() for x in self.shape.GetItems(items)["else"]) else: - shapeItems = map( - lambda x: x.GetId(), self.shape.GetItems(items)["if"] - ) + shapeItems = (x.GetId() for x in self.shape.GetItems(items)["if"]) else: - shapeItems = map(lambda x: x.GetId(), self.shape.GetItems(items)) + shapeItems = (x.GetId() for x in self.shape.GetItems(items)) else: shapeItems = [] diff --git a/gui/wxpython/gmodeler/model.py b/gui/wxpython/gmodeler/model.py index d729c43a87a..864f1df68e9 100644 --- a/gui/wxpython/gmodeler/model.py +++ b/gui/wxpython/gmodeler/model.py @@ -493,7 +493,7 @@ def Validate(self): cmd = action.GetLog(string=False) task = GUI(show=None).ParseCommand(cmd=cmd) - errList += map(lambda x: cmd[0] + ": " + x, task.get_cmd_error()) + errList += (cmd[0] + ": " + x for x in task.get_cmd_error()) # check also variables for opt in cmd[1:]: @@ -695,7 +695,7 @@ def Run(self, log, onDone, parent=None): parent=parent, message=_("Variables below not defined:") + "\n\n" - + "\n".join(map(lambda x: "%s: %s (%s)" % (x[0], x[1], x[2]), err)), + + "\n".join(f"{x[0]}: {x[1]} ({x[2]})" for x in err), ) return @@ -735,9 +735,7 @@ def Run(self, log, onDone, parent=None): # split condition # TODO: this part needs some better solution - condVar, condText = map( - lambda x: x.strip(), re.split(r"\s* in \s*", cond) - ) + condVar, condText = (x.strip() for x in re.split(r"\s* in \s*", cond)) pattern = re.compile("%" + condVar) # for vars()[condVar] in eval(condText): ? vlist = [] @@ -2668,9 +2666,7 @@ def _writeItem(self, item, ignoreBlock=True, variables={}): value = '"' + value + '"' cond = pattern.sub(value, cond) if isinstance(item, ModelLoop): - condVar, condText = map( - lambda x: x.strip(), re.split(r"\s* in \s*", cond) - ) + condVar, condText = (x.strip() for x in re.split(r"\s* in \s*", cond)) cond = "%sfor %s in " % (" " * self.indent, condVar) if condText[0] == "`" and condText[-1] == "`": task = GUI(show=None).ParseCommand(cmd=utils.split(condText[1:-1])) @@ -3499,21 +3495,21 @@ def cleanup(): r""" %s("g.remove", flags="f", type="raster", name=%s) """ - % (run_command, ",".join(map(lambda x: '"' + x + '"', rast))) + % (run_command, ",".join(f'"{x}"' for x in rast3d)) ) if vect: self.fd.write( r""" %s("g.remove", flags="f", type="vector", name=%s) """ - % (run_command, ",".join(map(lambda x: '"' + x + '"', vect))) + % (run_command, ",".join(('"' + x + '"' for x in vect))) ) if rast3d: self.fd.write( r""" %s("g.remove", flags="f", type="raster_3d", name=%s) """ - % (run_command, ",".join(map(lambda x: '"' + x + '"', rast3d))) + % (run_command, ",".join(f'"{x}"' for x in rast3d)) ) if not rast and not vect and not rast3d: self.fd.write(" pass\n") diff --git a/gui/wxpython/gui_core/forms.py b/gui/wxpython/gui_core/forms.py index 95cbf55d7e9..00fd505c799 100644 --- a/gui/wxpython/gui_core/forms.py +++ b/gui/wxpython/gui_core/forms.py @@ -2490,7 +2490,7 @@ def OnCheckItem(index=None, flag=None, event=None): tab[section].SetupScrolling(True, True, 10, 10) tab[section].Layout() minsecsizes = tabsizer[section].GetSize() - maxsizes = list(map(lambda x: max(maxsizes[x], minsecsizes[x]), (0, 1))) + maxsizes = [max(maxsizes[x], minsecsizes[x]) for x in (0, 1)] # TODO: be less arbitrary with these 600 self.panelMinHeight = 100 diff --git a/gui/wxpython/gui_core/gselect.py b/gui/wxpython/gui_core/gselect.py index bed849f0c26..6b679c074b5 100644 --- a/gui/wxpython/gui_core/gselect.py +++ b/gui/wxpython/gui_core/gselect.py @@ -2291,8 +2291,8 @@ def hasRastSameProjAsLocation(dsn, table=None): lines = ret.splitlines() projectionMatch = "0" if lines: - bandNumber, bandType, projectionMatch = map( - lambda x: x.strip(), lines[0].split(",") + bandNumber, bandType, projectionMatch = ( + x.strip() for x in lines[0].split(",") ) return projectionMatch @@ -2321,8 +2321,8 @@ def getProjMatchCaption(projectionMatch): layerId = 1 for line in ret.splitlines(): - layerName, featureType, projectionMatch, geometryColumn = map( - lambda x: x.strip(), line.split(",") + layerName, featureType, projectionMatch, geometryColumn = ( + x.strip() for x in line.split(",") ) projectionMatchCaption = getProjMatchCaption(projectionMatch) grassName = GetValidLayerName(layerName) diff --git a/gui/wxpython/lmgr/frame.py b/gui/wxpython/lmgr/frame.py index 752f50bf597..0b5a07b8f07 100644 --- a/gui/wxpython/lmgr/frame.py +++ b/gui/wxpython/lmgr/frame.py @@ -1981,8 +1981,8 @@ def AddOrUpdateMap(self, mapName, ltype): self.AddMaps([mapName], ltype, check=True) else: display = self.GetMapDisplay() - mapLayers = map( - lambda x: x.GetName(), display.GetMap().GetListOfLayers(ltype=ltype) + mapLayers = ( + x.GetName() for x in display.GetMap().GetListOfLayers(ltype=ltype) ) if mapName in mapLayers: display.GetWindow().UpdateMap(render=True) diff --git a/gui/wxpython/main_window/frame.py b/gui/wxpython/main_window/frame.py index 361111d389f..41c6c57365f 100644 --- a/gui/wxpython/main_window/frame.py +++ b/gui/wxpython/main_window/frame.py @@ -2129,8 +2129,8 @@ def AddOrUpdateMap(self, mapName, ltype): self.AddMaps([mapName], ltype, check=True) else: display = self.GetMapDisplay() - mapLayers = map( - lambda x: x.GetName(), display.GetMap().GetListOfLayers(ltype=ltype) + mapLayers = ( + x.GetName() for x in display.GetMap().GetListOfLayers(ltype=ltype) ) if mapName in mapLayers: display.GetWindow().UpdateMap(render=True) diff --git a/gui/wxpython/modules/colorrules.py b/gui/wxpython/modules/colorrules.py index 6feeb22cab2..73cd2dac9b9 100644 --- a/gui/wxpython/modules/colorrules.py +++ b/gui/wxpython/modules/colorrules.py @@ -713,7 +713,7 @@ def ReadColorTable(self, ctable): minim = maxim = count = 0 for line in ctable.splitlines(): try: - value, color = map(lambda x: x.strip(), line.split(" ")) + value, color = (x.strip() for x in line.split(" ")) except ValueError: GMessage(parent=self, message=_("Invalid color table format")) self.rulesPanel.Clear() diff --git a/gui/wxpython/timeline/frame.py b/gui/wxpython/timeline/frame.py index a90fb44ffdb..0330adbde3d 100644 --- a/gui/wxpython/timeline/frame.py +++ b/gui/wxpython/timeline/frame.py @@ -572,9 +572,7 @@ def SetDatasets(self, datasets): GError(parent=self, message=str(error), showTraceback=False) return self.datasets = datasets - self.datasetSelect.SetValue( - ",".join(map(lambda x: x[0] + "@" + x[1], datasets)) - ) + self.datasetSelect.SetValue(",".join(f"{x[0]}@{x[1]}" for x in datasets)) self._redraw() def Show3D(self, show): diff --git a/gui/wxpython/tplot/frame.py b/gui/wxpython/tplot/frame.py index 48f8008412f..4a1cd9f1ca6 100755 --- a/gui/wxpython/tplot/frame.py +++ b/gui/wxpython/tplot/frame.py @@ -1268,7 +1268,7 @@ def SetDatasets( except: self.coorval.SetValue(",".join(coors)) if self.datasetsV: - vdatas = ",".join(map(lambda x: x[0] + "@" + x[1], self.datasetsV)) + vdatas = ",".join(f"{x[0]}@{x[1]}" for x in self.datasetsV) self.datasetSelectV.SetValue(vdatas) if attr: self.attribute.SetValue(attr) @@ -1276,7 +1276,7 @@ def SetDatasets( self.cats.SetValue(cats) if self.datasetsR: self.datasetSelectR.SetValue( - ",".join(map(lambda x: x[0] + "@" + x[1], self.datasetsR)) + ",".join(f"{x[0]}@{x[1]}" for x in self.datasetsR) ) if title: self.title.SetValue(title) diff --git a/pyproject.toml b/pyproject.toml index 268996612b7..25832ce3ea5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -108,7 +108,6 @@ ignore = [ "C405", # unnecessary-literal-set "C414", # unnecessary-double-cast-or-process "C416", # unnecessary-comprehension - "C417", # unnecessary-map "C419", # unnecessary-comprehension-in-call "COM812", # missing-trailing-comma "COM818", # trailing-comma-on-bare-tuple diff --git a/python/grass/script/core.py b/python/grass/script/core.py index 6647661b379..a790307aa3c 100644 --- a/python/grass/script/core.py +++ b/python/grass/script/core.py @@ -218,9 +218,7 @@ def get_real_command(cmd): if os.path.splitext(cmd)[1] == ".py": cmd = cmd[:-3] # PATHEXT is necessary to check on Windows (force lowercase) - pathext = list( - map(lambda x: x.lower(), os.environ["PATHEXT"].split(os.pathsep)) - ) + pathext = [x.lower() for x in os.environ["PATHEXT"].split(os.pathsep)] if ".py" not in pathext: # we assume that PATHEXT contains always '.py' os.environ["PATHEXT"] = ".py;" + os.environ["PATHEXT"] @@ -1287,7 +1285,7 @@ def region_env(region3d=False, flags=None, env=None, **kwargs): with open(windfile, "r") as fd: grass_region = "" for line in fd.readlines(): - key, value = map(lambda x: x.strip(), line.split(":", 1)) + key, value = (x.strip() for x in line.split(":", 1)) if kwargs and key not in {"proj", "zone"}: continue if ( diff --git a/python/grass/semantic_label/reader.py b/python/grass/semantic_label/reader.py index bd19af69110..1da67aff7f9 100644 --- a/python/grass/semantic_label/reader.py +++ b/python/grass/semantic_label/reader.py @@ -181,7 +181,7 @@ def find_file(self, semantic_label): shortcut and config[root]["shortcut"].upper() == shortcut.upper() and band.upper() - in map(lambda x: x.upper(), config[root]["bands"].keys()) + in (x.upper() for x in config[root]["bands"].keys()) ): return filename From 92cf465412d31ff57728dc8eb3c58fafbd419816 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Wed, 10 Jul 2024 23:03:21 -0400 Subject: [PATCH 008/471] style: Fix if-expr-min-max (FURB136) (#4026) --- pyproject.toml | 1 - python/grass/pygrass/modules/grid/split.py | 8 ++++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 25832ce3ea5..19c011d7bb8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -162,7 +162,6 @@ ignore = [ "FURB118", # reimplemented-operator "FURB129", # readlines-in-for "FURB131", # delete-full-slice - "FURB136", # if-expr-min-max "FURB140", # reimplemented-starmap "FURB142", # for-loop-set-mutations "FURB148", # unnecessary-enumerate diff --git a/python/grass/pygrass/modules/grid/split.py b/python/grass/pygrass/modules/grid/split.py index afd747090b0..cac604327d8 100644 --- a/python/grass/pygrass/modules/grid/split.py +++ b/python/grass/pygrass/modules/grid/split.py @@ -29,10 +29,10 @@ def get_bbox(reg, row, col, width, height, overlap): east = reg.west + ((col + 1) * width + overlap) * reg.ewres west = reg.west + (col * width - overlap) * reg.ewres return Bbox( - north=north if north <= reg.north else reg.north, - south=south if south >= reg.south else reg.south, - east=east if east <= reg.east else reg.east, - west=west if west >= reg.west else reg.west, + north=min(north, reg.north), + south=max(south, reg.south), + east=min(east, reg.east), + west=max(west, reg.west), ) From 6b38ffbc1004d735db0797abd170f0eaf4c4cd43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Wed, 10 Jul 2024 23:03:36 -0400 Subject: [PATCH 009/471] style: Fix too-many-newlines-at-end-of-file (W391) (#4025) --- pyproject.toml | 1 - raster/r.stats.zonal/graphics_for_description.ipynb | 7 ------- 2 files changed, 8 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 19c011d7bb8..324afdd3762 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -310,7 +310,6 @@ ignore = [ "UP032", # f-string "UP034", # extraneous-parentheses "UP036", # outdated-version-block - "W391", # too-many-newlines-at-end-of-file "W605", # invalid-escape-sequence "YTT204", # sys-version-info-minor-cmp-int ] diff --git a/raster/r.stats.zonal/graphics_for_description.ipynb b/raster/r.stats.zonal/graphics_for_description.ipynb index 38b0f4c2a1a..b61d77d3c82 100644 --- a/raster/r.stats.zonal/graphics_for_description.ipynb +++ b/raster/r.stats.zonal/graphics_for_description.ipynb @@ -77,13 +77,6 @@ "!optipng -o7 {filename}\n", "Image(filename)" ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { From 169ca9b66ef41f0e5db1cecfe351882b00dded5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Wed, 10 Jul 2024 23:04:04 -0400 Subject: [PATCH 010/471] style: Fix empty-comment (PLR2044) (#4024) --- gui/wxpython/psmap/instructions.py | 1 - pyproject.toml | 1 - python/grass/script/task.py | 1 - raster/r.solute.transport/example.py | 1 - 4 files changed, 4 deletions(-) diff --git a/gui/wxpython/psmap/instructions.py b/gui/wxpython/psmap/instructions.py index a123baaaba4..bca686898cb 100644 --- a/gui/wxpython/psmap/instructions.py +++ b/gui/wxpython/psmap/instructions.py @@ -401,7 +401,6 @@ def Read(self, filename): else: page["Orientation"] = orientation - # return True def SendToRead(self, instruction, text, **kwargs): diff --git a/pyproject.toml b/pyproject.toml index 324afdd3762..8404c018ab3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -201,7 +201,6 @@ ignore = [ "PLR1714", # repeated-equality-comparison "PLR1733", # unnecessary-dict-index-lookup "PLR2004", # magic-value-comparison - "PLR2044", # empty-comment "PLR5501", # collapsible-else-if "PLR6104", # non-augmented-assignment "PLR6201", # literal-membership diff --git a/python/grass/script/task.py b/python/grass/script/task.py index 66f7667f5e4..3dfe8049841 100644 --- a/python/grass/script/task.py +++ b/python/grass/script/task.py @@ -448,7 +448,6 @@ def convert_xml_to_utf8(xml_text): m = re.match(pattern, xml_text) if m is None: return xml_text.encode("utf-8") if xml_text else None - # enc = m.groups()[0] # modify: change the encoding to "utf-8", for correct parsing diff --git a/raster/r.solute.transport/example.py b/raster/r.solute.transport/example.py index aef8dfe0621..0371f681223 100755 --- a/raster/r.solute.transport/example.py +++ b/raster/r.solute.transport/example.py @@ -24,7 +24,6 @@ gs.run_command("r.mapcalc", expression="poros=0.17") gs.run_command("r.mapcalc", expression="syield=0.0001") gs.run_command("r.mapcalc", expression="null=0.0") -# gs.message(_("Compute a steady state groundwater flow")) gs.run_command( From ff453d215a3244644ef207f2d5d065f680016da0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Wed, 10 Jul 2024 23:20:01 -0400 Subject: [PATCH 011/471] style: Fix get-attr-with-constant (B009) (#4009) --- gui/wxpython/modules/colorrules.py | 4 ++-- pyproject.toml | 1 - scripts/wxpyimgview/wxpyimgview_gui.py | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/gui/wxpython/modules/colorrules.py b/gui/wxpython/modules/colorrules.py index 73cd2dac9b9..5ded54c8be8 100644 --- a/gui/wxpython/modules/colorrules.py +++ b/gui/wxpython/modules/colorrules.py @@ -1901,12 +1901,12 @@ def _columnWidgetEvtHandler(self, bind=True): ] for widget in widgets: if bind is True: - getattr(widget["widget"], "Bind")( + widget["widget"].Bind( widget["event"], widget["handler"], ) else: - getattr(widget["widget"], "Unbind")(widget["event"]) + widget["widget"].Unbind(widget["event"]) class ThematicVectorTable(VectorColorTable): diff --git a/pyproject.toml b/pyproject.toml index 8404c018ab3..02479d6b5a7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -92,7 +92,6 @@ ignore = [ "B006", # mutable-argument-default "B007", # unused-loop-control-variable "B008", # function-call-in-default-argument - "B009", # get-attr-with-constant "B015", # useless-comparison "B023", # function-uses-loop-variable "B026", # star-arg-unpacking-after-keyword-arg diff --git a/scripts/wxpyimgview/wxpyimgview_gui.py b/scripts/wxpyimgview/wxpyimgview_gui.py index 1ce955c47e6..b0f89deee80 100644 --- a/scripts/wxpyimgview/wxpyimgview_gui.py +++ b/scripts/wxpyimgview/wxpyimgview_gui.py @@ -83,7 +83,7 @@ def draw(self): dc = wx.PaintDC(self) data = app.imgbuf.reshape((app.i_height, app.i_width, 4)) data = data[::, ::, 2::-1] - fn = getattr(data, "tobytes", getattr(data, "tostring")) + fn = getattr(data, "tobytes", data.tostring) image = wx.Image(app.i_width, app.i_height, fn()) dc.DrawBitmap(BitmapFromImage(image), x0, y0, False) From ff2da11b41e6943ff3d83f4801df203228b81ecd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Thu, 11 Jul 2024 02:01:16 -0400 Subject: [PATCH 012/471] style: Fix unnecessary-comprehension-in-call (C419) (#4027) --- gui/wxpython/datacatalog/tree.py | 6 ++---- lib/init/grass.py | 2 +- pyproject.toml | 1 - python/grass/temporal/temporal_algebra.py | 8 ++++---- scripts/d.polar/d.polar.py | 2 +- 5 files changed, 8 insertions(+), 11 deletions(-) diff --git a/gui/wxpython/datacatalog/tree.py b/gui/wxpython/datacatalog/tree.py index 0d8b0563c47..085b9cb7f67 100644 --- a/gui/wxpython/datacatalog/tree.py +++ b/gui/wxpython/datacatalog/tree.py @@ -2092,10 +2092,8 @@ def _popupMenuLayer(self): if not isinstance(self._giface, StandaloneGrassInterface): if all( - [ - each.data["name"] == genv["LOCATION_NAME"] - for each in self.selected_location - ] + each.data["name"] == genv["LOCATION_NAME"] + for each in self.selected_location ): if len(self.selected_layer) > 1: item = wx.MenuItem(menu, wx.ID_ANY, _("&Display layers")) diff --git a/lib/init/grass.py b/lib/init/grass.py index 0a3dfe26434..c75dc14075b 100755 --- a/lib/init/grass.py +++ b/lib/init/grass.py @@ -1947,7 +1947,7 @@ def print_params(params): # check if we are dealing with parameters which require dev files dev_params = ["arch", "compiler", "build", "date"] - if any([param in dev_params for param in params]): + if any(param in dev_params for param in params): plat = gpath("include", "Make", "Platform.make") if not os.path.exists(plat): fatal(_("Please install the GRASS GIS development package")) diff --git a/pyproject.toml b/pyproject.toml index 02479d6b5a7..45093d83b8d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -107,7 +107,6 @@ ignore = [ "C405", # unnecessary-literal-set "C414", # unnecessary-double-cast-or-process "C416", # unnecessary-comprehension - "C419", # unnecessary-comprehension-in-call "COM812", # missing-trailing-comma "COM818", # trailing-comma-on-bare-tuple "D1", diff --git a/python/grass/temporal/temporal_algebra.py b/python/grass/temporal/temporal_algebra.py index 3c209f97207..66a24a3a154 100644 --- a/python/grass/temporal/temporal_algebra.py +++ b/python/grass/temporal/temporal_algebra.py @@ -2249,7 +2249,7 @@ def build_condition_list(self, tvarexpr, thenlist, topolist=["EQUAL"]): # Use method eval_global_var to evaluate expression. resultlist = self.eval_global_var(tvarexpr, thenlist) # Check if a given list is a list of maps. - elif all([issubclass(type(ele), AbstractMapDataset) for ele in tvarexpr]): + elif all(issubclass(type(ele), AbstractMapDataset) for ele in tvarexpr): # Use method eval_map_list to evaluate map_list in comparison to thenlist. resultlist = self.eval_map_list(tvarexpr, thenlist, topolist) elif len(tvarexpr) % 2 != 0: @@ -2259,12 +2259,12 @@ def build_condition_list(self, tvarexpr, thenlist, topolist=["EQUAL"]): expr = tvarexpr[iter] operator = tvarexpr[iter + 1] relexpr = tvarexpr[iter + 2] - if all([issubclass(type(ele), list) for ele in [expr, relexpr]]): + if all(issubclass(type(ele), list) for ele in [expr, relexpr]): resultlist = self.build_spatio_temporal_topology_list(expr, relexpr) # Loop through the list, search for map lists or global variables. for expr in tvarexpr: if isinstance(expr, list): - if all([issubclass(type(ele), AbstractMapDataset) for ele in expr]): + if all(issubclass(type(ele), AbstractMapDataset) for ele in expr): # Use method eval_map_list to evaluate map_list resultlist = self.eval_map_list(expr, thenlist, topolist) else: @@ -2274,7 +2274,7 @@ def build_condition_list(self, tvarexpr, thenlist, topolist=["EQUAL"]): elif isinstance(expr, GlobalTemporalVar): # Use according functions for different global variable types. if expr.get_type() == "operator": - if all(["condition_value" in dir(map_i) for map_i in thenlist]): + if all("condition_value" in dir(map_i) for map_i in thenlist): # Add operator string to the condition list. [ map_i.condition_value.extend(expr.get_type_value()) diff --git a/scripts/d.polar/d.polar.py b/scripts/d.polar/d.polar.py index ed26e0a1d42..4ef578d5676 100755 --- a/scripts/d.polar/d.polar.py +++ b/scripts/d.polar/d.polar.py @@ -510,7 +510,7 @@ def main(): occurrences = sorted([(math.radians(x), freq[x]) for x in freq]) # find the maximum value - maxradius = max([f for a, f in occurrences]) + maxradius = max(f for a, f in occurrences) # now do cos() sin() sine_cosine = [(math.cos(a) * f, math.sin(a) * f) for a, f in occurrences] From 52a7fd70f045427e6b790328d711a5c6a8b323de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Thu, 11 Jul 2024 07:15:59 -0400 Subject: [PATCH 013/471] style: Fix f-string-number-format (FURB116) (#4030) --- pyproject.toml | 2 -- python/grass/imaging/images2swf.py | 6 +++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 45093d83b8d..6d8dba3fdc4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -155,8 +155,6 @@ ignore = [ "FURB101", # read-whole-file "FURB103", # write-whole-file. "FURB105", # print-empty-string - "FURB116", # f-string-number-format - "FURB116", # f-string-number-format "FURB118", # reimplemented-operator "FURB129", # readlines-in-for "FURB131", # delete-full-slice diff --git a/python/grass/imaging/images2swf.py b/python/grass/imaging/images2swf.py index df6eb023c24..e98d567d83c 100644 --- a/python/grass/imaging/images2swf.py +++ b/python/grass/imaging/images2swf.py @@ -253,7 +253,7 @@ def bitsToInt(bb, n=8): # Get value in bits for i in range(len(bb)): b = bb[i : i + 1] - tmp = bin(ord(b))[2:] + tmp = f"{ord(b):b}" # value += tmp.rjust(8,'0') value = tmp.rjust(8, "0") + value @@ -271,7 +271,7 @@ def getTypeAndLen(bb): # Get first 16 bits for i in range(2): b = bb[i : i + 1] - tmp = bin(ord(b))[2:] + tmp = f"{ord(b):b}" # value += tmp.rjust(8,'0') value = tmp.rjust(8, "0") + value @@ -285,7 +285,7 @@ def getTypeAndLen(bb): value = "" for i in range(2, 6): b = bb[i : i + 1] # becomes a single-byte bytes() on both PY3 and PY2 - tmp = bin(ord(b))[2:] + tmp = f"{ord(b):b}" # value += tmp.rjust(8,'0') value = tmp.rjust(8, "0") + value L = int(value, 2) From 204903cdabb516c9f13c5d0a3199b224af085488 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Thu, 11 Jul 2024 07:16:20 -0400 Subject: [PATCH 014/471] style: Fix is-literal (F632) (#4031) --- imagery/i.atcorr/create_iwave.py | 6 +++--- pyproject.toml | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/imagery/i.atcorr/create_iwave.py b/imagery/i.atcorr/create_iwave.py index 5fc1fa78559..b895d3d5125 100644 --- a/imagery/i.atcorr/create_iwave.py +++ b/imagery/i.atcorr/create_iwave.py @@ -175,13 +175,13 @@ def pretty_print(filter_f): """ pstring = "" for i in range(len(filter_f) + 1): - if i % 8 is 0: - if i is not 0: + if i % 8 == 0: + if i != 0: value_wo_leading_zero = ("%.4f" % (filter_f[i - 1])).lstrip("0") pstring += value_wo_leading_zero if i > 1 and i < len(filter_f): pstring += ", " - if i is not 1: + if i != 1: # trim the trailing whitespace at the end of line pstring = pstring.rstrip() pstring += "\n " diff --git a/pyproject.toml b/pyproject.toml index 6d8dba3fdc4..50935dc2a01 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -144,7 +144,6 @@ ignore = [ "F403", # undefined-local-with-import-star "F405", # undefined-local-with-import-star-usage "F601", # multi-value-repeated-key-literal - "F632", # is-literal "F811", # redefined-while-unused "F821", # undefined-name "F822", # undefined-export From 3ef2715bc756520820772be081595a6c1656336e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Thu, 11 Jul 2024 08:55:28 -0400 Subject: [PATCH 015/471] style: Fix print-empty-string (FURB105) (#4029) --- pyproject.toml | 1 - utils/generate_release_notes.py | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 50935dc2a01..74c62b14f0e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -153,7 +153,6 @@ ignore = [ "FBT003", # boolean-positional-value-in-call "FURB101", # read-whole-file "FURB103", # write-whole-file. - "FURB105", # print-empty-string "FURB118", # reimplemented-operator "FURB129", # readlines-in-for "FURB131", # delete-full-slice diff --git a/utils/generate_release_notes.py b/utils/generate_release_notes.py index 2ccac59ae8f..40511c3dc44 100755 --- a/utils/generate_release_notes.py +++ b/utils/generate_release_notes.py @@ -113,7 +113,7 @@ def print_category(category, changes, file=None): for item in itertools.chain(overflow, hidden): print(f" * {item}", file=file) print("\n") - print("") + print() def print_by_category(changes, categories, file=None): @@ -141,7 +141,7 @@ def print_support(file=None): for member in data: supporters.append(f"""[{member['name']}]({member['profile']})""") print(", ".join(supporters)) - print("") + print() def adjust_after(lines): @@ -198,7 +198,7 @@ def print_notes( print_by_category(changes_by_category, categories=categories, file=file) if after: print(after) - print("") + print() print(binder_badge(end_tag)) From ee9fa48798c84ba329d3a698b0353a1f61c18c36 Mon Sep 17 00:00:00 2001 From: Ivan Mincik Date: Thu, 11 Jul 2024 15:21:55 +0000 Subject: [PATCH 016/471] doc: add Nix documentation (#3990) Co-authored-by: Markus Neteler --- doc/NIX.md | 103 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 doc/NIX.md diff --git a/doc/NIX.md b/doc/NIX.md new file mode 100644 index 00000000000..412e75d5a1a --- /dev/null +++ b/doc/NIX.md @@ -0,0 +1,103 @@ +# How to use Nix + +## What is Nix + +[Nix](https://nixos.org/) is a powerful package manager and system configuration +tool that aims to make software deployment fully reproducible. + +## Nix installation + +- Install Nix + [(learn more about this installer)](https://zero-to-nix.com/start/install) + + ```bash + curl --proto '=https' --tlsv1.2 -sSf \ + -L https://install.determinate.systems/nix \ + | sh -s -- install + ``` + +## Create GRASS GIS development environment + +Nix provides a development environment containing all required dependencies. + +- Launch development environment + + ```bash + nix develop + ``` + +- Optionally, use [direnv](https://direnv.net) to activate environment + automatically when entering the source code directory + + ```bash + echo "use flake" > .envrc + direnv allow + ``` + +## Launch GRASS GIS directly from the source code + +Nix allows to run a program directly from git source code repository using +following command: + +```bash +nix run \ + github://# -- +``` + +- Launch latest version of GRASS from `main` branch + + ```bash + nix run github:OSGeo/grass#grass + ``` + +- Launch GRASS from specific Git revision, branch or tag + + ```bash + nix run github:OSGeo/grass/#grass + ``` + +- Launch GRASS from pull request + + ```bash + nix run github:/grass/#grass + ``` + +## Install GRASS GIS directly from the source code + +To install a program permanently, use following command: + +```bash +nix profile install \ + github://# -- +``` + +- Install latest version of GRASS from `main` branch + + ```bash + nix profile install github:OSGeo/grass#grass + ``` + +- Install GRASS from specific Git revision, branch or tag + + ```bash + nix profile install github:OSGeo/grass/#grass + ``` + +## Uninstall GRASS GIS + +- List installed programs + + ```bash + nix profile list + ``` + +- Uninstall a program + + ```bash + nix profile remove + ``` + +## Nix documentation + +- [nix.dev](https://nix.dev) +- [Zero to Nix](https://zero-to-nix.com) From b3cb5db01dc049092f095ffed86fa9ce05db769b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 12 Jul 2024 07:11:36 -0400 Subject: [PATCH 017/471] CI(deps): Update github/codeql-action action to v3.25.12 (#4038) --- .github/workflows/codeql-analysis.yml | 4 ++-- .github/workflows/python-code-quality.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 0cff48db3f2..5317047188a 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -56,7 +56,7 @@ jobs: if: ${{ matrix.language == 'c-cpp' }} - name: Initialize CodeQL - uses: github/codeql-action/init@b611370bb5703a7efb587f9d136a52ea24c5c38c # v3.25.11 + uses: github/codeql-action/init@4fa2a7953630fd2f3fb380f21be14ede0169dd4f # v3.25.12 with: languages: ${{ matrix.language }} config-file: ./.github/codeql/codeql-config.yml @@ -81,6 +81,6 @@ jobs: run: .github/workflows/build_ubuntu-22.04.sh "${HOME}/install" - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@b611370bb5703a7efb587f9d136a52ea24c5c38c # v3.25.11 + uses: github/codeql-action/analyze@4fa2a7953630fd2f3fb380f21be14ede0169dd4f # v3.25.12 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index bf068a86377..581cfaddd35 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -132,7 +132,7 @@ jobs: path: bandit.sarif - name: Upload SARIF File into Security Tab - uses: github/codeql-action/upload-sarif@b611370bb5703a7efb587f9d136a52ea24c5c38c # v3.25.11 + uses: github/codeql-action/upload-sarif@4fa2a7953630fd2f3fb380f21be14ede0169dd4f # v3.25.12 with: sarif_file: bandit.sarif From d31afbd637a6cd62b4cbc0100872cac0733aa97a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 12 Jul 2024 07:12:19 -0400 Subject: [PATCH 018/471] CI(deps): Update docker/dockerfile Docker tag to v1.9 (#4037) --- Dockerfile | 2 +- docker/ubuntu/Dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 605fdbee4ec..48c57ff5d6a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -# syntax=docker/dockerfile:1.8@sha256:e87caa74dcb7d46cd820352bfea12591f3dba3ddc4285e19c7dcd13359f7cefd +# syntax=docker/dockerfile:1.9@sha256:fe40cf4e92cd0c467be2cfc30657a680ae2398318afd50b0c80585784c604f28 # Note: This file must be kept in sync in ./Dockerfile and ./docker/ubuntu/Dockerfile. # Changes to this file must be copied over to the other file. diff --git a/docker/ubuntu/Dockerfile b/docker/ubuntu/Dockerfile index 605fdbee4ec..48c57ff5d6a 100644 --- a/docker/ubuntu/Dockerfile +++ b/docker/ubuntu/Dockerfile @@ -1,4 +1,4 @@ -# syntax=docker/dockerfile:1.8@sha256:e87caa74dcb7d46cd820352bfea12591f3dba3ddc4285e19c7dcd13359f7cefd +# syntax=docker/dockerfile:1.9@sha256:fe40cf4e92cd0c467be2cfc30657a680ae2398318afd50b0c80585784c604f28 # Note: This file must be kept in sync in ./Dockerfile and ./docker/ubuntu/Dockerfile. # Changes to this file must be copied over to the other file. From 8f83c24630ac68f5b0507e653179dcf27d3b5f68 Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Sat, 13 Jul 2024 02:32:36 -0400 Subject: [PATCH 019/471] i.atcorr: Fix uninitialized variable (#4022) --- imagery/i.atcorr/main.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/imagery/i.atcorr/main.cpp b/imagery/i.atcorr/main.cpp index 8b2a1779fa3..19a28e7cc35 100644 --- a/imagery/i.atcorr/main.cpp +++ b/imagery/i.atcorr/main.cpp @@ -204,8 +204,7 @@ class TICache { private: struct RBitem set_alt_vis(double alt, double vis) { - struct RBitem rbitem; - + struct RBitem rbitem = {}; /* alt and vis must be in meters */ rbitem.alt = (alt < 0 ? (int)(alt - 0.5) : (int)(alt + 0.5)); rbitem.vis = (int)(vis + 0.5); From cb0087e630b5105d137a57ca5f4b0d0c31aa2922 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Sat, 13 Jul 2024 07:54:36 -0400 Subject: [PATCH 020/471] style: Fix sorted-min-max (FURB192) (#4035) --- man/build_class_graphical.py | 2 +- pyproject.toml | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/man/build_class_graphical.py b/man/build_class_graphical.py index 31caffcdf75..4e295bb18f5 100644 --- a/man/build_class_graphical.py +++ b/man/build_class_graphical.py @@ -128,7 +128,7 @@ def get_module_image(module, images): return image if basename == module: return image - return sorted(candidates, key=len)[0] + return min(candidates, key=len) def generate_page_for_category( diff --git a/pyproject.toml b/pyproject.toml index 74c62b14f0e..f4af0da8f0f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -162,7 +162,6 @@ ignore = [ "FURB152", # math-constant "FURB154", # repeated-global "FURB171", # single-item-membership-test - "FURB192", # sorted-min-max "I001", # unsorted-imports "ISC003", # explicit-string-concatenation "PERF203", # try-except-in-loop From a751f939243c433496c4ebce0f3ccd55b9a502d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Sat, 13 Jul 2024 07:57:06 -0400 Subject: [PATCH 021/471] style: Fix readlines-in-for (FURB129) (#4034) --- gui/wxpython/core/gconsole.py | 2 +- gui/wxpython/core/render.py | 2 +- gui/wxpython/core/settings.py | 2 +- gui/wxpython/core/utils.py | 2 +- gui/wxpython/core/watchdog.py | 2 +- gui/wxpython/core/workspace.py | 2 +- gui/wxpython/gcp/manager.py | 10 +++++----- gui/wxpython/gui_core/ghelp.py | 2 +- gui/wxpython/image2target/ii2t_gis_set.py | 2 +- gui/wxpython/image2target/ii2t_manager.py | 10 +++++----- gui/wxpython/location_wizard/wizard.py | 10 +++++----- gui/wxpython/photo2image/ip2i_manager.py | 4 ++-- gui/wxpython/vnet/vnet_data.py | 4 ++-- lib/init/grass.py | 2 +- pyproject.toml | 1 - python/grass/grassdb/history.py | 3 +-- python/grass/pygrass/raster/category.py | 2 +- python/grass/script/core.py | 2 +- python/grass/script/db.py | 2 +- scripts/d.frame/d.frame.py | 2 +- scripts/g.extension/g.extension.py | 2 +- scripts/i.spectral/i.spectral.py | 2 +- scripts/r.pack/r.pack.py | 2 +- 23 files changed, 36 insertions(+), 38 deletions(-) diff --git a/gui/wxpython/core/gconsole.py b/gui/wxpython/core/gconsole.py index 05dc0ea7519..671e71d1548 100644 --- a/gui/wxpython/core/gconsole.py +++ b/gui/wxpython/core/gconsole.py @@ -664,7 +664,7 @@ def RunCmd( if os.path.splitext(command[0])[1] in {".py", ".sh"}: try: with open(command[0], "r") as sfile: - for line in sfile.readlines(): + for line in sfile: if len(line) < 3: continue if line.startswith(("#%", "# %")): diff --git a/gui/wxpython/core/render.py b/gui/wxpython/core/render.py index 77f4fcbdb94..f9fcb4fd4d4 100644 --- a/gui/wxpython/core/render.py +++ b/gui/wxpython/core/render.py @@ -908,7 +908,7 @@ def GetWindow(self): % {"file": filename, "ret": e} ) - for line in windfile.readlines(): + for line in windfile: line = line.strip() try: key, value = line.split(":", 1) diff --git a/gui/wxpython/core/settings.py b/gui/wxpython/core/settings.py index f98e9bddd38..271c5260cae 100644 --- a/gui/wxpython/core/settings.py +++ b/gui/wxpython/core/settings.py @@ -951,7 +951,7 @@ def _readLegacyFile(self, settings=None): try: line = "" - for line in fd.readlines(): + for line in fd: line = line.rstrip("%s" % os.linesep) group, key = line.split(self.sep)[0:2] kv = line.split(self.sep)[2:] diff --git a/gui/wxpython/core/utils.py b/gui/wxpython/core/utils.py index 3eedc619772..a4611326c5e 100644 --- a/gui/wxpython/core/utils.py +++ b/gui/wxpython/core/utils.py @@ -840,7 +840,7 @@ def StoreEnvVariable(key, value=None, envFile=None): except OSError as e: sys.stderr.write(_("Unable to open file '%s'\n") % envFile) return - for line in fd.readlines(): + for line in fd: line = line.rstrip(os.linesep) try: k, v = (x.strip() for x in line.split(" ", 1)[1].split("=", 1)) diff --git a/gui/wxpython/core/watchdog.py b/gui/wxpython/core/watchdog.py index 06cf8f3d4a5..3821a753246 100644 --- a/gui/wxpython/core/watchdog.py +++ b/gui/wxpython/core/watchdog.py @@ -68,7 +68,7 @@ def on_modified(self, event): time.sleep(0.1) with open(event.src_path, "r") as f: gisrc = {} - for line in f.readlines(): + for line in f: key, val = line.split(":") gisrc[key.strip()] = val.strip() new = os.path.join( diff --git a/gui/wxpython/core/workspace.py b/gui/wxpython/core/workspace.py index 12fd08247a9..65a802d0809 100644 --- a/gui/wxpython/core/workspace.py +++ b/gui/wxpython/core/workspace.py @@ -1757,7 +1757,7 @@ def read(self, parent): return [] line_id = 1 - for line in file.readlines(): + for line in file: self.process_line(line.rstrip("\n"), line_id) line_id += 1 diff --git a/gui/wxpython/gcp/manager.py b/gui/wxpython/gcp/manager.py index a938ae49f6c..87b18f8437d 100644 --- a/gui/wxpython/gcp/manager.py +++ b/gui/wxpython/gcp/manager.py @@ -122,7 +122,7 @@ def __init__(self, parent, giface): self.gisrc_dict = {} try: f = open(self.target_gisrc, "r") - for line in f.readlines(): + for line in f: line = line.replace("\n", "").strip() if len(line) < 1: continue @@ -978,7 +978,7 @@ def OnEnterPage(self, event=None): try: with open(vgrpfile) as f: - for vect in f.readlines(): + for vect in f: vect = vect.strip("\n") if len(vect) < 1: continue @@ -1616,7 +1616,7 @@ def ReadGCPs(self): f = open(self.file["points"], "r") GCPcnt = 0 - for line in f.readlines(): + for line in f: if line[0] == "#" or line == "": continue line = line.replace("\n", "").strip() @@ -1868,7 +1868,7 @@ def OnGeorect(self, event): f = open(self.file["vgrp"]) vectlist = [] try: - for vect in f.readlines(): + for vect in f: vect = vect.strip("\n") if len(vect) < 1: continue @@ -2736,7 +2736,7 @@ def __init__( f = open(self.vgrpfile) try: checked = [] - for line in f.readlines(): + for line in f: line = line.replace("\n", "") if len(line) < 1: continue diff --git a/gui/wxpython/gui_core/ghelp.py b/gui/wxpython/gui_core/ghelp.py index 8366e464a66..ea8de6b552d 100644 --- a/gui/wxpython/gui_core/ghelp.py +++ b/gui/wxpython/gui_core/ghelp.py @@ -809,7 +809,7 @@ def fillContentsFromFile(self, htmlFile, skipDescription=True): try: contents = [] skip = False - for line in open(htmlFile, "rb").readlines(): + for line in open(htmlFile, "rb"): if "DESCRIPTION" in line: skip = False if not skip: diff --git a/gui/wxpython/image2target/ii2t_gis_set.py b/gui/wxpython/image2target/ii2t_gis_set.py index d7985f915da..e3cb0257af1 100644 --- a/gui/wxpython/image2target/ii2t_gis_set.py +++ b/gui/wxpython/image2target/ii2t_gis_set.py @@ -542,7 +542,7 @@ def _readGisRC(self): if gisrc and os.path.isfile(gisrc): try: rc = open(gisrc, "r") - for line in rc.readlines(): + for line in rc: try: key, val = line.split(":", 1) except ValueError as e: diff --git a/gui/wxpython/image2target/ii2t_manager.py b/gui/wxpython/image2target/ii2t_manager.py index 6b543ec2356..19a66f01dba 100644 --- a/gui/wxpython/image2target/ii2t_manager.py +++ b/gui/wxpython/image2target/ii2t_manager.py @@ -162,7 +162,7 @@ def __init__(self, parent, giface): self.gisrc_dict = {} try: f = open(self.target_gisrc, "r") - for line in f.readlines(): + for line in f: line = line.replace("\n", "").strip() if len(line) < 1: continue @@ -974,7 +974,7 @@ def OnEnterPage(self, event=None): f = open(vgrpfile) try: - for vect in f.readlines(): + for vect in f: vect = vect.strip("\n") if len(vect) < 1: continue @@ -1640,7 +1640,7 @@ def ReadGCPs(self): f = open(self.file["control_points"], "r") GCPcnt = 0 - for line in f.readlines(): + for line in f: if line[0] == "#" or line == "": continue line = line.replace("\n", "").strip() @@ -1821,7 +1821,7 @@ def OnGeorect(self, event): f = open(self.file["vgrp"]) vectlist = [] try: - for vect in f.readlines(): + for vect in f: vect = vect.strip("\n") if len(vect) < 1: continue @@ -2682,7 +2682,7 @@ def __init__( f = open(self.vgrpfile) try: checked = [] - for line in f.readlines(): + for line in f: line = line.replace("\n", "") if len(line) < 1: continue diff --git a/gui/wxpython/location_wizard/wizard.py b/gui/wxpython/location_wizard/wizard.py index f4a4cc766ef..408371ad21c 100644 --- a/gui/wxpython/location_wizard/wizard.py +++ b/gui/wxpython/location_wizard/wizard.py @@ -2563,7 +2563,7 @@ def __readData(self): f = open(os.path.join(globalvar.ETCDIR, "proj", "parms.table"), "r") self.projections = {} self.projdesc = {} - for line in f.readlines(): + for line in f: line = line.strip() try: proj, projdesc, params = line.split(":") @@ -2586,7 +2586,7 @@ def __readData(self): f = open(os.path.join(globalvar.ETCDIR, "proj", "datum.table"), "r") self.datums = {} paramslist = [] - for line in f.readlines(): + for line in f: line = line.expandtabs(1) line = line.strip() if line == "" or line[0] == "#": @@ -2603,7 +2603,7 @@ def __readData(self): # read Earth-based ellipsiod definitions f = open(os.path.join(globalvar.ETCDIR, "proj", "ellipse.table"), "r") self.ellipsoids = {} - for line in f.readlines(): + for line in f: line = line.expandtabs(1) line = line.strip() if line == "" or line[0] == "#": @@ -2621,7 +2621,7 @@ def __readData(self): os.path.join(globalvar.ETCDIR, "proj", "ellipse.table.solar.system"), "r" ) self.planetary_ellipsoids = {} - for line in f.readlines(): + for line in f: line = line.expandtabs(1) line = line.strip() if line == "" or line[0] == "#": @@ -2637,7 +2637,7 @@ def __readData(self): # read projection parameter description and parsing table f = open(os.path.join(globalvar.ETCDIR, "proj", "desc.table"), "r") self.paramdesc = {} - for line in f.readlines(): + for line in f: line = line.strip() try: pparam, datatype, proj4term, desc = line.split(":") diff --git a/gui/wxpython/photo2image/ip2i_manager.py b/gui/wxpython/photo2image/ip2i_manager.py index 35744401366..c3b87ac3928 100644 --- a/gui/wxpython/photo2image/ip2i_manager.py +++ b/gui/wxpython/photo2image/ip2i_manager.py @@ -123,7 +123,7 @@ def __init__( self.gisrc_dict = {} try: f = open(self.target_gisrc, "r") - for line in f.readlines(): + for line in f: line = line.replace("\n", "").strip() if len(line) < 1: continue @@ -963,7 +963,7 @@ def ReadGCPs(self): f = open(self.file["points"], "r") GCPcnt = 0 - for line in f.readlines(): + for line in f: if line[0] == "#" or line == "": continue line = line.replace("\n", "").strip() diff --git a/gui/wxpython/vnet/vnet_data.py b/gui/wxpython/vnet/vnet_data.py index 0d84fe6f46a..9f2d11cf46a 100644 --- a/gui/wxpython/vnet/vnet_data.py +++ b/gui/wxpython/vnet/vnet_data.py @@ -1072,7 +1072,7 @@ def GetLastModified(self): ) try: head = open(headPath, "r") - for line in head.readlines(): + for line in head: i = line.find( "MAP DATE:", ) @@ -1299,7 +1299,7 @@ def _getHistStepData(self, histStep): newHistStep = False isSearchedHistStep = False - for line in hist.readlines(): + for line in hist: if not line.strip() and isSearchedHistStep: break elif not line.strip(): diff --git a/lib/init/grass.py b/lib/init/grass.py index c75dc14075b..00baa12f1b3 100755 --- a/lib/init/grass.py +++ b/lib/init/grass.py @@ -1990,7 +1990,7 @@ def print_params(params): date_str = "#define GRASS_HEADERS_DATE " gdate = gpath("include", "grass", "version.h") with open(gdate) as filegdate: - for line in filegdate.readlines(): + for line in filegdate: if line.startswith(date_str): sys.stdout.write( "{}\n".format( diff --git a/pyproject.toml b/pyproject.toml index f4af0da8f0f..24ee558e221 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -154,7 +154,6 @@ ignore = [ "FURB101", # read-whole-file "FURB103", # write-whole-file. "FURB118", # reimplemented-operator - "FURB129", # readlines-in-for "FURB131", # delete-full-slice "FURB140", # reimplemented-starmap "FURB142", # for-loop-set-mutations diff --git a/python/grass/grassdb/history.py b/python/grass/grassdb/history.py index 2657632c9e1..ec7b1dc14eb 100644 --- a/python/grass/grassdb/history.py +++ b/python/grass/grassdb/history.py @@ -86,8 +86,7 @@ def _read_from_plain_text(history_path): history_path, encoding="utf-8", mode="r", errors="replace" ) as file_history: content_list = [ - {"command": line.strip(), "command_info": None} - for line in file_history.readlines() + {"command": line.strip(), "command_info": None} for line in file_history ] except OSError as e: raise OSError( diff --git a/python/grass/pygrass/raster/category.py b/python/grass/pygrass/raster/category.py index 04049754954..2cee672ed32 100644 --- a/python/grass/pygrass/raster/category.py +++ b/python/grass/pygrass/raster/category.py @@ -299,7 +299,7 @@ def read_rules(self, filename, sep=":"): """ self.reset() with open(filename, "r") as f: - for row in f.readlines(): + for row in f: cat = row.strip().split(sep) if len(cat) == 2: label, min_cat = cat diff --git a/python/grass/script/core.py b/python/grass/script/core.py index a790307aa3c..5458ea1cb78 100644 --- a/python/grass/script/core.py +++ b/python/grass/script/core.py @@ -1284,7 +1284,7 @@ def region_env(region3d=False, flags=None, env=None, **kwargs): ) with open(windfile, "r") as fd: grass_region = "" - for line in fd.readlines(): + for line in fd: key, value = (x.strip() for x in line.split(":", 1)) if kwargs and key not in {"proj", "zone"}: continue diff --git a/python/grass/script/db.py b/python/grass/script/db.py index 421c57267d7..2725ff06ebe 100644 --- a/python/grass/script/db.py +++ b/python/grass/script/db.py @@ -191,7 +191,7 @@ def db_select(sql=None, filename=None, table=None, env=None, **args): fatal(_("Fetching data failed")) ofile = open(fname) - result = [tuple(x.rstrip(os.linesep).split(args["sep"])) for x in ofile.readlines()] + result = [tuple(x.rstrip(os.linesep).split(args["sep"])) for x in ofile] ofile.close() try_remove(fname) diff --git a/scripts/d.frame/d.frame.py b/scripts/d.frame/d.frame.py index 2d75457bee0..a1befa3a1dd 100755 --- a/scripts/d.frame/d.frame.py +++ b/scripts/d.frame/d.frame.py @@ -96,7 +96,7 @@ def read_monitor_file(monitor, ftype="env"): fatal(_("Unable to get monitor info. %s"), e) lines = [] - for line in fd.readlines(): + for line in fd: lines.append(line) fd.close() diff --git a/scripts/g.extension/g.extension.py b/scripts/g.extension/g.extension.py index 50e8a97d2bb..4c64a9f9ff8 100644 --- a/scripts/g.extension/g.extension.py +++ b/scripts/g.extension/g.extension.py @@ -2000,7 +2000,7 @@ def install_extension_std_platforms(name, source, url, branch): if filename == "Makefile": # get the module name: PGM = with open(os.path.join(r, "Makefile")) as fp: - for line in fp.readlines(): + for line in fp: if re.match(r"PGM.*.=|PGM=", line): try: modulename = line.split("=")[1].strip() diff --git a/scripts/i.spectral/i.spectral.py b/scripts/i.spectral/i.spectral.py index a54cd92e07e..9ac8cd09bf3 100755 --- a/scripts/i.spectral/i.spectral.py +++ b/scripts/i.spectral/i.spectral.py @@ -211,7 +211,7 @@ def draw_linegraph(what): ) ) with open(gcore.parse_command("d.mon", flags="g", quiet=True)["env"]) as f: - for line in f.readlines(): + for line in f: if "GRASS_RENDER_FILE=" in line: gcore.info( _( diff --git a/scripts/r.pack/r.pack.py b/scripts/r.pack/r.pack.py index bd8c96f74cf..51420b74a77 100644 --- a/scripts/r.pack/r.pack.py +++ b/scripts/r.pack/r.pack.py @@ -94,7 +94,7 @@ def main(): vrt = os.path.join(map_file["file"], "vrt") if os.path.exists(vrt): with open(vrt, "r") as f: - for r in f.readlines(): + for r in f: map, mapset = r.split("@") map_basedir = os.path.sep.join( os.path.normpath( From 3b309db29aa0b4ff404e89565efa9f47fa513d38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Sat, 13 Jul 2024 08:02:28 -0400 Subject: [PATCH 022/471] style: Fix repeated-equality-comparison (PLR1714) (#4042) * style: Fix repeated-equality-comparison (PLR1714) * style: Fix literal-membership (PLR6201) --- gui/wxpython/core/utils.py | 4 ++-- gui/wxpython/core/watchdog.py | 12 +++------- gui/wxpython/gui_core/ghelp.py | 2 +- gui/wxpython/history/browser.py | 6 ++--- gui/wxpython/lmgr/statusbar.py | 2 +- gui/wxpython/main_window/frame.py | 10 ++++----- gui/wxpython/nviz/tools.py | 4 ++-- gui/wxpython/psmap/frame.py | 2 +- gui/wxpython/rlisetup/wizard.py | 4 ++-- gui/wxpython/tools/build_modules_xml.py | 2 +- gui/wxpython/web_services/widgets.py | 2 +- gui/wxpython/wxplot/base.py | 4 ++-- pyproject.toml | 1 - python/grass/gunittest/gutils.py | 4 ++-- python/grass/pygrass/raster/__init__.py | 6 ++--- python/grass/pygrass/vector/abstract.py | 9 +++----- python/grass/script/array.py | 2 +- python/grass/script/utils.py | 4 ++-- .../temporal/abstract_space_time_dataset.py | 6 ++--- python/grass/temporal/base.py | 2 +- python/grass/temporal/core.py | 2 +- python/grass/temporal/datetime_math.py | 2 +- python/grass/temporal/extract.py | 2 +- python/grass/temporal/factory.py | 6 ++--- python/grass/temporal/mapcalc.py | 2 +- python/grass/temporal/open_stds.py | 22 +++++-------------- python/grass/temporal/spatial_extent.py | 12 +++++----- .../temporal/spatio_temporal_relationships.py | 6 ++--- python/grass/temporal/stds_export.py | 2 +- python/grass/temporal/stds_import.py | 2 +- python/grass/temporal/temporal_extent.py | 4 ++-- python/grass/temporal/temporal_operator.py | 10 ++++----- .../temporal/temporal_raster3d_algebra.py | 2 +- .../grass/temporal/temporal_raster_algebra.py | 2 +- .../grass/temporal/temporal_vector_algebra.py | 2 +- python/grass/temporal/univar_statistics.py | 2 +- .../testsuite/test_addons_download.py | 2 +- scripts/v.what.strds/v.what.strds.py | 2 +- temporal/t.rast.to.rast3/t.rast.to.rast3.py | 12 +++++----- temporal/t.vect.db.select/t.vect.db.select.py | 4 ++-- .../t.vect.observe.strds.py | 2 +- .../t.vect.what.strds/t.vect.what.strds.py | 2 +- 42 files changed, 85 insertions(+), 107 deletions(-) diff --git a/gui/wxpython/core/utils.py b/gui/wxpython/core/utils.py index a4611326c5e..66137f3c974 100644 --- a/gui/wxpython/core/utils.py +++ b/gui/wxpython/core/utils.py @@ -288,7 +288,7 @@ def ListOfMapsets(get="ordered"): :return: list of mapsets :return: [] on error """ - if get == "all" or get == "ordered": + if get in {"all", "ordered"}: ret = RunCommand("g.mapsets", read=True, quiet=True, flags="l", sep="newline") if not ret: return [] @@ -297,7 +297,7 @@ def ListOfMapsets(get="ordered"): if get == "all": return mapsets_all - if get == "accessible" or get == "ordered": + if get in {"accessible", "ordered"}: ret = RunCommand("g.mapsets", read=True, quiet=True, flags="p", sep="newline") if not ret: return [] diff --git a/gui/wxpython/core/watchdog.py b/gui/wxpython/core/watchdog.py index 3821a753246..9315a1010d6 100644 --- a/gui/wxpython/core/watchdog.py +++ b/gui/wxpython/core/watchdog.py @@ -94,9 +94,7 @@ def __init__(self, patterns, element, event_handler): self.event_handler = event_handler def on_created(self, event): - if ( - self.element == "vector" or self.element == "raster_3d" - ) and not event.is_directory: + if (self.element in {"vector", "raster_3d"}) and not event.is_directory: return evt = updateMapset( src_path=event.src_path, @@ -107,9 +105,7 @@ def on_created(self, event): wx.PostEvent(self.event_handler, evt) def on_deleted(self, event): - if ( - self.element == "vector" or self.element == "raster_3d" - ) and not event.is_directory: + if (self.element in {"vector", "raster_3d"}) and not event.is_directory: return evt = updateMapset( src_path=event.src_path, @@ -120,9 +116,7 @@ def on_deleted(self, event): wx.PostEvent(self.event_handler, evt) def on_moved(self, event): - if ( - self.element == "vector" or self.element == "raster_3d" - ) and not event.is_directory: + if (self.element in {"vector", "raster_3d"}) and not event.is_directory: return evt = updateMapset( src_path=event.src_path, diff --git a/gui/wxpython/gui_core/ghelp.py b/gui/wxpython/gui_core/ghelp.py index ea8de6b552d..0602aeaea0c 100644 --- a/gui/wxpython/gui_core/ghelp.py +++ b/gui/wxpython/gui_core/ghelp.py @@ -630,7 +630,7 @@ def _langPanel(self, lang, js): # panel.Collapse(True) pageSizer = wx.BoxSizer(wx.VERTICAL) for k, v in js.items(): - if k != "total" and k != "name": + if k not in {"total", "name"}: box = self._langBox(win, k, v) pageSizer.Add(box, proportion=1, flag=wx.EXPAND | wx.ALL, border=3) diff --git a/gui/wxpython/history/browser.py b/gui/wxpython/history/browser.py index 9bf5620d12f..6a31523d51f 100644 --- a/gui/wxpython/history/browser.py +++ b/gui/wxpython/history/browser.py @@ -165,12 +165,10 @@ def _createRegionSettingsBox(self): def _general_info_filter(self, key, value): filter_keys = ["timestamp", "runtime", "status"] - return key in filter_keys or ( - (key == "mask2d" or key == "mask3d") and value is True - ) + return key in filter_keys or ((key in {"mask2d", "mask3d"}) and value is True) def _region_settings_filter(self, key): - return (key != "projection") and (key != "zone") and (key != "cells") + return key not in {"projection", "zone", "cells"} def _updateGeneralInfoBox(self, command_info): """Update a static box for displaying general info about the command. diff --git a/gui/wxpython/lmgr/statusbar.py b/gui/wxpython/lmgr/statusbar.py index e8c62c36ede..ac4748bfe1f 100644 --- a/gui/wxpython/lmgr/statusbar.py +++ b/gui/wxpython/lmgr/statusbar.py @@ -94,7 +94,7 @@ def dbChanged(self, map=None, newname=None): :param str map: map that is changed :param str newname: new map """ - if map == self.mask_layer or newname == self.mask_layer: + if self.mask_layer in {map, newname}: self.Refresh() self.giface.updateMap.emit() diff --git a/gui/wxpython/main_window/frame.py b/gui/wxpython/main_window/frame.py index 41c6c57365f..57cf6efb868 100644 --- a/gui/wxpython/main_window/frame.py +++ b/gui/wxpython/main_window/frame.py @@ -1058,11 +1058,11 @@ def _closePageNoEvent(self, pgnum_dict, is_docked): def _focusPage(self, notification): """Focus the 'Console' notebook page according to event notification.""" - if ( - notification == Notification.HIGHLIGHT - or notification == Notification.MAKE_VISIBLE - or notification == Notification.RAISE_WINDOW - ): + if notification in { + Notification.HIGHLIGHT, + Notification.MAKE_VISIBLE, + Notification.RAISE_WINDOW, + }: self.FocusPage("Console") def FocusPage(self, page_text): diff --git a/gui/wxpython/nviz/tools.py b/gui/wxpython/nviz/tools.py index 1814772839c..19d35b13c20 100644 --- a/gui/wxpython/nviz/tools.py +++ b/gui/wxpython/nviz/tools.py @@ -2642,7 +2642,7 @@ def GetLayerData(self, nvizType, nameOnly=False): if nameOnly: return name - if nvizType == "surface" or nvizType == "fringe": + if nvizType in {"surface", "fringe"}: return self._getLayerPropertiesByName(name, mapType="raster") elif nvizType == "vector": return self._getLayerPropertiesByName(name, mapType="vector") @@ -4044,7 +4044,7 @@ def UpdateVectorShow(self, vecType, enabled): :param vecType: vector type (lines, points) """ - if vecType != "lines" and vecType != "points": + if vecType not in {"lines", "points"}: return False for win in self.win["vector"][vecType].keys(): diff --git a/gui/wxpython/psmap/frame.py b/gui/wxpython/psmap/frame.py index 4166ea63676..a03021acc2f 100644 --- a/gui/wxpython/psmap/frame.py +++ b/gui/wxpython/psmap/frame.py @@ -570,7 +570,7 @@ def getFile(self, wildcard): suffix = suffix[dlg.GetFilterIndex()] if not os.path.splitext(filename)[1]: filename = filename + suffix - elif os.path.splitext(filename)[1] != suffix and suffix != "": + elif suffix not in {os.path.splitext(filename)[1], ""}: filename = os.path.splitext(filename)[0] + suffix dlg.Destroy() diff --git a/gui/wxpython/rlisetup/wizard.py b/gui/wxpython/rlisetup/wizard.py index d5d508b7fb4..12888948ec3 100644 --- a/gui/wxpython/rlisetup/wizard.py +++ b/gui/wxpython/rlisetup/wizard.py @@ -362,7 +362,7 @@ def _write_area(self, fil): fil.write("\nMOVINGWINDOW\n") # KUNITSC = samplingtype=units, regionbox=keyboard, shape=cirlce # KUNITSR = samplingtype=units, regionbox=keyboard, shape=rectangle - elif samtype == SamplingType.KUNITSC or samtype == SamplingType.KUNITSR: + elif samtype in {SamplingType.KUNITSC, SamplingType.KUNITSR}: if samtype == SamplingType.KUNITSC: self._circle(self.units.width, self.units.height) cl = float(self.CIR_CL) / float(self.rasterinfo["cols"]) @@ -1115,7 +1115,7 @@ def ShowExtraOptions(self, samtype): self.sizer.Hide(self.areaPanel) self.sizer.Hide(self.calculatingAreas) self.sizer.Show(self.regionNumPanel) - elif samtype == SamplingType.UNITS or samtype == SamplingType.MVWIN: + elif samtype in {SamplingType.UNITS, SamplingType.MVWIN}: self.sizer.Hide(self.regionNumPanel) self.sizer.Hide(self.areaPanel) self.sizer.Hide(self.calculatingAreas) diff --git a/gui/wxpython/tools/build_modules_xml.py b/gui/wxpython/tools/build_modules_xml.py index 8dc341d7c75..a97bdeb4627 100644 --- a/gui/wxpython/tools/build_modules_xml.py +++ b/gui/wxpython/tools/build_modules_xml.py @@ -55,7 +55,7 @@ def parse_modules(fd): indent = 4 for m in sorted(mlist): # TODO: get rid of g.mapsets_picker.py - if m == "g.mapsets_picker.py" or m == "g.parser": + if m in {"g.mapsets_picker.py", "g.parser"}: continue desc, keyw = get_module_metadata(m) fd.write('%s\n' % (" " * indent, m)) diff --git a/gui/wxpython/web_services/widgets.py b/gui/wxpython/web_services/widgets.py index 2da957ef948..811b718a012 100644 --- a/gui/wxpython/web_services/widgets.py +++ b/gui/wxpython/web_services/widgets.py @@ -508,7 +508,7 @@ def OnCmdOutput(self, event): """Manage cmd output.""" if Debug.GetLevel() != 0: Debug.msg(1, event.text) - elif event.type != "message" and event.type != "warning": + elif event.type not in {"message", "warning"}: self.cmd_err_str += event.text + os.linesep def _prepareForNewConn(self, url, username, password): diff --git a/gui/wxpython/wxplot/base.py b/gui/wxpython/wxplot/base.py index d52a8cde52e..7dfb444bd1e 100755 --- a/gui/wxpython/wxplot/base.py +++ b/gui/wxpython/wxplot/base.py @@ -584,7 +584,7 @@ def PlotText(self, event): ) btnval = dlg.ShowModal() - if btnval == wx.ID_SAVE or btnval == wx.ID_OK or btnval == wx.ID_CANCEL: + if btnval in {wx.ID_SAVE, wx.ID_OK, wx.ID_CANCEL}: dlg.Destroy() def PlotOptions(self, event): @@ -602,7 +602,7 @@ def PlotOptions(self, event): ) btnval = dlg.ShowModal() - if btnval == wx.ID_SAVE or btnval == wx.ID_OK or btnval == wx.ID_CANCEL: + if btnval in {wx.ID_SAVE, wx.ID_OK, wx.ID_CANCEL}: dlg.Destroy() self.Update() diff --git a/pyproject.toml b/pyproject.toml index 24ee558e221..ab792fe2ac6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -190,7 +190,6 @@ ignore = [ "PLR0917", # too-many-positional "PLR1702", # too-many-nested-blocks "PLR1704", # redefined-argument-from-local - "PLR1714", # repeated-equality-comparison "PLR1733", # unnecessary-dict-index-lookup "PLR2004", # magic-value-comparison "PLR5501", # collapsible-else-if diff --git a/python/grass/gunittest/gutils.py b/python/grass/gunittest/gutils.py index 7c1f2248089..16618e729f5 100644 --- a/python/grass/gunittest/gutils.py +++ b/python/grass/gunittest/gutils.py @@ -39,9 +39,9 @@ def is_map_in_mapset(name, type, mapset=None): # so anything accepted by g.findfile will work but this can change in the # future (the documentation is clear about what's legal) # supporting both short and full names - if type == "rast" or type == "raster": + if type in {"rast", "raster"}: type = "cell" - elif type == "rast3d" or type == "raster3d": + elif type in {"rast3d", "raster3d"}: type = "grid3" elif type == "vect": type = "vector" diff --git a/python/grass/pygrass/raster/__init__.py b/python/grass/pygrass/raster/__init__.py index 2fe95602fdd..818d9576514 100644 --- a/python/grass/pygrass/raster/__init__.py +++ b/python/grass/pygrass/raster/__init__.py @@ -518,12 +518,12 @@ def open(self, mode=None, mtype=None, overwrite=None): self.cats.mtype = self.mtype self.cats.read() self.hist.read() - if (self.mode == "w" or self.mode == "rw") and self.overwrite is False: + if (self.mode in {"w", "rw"}) and self.overwrite is False: str_err = _("Raster map <{0}> already exists. Use overwrite.") fatal(str_err.format(self)) # We copy the raster map content into the segments - if self.mode == "rw" or self.mode == "r": + if self.mode in {"rw", "r"}: self._fd = libraster.Rast_open_old(self.name, self.mapset) self._gtype = libraster.Rast_get_map_type(self._fd) self.mtype = RTYPE_STR[self._gtype] @@ -563,7 +563,7 @@ def close(self, rm_temp_files=True): :param rm_temp_files: if True all the segments file will be removed :type rm_temp_files: bool """ - if self.mode == "w" or self.mode == "rw": + if self.mode in {"w", "rw"}: self.segment.flush() self.segment2map() if rm_temp_files: diff --git a/python/grass/pygrass/vector/abstract.py b/python/grass/pygrass/vector/abstract.py index de4f74b9460..3929006ef25 100644 --- a/python/grass/pygrass/vector/abstract.py +++ b/python/grass/pygrass/vector/abstract.py @@ -20,10 +20,7 @@ def is_open(c_mapinfo): """Return if the Vector is open""" - return ( - c_mapinfo.contents.open != 0 - and c_mapinfo.contents.open != libvect.VECT_CLOSED_CODE - ) + return c_mapinfo.contents.open not in {0, libvect.VECT_CLOSED_CODE} # ============================================= @@ -465,8 +462,8 @@ def close(self, build=False): str_err = "Error when trying to close the map with Vect_close" raise GrassError(str_err) if ( - self.c_mapinfo.contents.mode == libvect.GV_MODE_RW - or self.c_mapinfo.contents.mode == libvect.GV_MODE_WRITE + self.c_mapinfo.contents.mode + in {libvect.GV_MODE_RW, libvect.GV_MODE_WRITE} ) and build: self.build() diff --git a/python/grass/script/array.py b/python/grass/script/array.py index 7358d397f32..29c99d7617c 100644 --- a/python/grass/script/array.py +++ b/python/grass/script/array.py @@ -307,7 +307,7 @@ def write(self, mapname, null=None, overwrite=None, quiet=None): flags = None if kind == "f": - if size != 4 and size != 8: + if size not in {4, 8}: raise ValueError(_("Invalid FP size <%d>") % size) elif kind in "biu": if size not in {1, 2, 4, 8}: diff --git a/python/grass/script/utils.py b/python/grass/script/utils.py index 57a3f3cd2de..0269ff15d2d 100644 --- a/python/grass/script/utils.py +++ b/python/grass/script/utils.py @@ -71,9 +71,9 @@ def separator(sep): return "," elif sep == "space": return " " - elif sep == "tab" or sep == "\\t": + elif sep in {"tab", "\\t"}: return "\t" - elif sep == "newline" or sep == "\\n": + elif sep in {"newline", "\\n"}: return "\n" return sep diff --git a/python/grass/temporal/abstract_space_time_dataset.py b/python/grass/temporal/abstract_space_time_dataset.py index 68a8c5498e9..7f8209d8072 100644 --- a/python/grass/temporal/abstract_space_time_dataset.py +++ b/python/grass/temporal/abstract_space_time_dataset.py @@ -590,7 +590,7 @@ def check_temporal_topology(self, maps=None, dbif=None): map_time = self.get_map_time() - if map_time == "interval" or map_time == "mixed": + if map_time in {"interval", "mixed"}: if "equal" in relations and relations["equal"] > 0: return False if "during" in relations and relations["during"] > 0: @@ -966,9 +966,9 @@ def sample_by_dataset_sql(self, stds, method=None, spatial=False, dbif=None): use_during = True if name == "overlap": use_overlap = True - if name == "contain" or name == "contains": + if name in {"contain", "contains"}: use_contain = True - if name == "equal" or name == "equals": + if name in {"equal", "equals"}: use_equal = True if name == "follows": use_follows = True diff --git a/python/grass/temporal/base.py b/python/grass/temporal/base.py index a275fa1353f..887ed63fa2a 100644 --- a/python/grass/temporal/base.py +++ b/python/grass/temporal/base.py @@ -741,7 +741,7 @@ def set_ttype(self, ttype): :param ttype: The temporal type of the dataset "absolute or relative" """ - if ttype is None or (ttype != "absolute" and ttype != "relative"): + if ttype is None or (ttype not in {"absolute", "relative"}): self.D["temporal_type"] = "absolute" else: self.D["temporal_type"] = ttype diff --git a/python/grass/temporal/core.py b/python/grass/temporal/core.py index 85834df5d41..9ae898cb536 100644 --- a/python/grass/temporal/core.py +++ b/python/grass/temporal/core.py @@ -62,7 +62,7 @@ def profile_function(func): """Profiling function provided by the temporal framework""" do_profiling = os.getenv("GRASS_TGIS_PROFILE") - if do_profiling == "True" or do_profiling == "1": + if do_profiling in {"True", "1"}: import cProfile import io import pstats diff --git a/python/grass/temporal/datetime_math.py b/python/grass/temporal/datetime_math.py index 7ed3c7fb8f5..b0c0d81cf9d 100644 --- a/python/grass/temporal/datetime_math.py +++ b/python/grass/temporal/datetime_math.py @@ -242,7 +242,7 @@ def modify_datetime_by_string(mydate, increment, mult=1, sign=1): :return: The new datetime object or none in case of an error """ sign = int(sign) - if sign != 1 and sign != -1: + if sign not in {1, -1}: return None if increment: diff --git a/python/grass/temporal/extract.py b/python/grass/temporal/extract.py index eccfc9b6acc..94a4fd0f1c9 100644 --- a/python/grass/temporal/extract.py +++ b/python/grass/temporal/extract.py @@ -251,7 +251,7 @@ def extract_dataset( # In case of a empty map continue, do not register empty # maps - if type == "raster" or type == "raster3d": + if type in {"raster", "raster3d"}: if ( new_map.metadata.get_min() is None and new_map.metadata.get_max() is None diff --git a/python/grass/temporal/factory.py b/python/grass/temporal/factory.py index b4aec29f7fa..ba7f5e7d4f0 100644 --- a/python/grass/temporal/factory.py +++ b/python/grass/temporal/factory.py @@ -44,11 +44,11 @@ def dataset_factory(type, id): sp = SpaceTimeRaster3DDataset(id) elif type == "stvds": sp = SpaceTimeVectorDataset(id) - elif type == "rast" or type == "raster": + elif type in {"rast", "raster"}: sp = RasterDataset(id) - elif type == "raster_3d" or type == "rast3d" or type == "raster3d": + elif type in {"raster_3d", "rast3d", "raster3d"}: sp = Raster3DDataset(id) - elif type == "vect" or type == "vector": + elif type in {"vect", "vector"}: sp = VectorDataset(id) else: msgr = get_tgis_message_interface() diff --git a/python/grass/temporal/mapcalc.py b/python/grass/temporal/mapcalc.py index b3db20eef98..1d098f33647 100644 --- a/python/grass/temporal/mapcalc.py +++ b/python/grass/temporal/mapcalc.py @@ -312,7 +312,7 @@ def dataset_mapcalculator( proc_list[proc_count].start() proc_count += 1 - if proc_count == nprocs or proc_count == num or count == num: + if proc_count in {nprocs, num} or count == num: proc_count = 0 exitcodes = 0 for proc in proc_list: diff --git a/python/grass/temporal/open_stds.py b/python/grass/temporal/open_stds.py index 75d1c1eb075..f17288695e3 100644 --- a/python/grass/temporal/open_stds.py +++ b/python/grass/temporal/open_stds.py @@ -58,18 +58,13 @@ def open_old_stds(name, type, dbif=None): msgr.fatal("Invalid name of the space time dataset. Only one dot allowed.") id = name + "@" + mapset - if type == "strds" or type == "rast" or type == "raster": + if type in {"strds", "rast", "raster"}: sp = dataset_factory("strds", id) if semantic_label: sp.set_semantic_label(semantic_label) - elif ( - type == "str3ds" - or type == "raster3d" - or type == "rast3d" - or type == "raster_3d" - ): + elif type in {"str3ds", "raster3d", "rast3d", "raster_3d"}: sp = dataset_factory("str3ds", id) - elif type == "stvds" or type == "vect" or type == "vector": + elif type in {"stvds", "vect", "vector"}: sp = dataset_factory("stvds", id) else: msgr.fatal(_("Unknown type: %s") % (type)) @@ -123,21 +118,16 @@ def check_new_stds(name, type, dbif=None, overwrite=False): ) id = name - if type == "strds" or type == "rast" or type == "raster": + if type in {"strds", "rast", "raster"}: if name.find(".") > -1: # a dot is used as a separator for semantic label filtering msgr.fatal( _("Illegal dataset name <{}>. Character '.' not allowed.").format(name) ) sp = dataset_factory("strds", id) - elif ( - type == "str3ds" - or type == "raster3d" - or type == "rast3d " - or type == "raster_3d" - ): + elif type in {"str3ds", "raster3d", "rast3d ", "raster_3d"}: sp = dataset_factory("str3ds", id) - elif type == "stvds" or type == "vect" or type == "vector": + elif type in {"stvds", "vect", "vector"}: sp = dataset_factory("stvds", id) else: msgr.error(_("Unknown type: %s") % (type)) diff --git a/python/grass/temporal/spatial_extent.py b/python/grass/temporal/spatial_extent.py index 4f6ecb8280c..b30aaa799af 100644 --- a/python/grass/temporal/spatial_extent.py +++ b/python/grass/temporal/spatial_extent.py @@ -1262,11 +1262,11 @@ def meet_2d(self, extent): return False # Check boundaries of the faces - if edge == "E" or edge == "W": + if edge in {"E", "W"}: if N < eS or S > eN: return False - if edge == "N" or edge == "S": + if edge in {"N", "S"}: if E < eW or W > eE: return False @@ -1332,19 +1332,19 @@ def meet(self, extent): return False # Check boundaries of the faces - if edge == "E" or edge == "W": + if edge in {"E", "W"}: if N < eS or S > eN: return False if T < eB or B > eT: return False - if edge == "N" or edge == "S": + if edge in {"N", "S"}: if E < eW or W > eE: return False if T < eB or B > eT: return False - if edge == "T" or edge == "B": + if edge in {"T", "B"}: if E < eW or W > eE: return False if N < eS or S > eN: @@ -1806,7 +1806,7 @@ def set_projection(self, proj): """Set the projection of the spatial extent it should be XY or LL. As default the projection is XY """ - if proj is None or (proj != "XY" and proj != "LL"): + if proj is None or (proj not in {"XY", "LL"}): self.D["proj"] = "XY" else: self.D["proj"] = proj diff --git a/python/grass/temporal/spatio_temporal_relationships.py b/python/grass/temporal/spatio_temporal_relationships.py index 04fc4e58b8e..d22bc2bb0da 100644 --- a/python/grass/temporal/spatio_temporal_relationships.py +++ b/python/grass/temporal/spatio_temporal_relationships.py @@ -620,7 +620,7 @@ def __contains__(self, _map): def set_temoral_relationship(A, B, relation): - if relation == "equal" or relation == "equals": + if relation in {"equal", "equals"}: if A != B: if not B.get_equal() or (B.get_equal() and A not in B.get_equal()): B.append_equal(A) @@ -636,7 +636,7 @@ def set_temoral_relationship(A, B, relation): B.append_precedes(A) if not A.get_follows() or (A.get_follows() and B not in A.get_follows()): A.append_follows(B) - elif relation == "during" or relation == "starts" or relation == "finishes": + elif relation in {"during", "starts", "finishes"}: if not B.get_during() or (B.get_during() and A not in B.get_during()): B.append_during(A) if not A.get_contains() or (A.get_contains() and B not in A.get_contains()): @@ -651,7 +651,7 @@ def set_temoral_relationship(A, B, relation): B.append_finishes(A) if not A.get_finished() or (A.get_finished() and B not in A.get_finished()): A.append_finished(B) - elif relation == "contains" or relation == "started" or relation == "finished": + elif relation in {"contains", "started", "finished"}: if not B.get_contains() or (B.get_contains() and A not in B.get_contains()): B.append_contains(A) if not A.get_during() or (A.get_during() and B not in A.get_during()): diff --git a/python/grass/temporal/stds_export.py b/python/grass/temporal/stds_export.py index 7ef84710e76..f2fdd7ca9bf 100644 --- a/python/grass/temporal/stds_export.py +++ b/python/grass/temporal/stds_export.py @@ -381,7 +381,7 @@ def export_stds( if rows: if type_ == "strds": - if format_ == "GTiff" or format_ == "AAIGrid": + if format_ in {"GTiff", "AAIGrid"}: _export_raster_maps_as_gdal( rows, tar, list_file, new_cwd, fs, format_, datatype, **kwargs ) diff --git a/python/grass/temporal/stds_import.py b/python/grass/temporal/stds_import.py index eb68838e798..e8684d9ecb4 100644 --- a/python/grass/temporal/stds_import.py +++ b/python/grass/temporal/stds_import.py @@ -518,7 +518,7 @@ def import_stds( # Import the maps if type_ == "strds": - if format_ == "GTiff" or format_ == "AAIGrid": + if format_ in {"GTiff", "AAIGrid"}: _import_raster_maps_from_gdal( maplist, overr, diff --git a/python/grass/temporal/temporal_extent.py b/python/grass/temporal/temporal_extent.py index b3ce5714c3b..fd9c63db2fa 100644 --- a/python/grass/temporal/temporal_extent.py +++ b/python/grass/temporal/temporal_extent.py @@ -176,7 +176,7 @@ def intersect(self, extent): """ relation = self.temporal_relation(extent) - if relation == "after" or relation == "before": + if relation in {"after", "before"}: return None if self.D["end_time"] is None: @@ -423,7 +423,7 @@ def union(self, extent): relation = self.temporal_relation(extent) - if relation == "after" or relation == "before": + if relation in {"after", "before"}: return None return self.disjoint_union(extent) diff --git a/python/grass/temporal/temporal_operator.py b/python/grass/temporal/temporal_operator.py index bbecd6720a9..572e1559611 100644 --- a/python/grass/temporal/temporal_operator.py +++ b/python/grass/temporal/temporal_operator.py @@ -241,19 +241,19 @@ def temporal_symbol(self, t): # Check for reserved words if t.value in TemporalOperatorLexer.relations.keys(): t.type = TemporalOperatorLexer.relations.get(t.value) - elif t.value == "l" or t.value == "left": + elif t.value in {"l", "left"}: t.value = "l" t.type = "LEFTREF" - elif t.value == "r" or t.value == "right": + elif t.value in {"r", "right"}: t.value = "r" t.type = "RIGHTREF" - elif t.value == "u" or t.value == "union": + elif t.value in {"u", "union"}: t.value = "u" t.type = "UNION" - elif t.value == "d" or t.value == "disjoint": + elif t.value in {"d", "disjoint"}: t.value = "d" t.type = "DISJOINT" - elif t.value == "i" or t.value == "intersect": + elif t.value in {"i", "intersect"}: t.value = "i" t.type = "INTERSECT" else: diff --git a/python/grass/temporal/temporal_raster3d_algebra.py b/python/grass/temporal/temporal_raster3d_algebra.py index b8920e27848..cd26f8cb220 100644 --- a/python/grass/temporal/temporal_raster3d_algebra.py +++ b/python/grass/temporal/temporal_raster3d_algebra.py @@ -63,7 +63,7 @@ def parse(self, expression, basename=None, overwrite=False): if not tok: break - if tok.type == "STVDS" or tok.type == "STRDS" or tok.type == "STR3DS": + if tok.type in {"STVDS", "STRDS", "STR3DS"}: raise SyntaxError("Syntax error near '%s'" % (tok.type)) self.lexer = TemporalRasterAlgebraLexer() diff --git a/python/grass/temporal/temporal_raster_algebra.py b/python/grass/temporal/temporal_raster_algebra.py index 182a679c6a7..1785183cc50 100644 --- a/python/grass/temporal/temporal_raster_algebra.py +++ b/python/grass/temporal/temporal_raster_algebra.py @@ -109,7 +109,7 @@ def parse(self, expression, basename=None, overwrite=False): if not tok: break - if tok.type == "STVDS" or tok.type == "STRDS" or tok.type == "STR3DS": + if tok.type in {"STVDS", "STRDS", "STR3DS"}: raise SyntaxError("Syntax error near '%s'" % (tok.type)) self.lexer = TemporalRasterAlgebraLexer() diff --git a/python/grass/temporal/temporal_vector_algebra.py b/python/grass/temporal/temporal_vector_algebra.py index a5c5bc27533..7afc9978df6 100644 --- a/python/grass/temporal/temporal_vector_algebra.py +++ b/python/grass/temporal/temporal_vector_algebra.py @@ -162,7 +162,7 @@ def parse(self, expression, basename=None, overwrite=False): if not tok: break - if tok.type == "STVDS" or tok.type == "STRDS" or tok.type == "STR3DS": + if tok.type in {"STVDS", "STRDS", "STR3DS"}: raise SyntaxError("Syntax error near '%s'" % (tok.type)) self.lexer = TemporalVectorAlgebraLexer() diff --git a/python/grass/temporal/univar_statistics.py b/python/grass/temporal/univar_statistics.py index ddf55345b34..9bf29f74eba 100755 --- a/python/grass/temporal/univar_statistics.py +++ b/python/grass/temporal/univar_statistics.py @@ -440,7 +440,7 @@ def print_vector_dataset_univar_statistics( else: string += fs + fs + fs - if type == "point" or type == "centroid": + if type in {"point", "centroid"}: if "mean" in stats: string += ( fs diff --git a/scripts/g.extension/testsuite/test_addons_download.py b/scripts/g.extension/testsuite/test_addons_download.py index e5077f9894e..384c6a4950f 100644 --- a/scripts/g.extension/testsuite/test_addons_download.py +++ b/scripts/g.extension/testsuite/test_addons_download.py @@ -159,7 +159,7 @@ def test_github_install_official_multimodule(self): for file in files: self.assertFileExists(file) - if file.suffix != ".html" and file.suffix != ".py": + if file.suffix not in {".html", ".py"}: self.assertModule(str(file), help=True) def test_github_install_official_non_exists_module(self): diff --git a/scripts/v.what.strds/v.what.strds.py b/scripts/v.what.strds/v.what.strds.py index aea70dbfb71..2bd6cd72131 100644 --- a/scripts/v.what.strds/v.what.strds.py +++ b/scripts/v.what.strds/v.what.strds.py @@ -115,7 +115,7 @@ def main(): _("Attribute table of vector {name} will be updated...").format(name=input) ) - if where == "" or where == " " or where == "\n": + if where in {"", " ", "\n"}: where = None overwrite = gs.overwrite() diff --git a/temporal/t.rast.to.rast3/t.rast.to.rast3.py b/temporal/t.rast.to.rast3/t.rast.to.rast3.py index 6e0ff8e8506..e3bdd9567ad 100755 --- a/temporal/t.rast.to.rast3/t.rast.to.rast3.py +++ b/temporal/t.rast.to.rast3/t.rast.to.rast3.py @@ -94,10 +94,10 @@ def main(): print("Gran from stds %0.15f" % (granularity)) - if unit == "years" or unit == "year": + if unit in {"years", "year"}: bottom = float(start.year - 1900) top = float(granularity * num_maps) - elif unit == "months" or unit == "month": + elif unit in {"months", "month"}: bottom = float((start.year - 1900) * 12 + start.month) top = float(granularity * num_maps) else: @@ -106,13 +106,13 @@ def main(): hours = 0.0 minutes = 0.0 seconds = 0.0 - if unit == "days" or unit == "day": + if unit in {"days", "day"}: days = float(granularity) - if unit == "hours" or unit == "hour": + if unit in {"hours", "hour"}: hours = float(granularity) - if unit == "minutes" or unit == "minute": + if unit in {"minutes", "minute"}: minutes = float(granularity) - if unit == "seconds" or unit == "second": + if unit in {"seconds", "second"}: seconds = float(granularity) granularity = float( diff --git a/temporal/t.vect.db.select/t.vect.db.select.py b/temporal/t.vect.db.select/t.vect.db.select.py index 96b76f8361b..d9cf9c12858 100755 --- a/temporal/t.vect.db.select/t.vect.db.select.py +++ b/temporal/t.vect.db.select/t.vect.db.select.py @@ -67,10 +67,10 @@ def main(): layer = options["layer"] separator = gs.separator(options["separator"]) - if where == "" or where == " " or where == "\n": + if where in {"", " ", "\n"}: where = None - if columns == "" or columns == " " or columns == "\n": + if columns in {"", " ", "\n"}: columns = None # Make sure the temporal database exists diff --git a/temporal/t.vect.observe.strds/t.vect.observe.strds.py b/temporal/t.vect.observe.strds/t.vect.observe.strds.py index a1d7fd724b3..8b3850fb2ef 100755 --- a/temporal/t.vect.observe.strds/t.vect.observe.strds.py +++ b/temporal/t.vect.observe.strds/t.vect.observe.strds.py @@ -92,7 +92,7 @@ def main(): where = options["where"] columns = options["columns"] - if where == "" or where == " " or where == "\n": + if where in {"", " ", "\n"}: where = None overwrite = gs.overwrite() diff --git a/temporal/t.vect.what.strds/t.vect.what.strds.py b/temporal/t.vect.what.strds/t.vect.what.strds.py index 226bc5c621f..5d29ea3b5fb 100755 --- a/temporal/t.vect.what.strds/t.vect.what.strds.py +++ b/temporal/t.vect.what.strds/t.vect.what.strds.py @@ -86,7 +86,7 @@ def main(): tempwhere = options["t_where"] sampling = options["sampling"] - if where == "" or where == " " or where == "\n": + if where in {"", " ", "\n"}: where = None # Make sure the temporal database exists From b01a67d91bd4f96eb31be3dcd68068d25e68406d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Sat, 13 Jul 2024 08:05:42 -0400 Subject: [PATCH 023/471] style: Fix unnecessary-assign (RET504) (#4043) --- gui/wxpython/core/utils.py | 3 +- gui/wxpython/core/workspace.py | 8 ++--- gui/wxpython/datacatalog/tree.py | 3 +- gui/wxpython/gcp/manager.py | 3 +- gui/wxpython/gmodeler/model.py | 7 ++--- gui/wxpython/gui_core/dialogs.py | 6 ++-- gui/wxpython/gui_core/forms.py | 3 +- gui/wxpython/gui_core/gselect.py | 3 +- gui/wxpython/gui_core/pyedit.py | 3 +- gui/wxpython/gui_core/simplelmgr.py | 12 +++---- gui/wxpython/gui_core/treeview.py | 6 ++-- gui/wxpython/gui_core/widgets.py | 6 +--- gui/wxpython/history/tree.py | 4 +-- gui/wxpython/iclass/digit.py | 3 +- gui/wxpython/iscatt/frame.py | 3 +- gui/wxpython/iscatt/iscatt_core.py | 8 ++--- gui/wxpython/iscatt/plots.py | 3 +- gui/wxpython/lmgr/frame.py | 3 +- gui/wxpython/lmgr/layertree.py | 3 +- gui/wxpython/location_wizard/wizard.py | 7 ++--- gui/wxpython/main_window/frame.py | 3 +- gui/wxpython/nviz/mapwindow.py | 4 +-- gui/wxpython/nviz/wxnviz.py | 4 +-- gui/wxpython/psmap/dialogs.py | 3 +- gui/wxpython/psmap/frame.py | 4 +-- gui/wxpython/psmap/instructions.py | 3 +- gui/wxpython/psmap/utils.py | 3 +- gui/wxpython/rlisetup/sampling_frame.py | 3 +- gui/wxpython/startup/guiutils.py | 3 +- gui/wxpython/startup/locdownload.py | 3 +- gui/wxpython/vnet/vnet_core.py | 3 +- gui/wxpython/vnet/vnet_data.py | 6 ++-- imagery/i.atcorr/create_iwave.py | 3 +- man/build_manual_gallery.py | 3 +- pyproject.toml | 8 ++++- python/grass/grassdb/checks.py | 4 +-- python/grass/grassdb/history.py | 6 ++-- python/grass/gunittest/reporters.py | 6 ++-- python/grass/imaging/images2gif.py | 3 +- python/grass/jupyter/utils.py | 3 +- python/grass/pydispatch/saferef.py | 3 +- python/grass/pygrass/modules/interface/env.py | 3 +- python/grass/pygrass/shell/show.py | 3 +- python/grass/pygrass/vector/geometry.py | 3 +- python/grass/script/core.py | 4 +-- python/grass/script/task.py | 10 ++---- python/grass/script/utils.py | 3 +- .../temporal/abstract_space_time_dataset.py | 5 +-- python/grass/temporal/mapcalc.py | 4 +-- python/grass/temporal/spatial_extent.py | 6 ++-- python/grass/temporal/temporal_algebra.py | 31 ++++++------------- .../temporal/temporal_raster_base_algebra.py | 11 ++----- .../grass/temporal/temporal_vector_algebra.py | 7 ++--- .../testsuite/test_r_semantic_label.py | 4 +-- scripts/v.import/v.import.py | 3 +- utils/md_isvalid.py | 4 +-- utils/mkhtml.py | 3 +- utils/test_generate_last_commit_file.py | 3 +- 58 files changed, 90 insertions(+), 195 deletions(-) diff --git a/gui/wxpython/core/utils.py b/gui/wxpython/core/utils.py index 66137f3c974..47977a1a27b 100644 --- a/gui/wxpython/core/utils.py +++ b/gui/wxpython/core/utils.py @@ -1179,8 +1179,7 @@ def unregisterPid(pid): def get_shell_pid(env=None): """Get shell PID from the GIS environment or None""" try: - shell_pid = int(grass.gisenv(env=env)["PID"]) - return shell_pid + return int(grass.gisenv(env=env)["PID"]) except (KeyError, ValueError) as error: Debug.msg( 1, "No PID for GRASS shell (assuming no shell running): {}".format(error) diff --git a/gui/wxpython/core/workspace.py b/gui/wxpython/core/workspace.py index 65a802d0809..7e5f85d6649 100644 --- a/gui/wxpython/core/workspace.py +++ b/gui/wxpython/core/workspace.py @@ -92,9 +92,7 @@ def __filterValue(self, value): :param value: """ value = value.replace("<", "<") - value = value.replace(">", ">") - - return value + return value.replace(">", ">") def __getNodeText(self, node, tag, default=""): """Get node text""" @@ -1043,9 +1041,7 @@ def __filterValue(self, value): """Make value XML-valid""" value = value.replace("<", "<") value = value.replace(">", ">") - value = value.replace("&", "&") - - return value + return value.replace("&", "&") def __writeLayer(self, mapTree, item): """Write bunch of layers to GRASS Workspace XML file""" diff --git a/gui/wxpython/datacatalog/tree.py b/gui/wxpython/datacatalog/tree.py index 085b9cb7f67..6d5530733d1 100644 --- a/gui/wxpython/datacatalog/tree.py +++ b/gui/wxpython/datacatalog/tree.py @@ -359,8 +359,7 @@ def _getValidSavedGrassDBs(self): dbs = UserSettings.Get( group="datacatalog", key="grassdbs", subkey="listAsString" ) - dbs = [db for db in dbs.split(",") if os.path.isdir(db)] - return dbs + return [db for db in dbs.split(",") if os.path.isdir(db)] def _saveGrassDBs(self): """Save current grass dbs in tree to settings""" diff --git a/gui/wxpython/gcp/manager.py b/gui/wxpython/gcp/manager.py index 87b18f8437d..692de174f55 100644 --- a/gui/wxpython/gcp/manager.py +++ b/gui/wxpython/gcp/manager.py @@ -1792,7 +1792,7 @@ def _getOverWriteDialog(self, maptype, overwrite): map_name = "<{}>".format(found["name"]) if found["name"] and not overwrite: - overwrite_dlg = wx.MessageDialog( + return wx.MessageDialog( self.GetParent(), message=_( "The {map_type} map {map_name} exists. " @@ -1804,7 +1804,6 @@ def _getOverWriteDialog(self, maptype, overwrite): caption=_("Overwrite?"), style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION, ) - return overwrite_dlg def OnGeorect(self, event): """ diff --git a/gui/wxpython/gmodeler/model.py b/gui/wxpython/gmodeler/model.py index 864f1df68e9..f0599b70d61 100644 --- a/gui/wxpython/gmodeler/model.py +++ b/gui/wxpython/gmodeler/model.py @@ -2027,9 +2027,7 @@ def _filterValue(self, value): :param value: """ value = value.replace("<", "<") - value = value.replace(">", ">") - - return value + return value.replace(">", ">") def _getNodeText(self, node, tag, default=""): """Get node text""" @@ -2342,8 +2340,7 @@ def _filterValue(self, value): :param value: string to be escaped as XML :return: a XML-valid string """ - value = saxutils.escape(value) - return value + return saxutils.escape(value) def _header(self): """Write header""" diff --git a/gui/wxpython/gui_core/dialogs.py b/gui/wxpython/gui_core/dialogs.py index 167a48ce6bb..9a19000e6e4 100644 --- a/gui/wxpython/gui_core/dialogs.py +++ b/gui/wxpython/gui_core/dialogs.py @@ -1208,8 +1208,7 @@ def _filter(self, data): """Apply filter for strings in data list""" flt_data = [] if len(self.flt_pattern) == 0: - flt_data = data[:] - return flt_data + return data[:] for dt in data: try: @@ -1872,8 +1871,7 @@ def __init__( def GetOpacity(self): """Button 'OK' pressed""" # return opacity value - opacity = float(self.value.GetValue()) / 100 - return opacity + return float(self.value.GetValue()) / 100 def OnApply(self, event): self.applyOpacity.emit(value=self.GetOpacity()) diff --git a/gui/wxpython/gui_core/forms.py b/gui/wxpython/gui_core/forms.py index 00fd505c799..1202d699eeb 100644 --- a/gui/wxpython/gui_core/forms.py +++ b/gui/wxpython/gui_core/forms.py @@ -3062,8 +3062,7 @@ def AddBitmapToImageList(self, section, imageList): image = wx.Image(iconSectionDict[section]).Scale( 16, 16, wx.IMAGE_QUALITY_HIGH ) - idx = imageList.Add(BitmapFromImage(image)) - return idx + return imageList.Add(BitmapFromImage(image)) return -1 diff --git a/gui/wxpython/gui_core/gselect.py b/gui/wxpython/gui_core/gselect.py index 6b679c074b5..dc553ae74bb 100644 --- a/gui/wxpython/gui_core/gselect.py +++ b/gui/wxpython/gui_core/gselect.py @@ -663,8 +663,7 @@ def AddItem(self, value, mapset=None, node=True, parent=None): data = {"node": node, "mapset": mapset} - item = self.seltree.AppendItem(parent, text=value, data=data) - return item + return self.seltree.AppendItem(parent, text=value, data=data) def OnKeyUp(self, event): """Enables to select items using keyboard diff --git a/gui/wxpython/gui_core/pyedit.py b/gui/wxpython/gui_core/pyedit.py index 7c56a7f7c07..573496cbbfc 100644 --- a/gui/wxpython/gui_core/pyedit.py +++ b/gui/wxpython/gui_core/pyedit.py @@ -301,8 +301,7 @@ def _openFile(self, file_path): """ try: with open(file_path, "r") as f: - content = f.read() - return content + return f.read() except PermissionError: GError( message=_( diff --git a/gui/wxpython/gui_core/simplelmgr.py b/gui/wxpython/gui_core/simplelmgr.py index d59a443c9b0..9f03a49396e 100644 --- a/gui/wxpython/gui_core/simplelmgr.py +++ b/gui/wxpython/gui_core/simplelmgr.py @@ -377,31 +377,27 @@ def GetOptData(self, dcmd, layer, params, propwin): def AddRaster(self, name, cmd, hidden, dialog): """Ads new raster layer.""" - layer = self._layerList.AddNewLayer( + return self._layerList.AddNewLayer( name=name, mapType="raster", active=True, cmd=cmd, hidden=hidden ) - return layer def AddRast3d(self, name, cmd, hidden, dialog): """Ads new raster3d layer.""" - layer = self._layerList.AddNewLayer( + return self._layerList.AddNewLayer( name=name, mapType="raster_3d", active=True, cmd=cmd, hidden=hidden ) - return layer def AddVector(self, name, cmd, hidden, dialog): """Ads new vector layer.""" - layer = self._layerList.AddNewLayer( + return self._layerList.AddNewLayer( name=name, mapType="vector", active=True, cmd=cmd, hidden=hidden ) - return layer def AddRGB(self, name, cmd, hidden, dialog): """Ads new vector layer.""" - layer = self._layerList.AddNewLayer( + return self._layerList.AddNewLayer( name=name, mapType="rgb", active=True, cmd=cmd, hidden=hidden ) - return layer def GetLayerInfo(self, layer, key): """Just for compatibility, should be removed in the future""" diff --git a/gui/wxpython/gui_core/treeview.py b/gui/wxpython/gui_core/treeview.py index 01bbe87d493..4c20885599c 100644 --- a/gui/wxpython/gui_core/treeview.py +++ b/gui/wxpython/gui_core/treeview.py @@ -91,8 +91,7 @@ def OnGetItemText(self, index, column=0): """ node = self._model.GetNodeByIndex(index) # remove & because of & needed in menu (&Files) - label = node.label.replace("&", "") - return label + return node.label.replace("&", "") def OnGetChildrenCount(self, index): """Overridden method necessary to communicate with tree model.""" @@ -258,8 +257,7 @@ def OnGetItemText(self, index, column=0): if column > 0: return node.data.get(self._columns[column], "") else: - label = node.label.replace("&", "") - return label + return node.label.replace("&", "") def OnRightClick(self, event): """Select item on right click. diff --git a/gui/wxpython/gui_core/widgets.py b/gui/wxpython/gui_core/widgets.py index 61225b8c21f..3196ff173db 100644 --- a/gui/wxpython/gui_core/widgets.py +++ b/gui/wxpython/gui_core/widgets.py @@ -1338,11 +1338,7 @@ def _searchModule(self, keys, value): nodes.sort(key=self._model.GetIndexOfNode) self._results = nodes self._resultIndex = -1 - commands = sorted( - [node.data["command"] for node in nodes if node.data["command"]] - ) - - return commands + return sorted([node.data["command"] for node in nodes if node.data["command"]]) def OnSelectModule(self, event=None): """Module selected from choice, update command prompt""" diff --git a/gui/wxpython/history/tree.py b/gui/wxpython/history/tree.py index 0e9d75870aa..8bccc3e97a7 100644 --- a/gui/wxpython/history/tree.py +++ b/gui/wxpython/history/tree.py @@ -215,12 +215,10 @@ def _timestampToDay(self, timestamp=None): return OLD_DATE timestamp_datetime = datetime.datetime.fromisoformat(timestamp) - day_midnight = datetime.datetime( + return datetime.datetime( timestamp_datetime.year, timestamp_datetime.month, timestamp_datetime.day ).date() - return day_midnight - def _initHistoryModel(self): """Fill tree history model based on the current history log.""" content_list = self.ReadFromHistory() diff --git a/gui/wxpython/iclass/digit.py b/gui/wxpython/iclass/digit.py index 691c1e75080..a6f0ef15948 100644 --- a/gui/wxpython/iclass/digit.py +++ b/gui/wxpython/iclass/digit.py @@ -126,8 +126,7 @@ def _getNewFeaturesLayer(self): return 1 def _getNewFeaturesCat(self): - cat = self.mapWindow.GetCurrentCategory() - return cat + return self.mapWindow.GetCurrentCategory() def DeleteAreasByCat(self, cats): """Delete areas (centroid+boundaries) by categories diff --git a/gui/wxpython/iscatt/frame.py b/gui/wxpython/iscatt/frame.py index bf5dfac91e4..6d87a90e273 100644 --- a/gui/wxpython/iscatt/frame.py +++ b/gui/wxpython/iscatt/frame.py @@ -330,8 +330,7 @@ def _newScatterPlotName(self, scatt_id): return name def _getScatterPlotName(self, i): - name = "scatter plot %d" % i - return name + return "scatter plot %d" % i def NewScatterPlot(self, scatt_id, transpose): # TODO needs to be resolved (should be in this class) diff --git a/gui/wxpython/iscatt/iscatt_core.py b/gui/wxpython/iscatt/iscatt_core.py index c663233a5e8..2eaa463940d 100644 --- a/gui/wxpython/iscatt/iscatt_core.py +++ b/gui/wxpython/iscatt/iscatt_core.py @@ -488,9 +488,7 @@ def GetBandsInfo(self, scatt_id): b1_info = self.an_data.GetBandInfo(b1) b2_info = self.an_data.GetBandInfo(b2) - bands_info = {"b1": b1_info, "b2": b2_info} - - return bands_info + return {"b1": b1_info, "b2": b2_info} def DeleScattPlot(self, cat_id, scatt_id): if cat_id not in self.cats: @@ -784,15 +782,13 @@ def idBandsToidScatt(band_1_id, band_2_id, n_bands): n_b1 = n_bands - 1 - scatt_id = int( + return int( (band_1_id * (2 * n_b1 + 1) - band_1_id * band_1_id) / 2 + band_2_id - band_1_id - 1 ) - return scatt_id - def GetRegion(): ret, region, msg = RunCommand("g.region", flags="gp", getErrorMsg=True, read=True) diff --git a/gui/wxpython/iscatt/plots.py b/gui/wxpython/iscatt/plots.py index ba951e0eec3..f9cad136314 100644 --- a/gui/wxpython/iscatt/plots.py +++ b/gui/wxpython/iscatt/plots.py @@ -711,8 +711,7 @@ def GetCoords(self): if self.empty_pol: return None - coords = deepcopy(self.pol.xy) - return coords + return deepcopy(self.pol.xy) def SetEmpty(self): self._setEmptyPol(True) diff --git a/gui/wxpython/lmgr/frame.py b/gui/wxpython/lmgr/frame.py index 0b5a07b8f07..ddb91789508 100644 --- a/gui/wxpython/lmgr/frame.py +++ b/gui/wxpython/lmgr/frame.py @@ -2336,13 +2336,12 @@ def MsgDisplayResolution(self, limitText=None): ) if limitText: message += "\n\n%s" % _(limitText) - dlg = wx.MessageDialog( + return wx.MessageDialog( parent=self, message=message, caption=_("Constrain map to region geometry?"), style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION | wx.CENTRE, ) - return dlg def _onMapsetWatchdog(self, map_path, map_dest): """Current mapset watchdog event handler diff --git a/gui/wxpython/lmgr/layertree.py b/gui/wxpython/lmgr/layertree.py index 3ef19ba46b1..e935af8ce5d 100644 --- a/gui/wxpython/lmgr/layertree.py +++ b/gui/wxpython/lmgr/layertree.py @@ -2428,7 +2428,7 @@ def _createCommandCtrl(self): height = 25 if sys.platform in {"win32", "darwin"}: height = 40 - ctrl = TextCtrl( + return TextCtrl( self, id=wx.ID_ANY, value="", @@ -2436,4 +2436,3 @@ def _createCommandCtrl(self): size=(self.GetSize()[0] - 100, height), style=wx.TE_PROCESS_ENTER | wx.TE_DONTWRAP, ) - return ctrl diff --git a/gui/wxpython/location_wizard/wizard.py b/gui/wxpython/location_wizard/wizard.py index 408371ad21c..41c84d4ca70 100644 --- a/gui/wxpython/location_wizard/wizard.py +++ b/gui/wxpython/location_wizard/wizard.py @@ -743,8 +743,7 @@ def GetSortImages(self): def OnGetItemText(self, item, col): """Get item text""" index = self.itemIndexMap[item] - s = str(self.itemDataMap[index][col]) - return s + return str(self.itemDataMap[index][col]) def OnGetItemImage(self, item): return -1 @@ -2795,9 +2794,7 @@ def CreateProj4String(self): for item in datumparams: proj4string = "%s +%s" % (proj4string, item) - proj4string = "%s +no_defs" % proj4string - - return proj4string + return "%s +no_defs" % proj4string def OnHelp(self, event): """'Help' button clicked""" diff --git a/gui/wxpython/main_window/frame.py b/gui/wxpython/main_window/frame.py index 57cf6efb868..6b9a9be640f 100644 --- a/gui/wxpython/main_window/frame.py +++ b/gui/wxpython/main_window/frame.py @@ -2430,13 +2430,12 @@ def MsgDisplayResolution(self, limitText=None): ) if limitText: message += "\n\n%s" % _(limitText) - dlg = wx.MessageDialog( + return wx.MessageDialog( parent=self, message=message, caption=_("Constrain map to region geometry?"), style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION | wx.CENTRE, ) - return dlg def _onMapsetWatchdog(self, map_path, map_dest): """Current mapset watchdog event handler diff --git a/gui/wxpython/nviz/mapwindow.py b/gui/wxpython/nviz/mapwindow.py index 3d282cc3c14..30a7682f6c7 100644 --- a/gui/wxpython/nviz/mapwindow.py +++ b/gui/wxpython/nviz/mapwindow.py @@ -243,7 +243,7 @@ def GetContentScaleFactor(self): def InitFly(self): """Initialize fly through dictionary""" - fly = { + return { "interval": 10, # interval for timerFly "value": [0, 0, 0], # calculated values for navigation "mode": 0, # fly through mode (0, 1) @@ -264,8 +264,6 @@ def InitFly(self): "flySpeedStep": 2, } - return fly - def OnTimerFly(self, event): """Fly event was emitted, move the scene""" if self.mouse["use"] != "fly": diff --git a/gui/wxpython/nviz/wxnviz.py b/gui/wxpython/nviz/wxnviz.py index f967d830d5d..8002d6f32d2 100644 --- a/gui/wxpython/nviz/wxnviz.py +++ b/gui/wxpython/nviz/wxnviz.py @@ -2212,9 +2212,7 @@ def Load(self): ] wx.EndBusyCursor() - id = Nviz_load_image(im, self.width, self.height, self.image.HasAlpha()) - - return id + return Nviz_load_image(im, self.width, self.height, self.image.HasAlpha()) def Draw(self): """Draw texture as an image""" diff --git a/gui/wxpython/psmap/dialogs.py b/gui/wxpython/psmap/dialogs.py index 6094c805e60..8ee3661d8b5 100644 --- a/gui/wxpython/psmap/dialogs.py +++ b/gui/wxpython/psmap/dialogs.py @@ -3200,8 +3200,7 @@ def getColsChoice(self, parent): else: cols = [] - choice = Choice(parent=parent, id=wx.ID_ANY, choices=cols) - return choice + return Choice(parent=parent, id=wx.ID_ANY, choices=cols) def update(self): # feature type diff --git a/gui/wxpython/psmap/frame.py b/gui/wxpython/psmap/frame.py index a03021acc2f..4f0a2220fd5 100644 --- a/gui/wxpython/psmap/frame.py +++ b/gui/wxpython/psmap/frame.py @@ -2613,9 +2613,7 @@ def ImageRect(self): iH = iH * self.currScale x = cW / 2 - iW / 2 y = cH / 2 - iH / 2 - imageRect = Rect(int(x), int(y), int(iW), int(iH)) - - return imageRect + return Rect(int(x), int(y), int(iW), int(iH)) def RedrawSelectBox(self, id): """Redraws select box when selected object changes its size""" diff --git a/gui/wxpython/psmap/instructions.py b/gui/wxpython/psmap/instructions.py index bca686898cb..bc10a1c0e46 100644 --- a/gui/wxpython/psmap/instructions.py +++ b/gui/wxpython/psmap/instructions.py @@ -1913,8 +1913,7 @@ def __init__(self, id, env): self.instruction = dict(self.defaultInstruction) def __str__(self): - instr = string.Template("raster $raster").substitute(self.instruction) - return instr + return string.Template("raster $raster").substitute(self.instruction) def Read(self, instruction, text): """Read instruction and save information""" diff --git a/gui/wxpython/psmap/utils.py b/gui/wxpython/psmap/utils.py index 3e60ad6c2b6..d54a45a52be 100644 --- a/gui/wxpython/psmap/utils.py +++ b/gui/wxpython/psmap/utils.py @@ -402,8 +402,7 @@ def getRasterType(map): map = "" file = gs.find_file(name=map, element="cell") if file.get("file"): - rasterType = gs.raster_info(map)["datatype"] - return rasterType + return gs.raster_info(map)["datatype"] else: return None diff --git a/gui/wxpython/rlisetup/sampling_frame.py b/gui/wxpython/rlisetup/sampling_frame.py index 2526d0537f8..dcd8398d049 100644 --- a/gui/wxpython/rlisetup/sampling_frame.py +++ b/gui/wxpython/rlisetup/sampling_frame.py @@ -385,8 +385,7 @@ def writeCircle(self, circle, rasterName): grass.use_temp_region() grass.run_command("g.region", zoom=rasterName) region = grass.region() - marea = MaskedArea(region, rasterName, circle.radius) - return marea + return MaskedArea(region, rasterName, circle.radius) def _rectangleDrawn(self): """When drawing finished, get region values""" diff --git a/gui/wxpython/startup/guiutils.py b/gui/wxpython/startup/guiutils.py index 8bf4debef66..d9f59696375 100644 --- a/gui/wxpython/startup/guiutils.py +++ b/gui/wxpython/startup/guiutils.py @@ -165,9 +165,8 @@ def create_location_interactively(guiparent, grassdb): gWizard = LocationWizard(parent=guiparent, grassdatabase=grassdb) if gWizard.location is None: - gWizard_output = (None, None, None) + return (None, None, None) # Returns Nones after Cancel - return gWizard_output if gWizard.georeffile: message = _("Do you want to import {} to the newly created project?").format( diff --git a/gui/wxpython/startup/locdownload.py b/gui/wxpython/startup/locdownload.py index 5a4eddbaf9d..4592103a0f9 100644 --- a/gui/wxpython/startup/locdownload.py +++ b/gui/wxpython/startup/locdownload.py @@ -121,8 +121,7 @@ def _get_heigth(self, string): n_lines = string.count("\n") attr = self.out.GetClassDefaultAttributes() font_size = attr.font.GetPointSize() - heigth = int((n_lines + 2) * font_size // 0.75) # 1 px = 0.75 pt - return heigth + return int((n_lines + 2) * font_size // 0.75) # 1 px = 0.75 pt def _resize(self, heigth=-1): """Resize widget heigth diff --git a/gui/wxpython/vnet/vnet_core.py b/gui/wxpython/vnet/vnet_core.py index deaf11ff670..17d85ef6ca4 100644 --- a/gui/wxpython/vnet/vnet_core.py +++ b/gui/wxpython/vnet/vnet_core.py @@ -1030,8 +1030,7 @@ def AddTmpMapAnalysisMsg(mapName, tmp_maps): # TODO "Temporary map %s already exists.\n" + "Do you want to continue in analysis and overwrite it?" ) % (mapName + "@" + grass.gisenv()["MAPSET"]) - tmpMap = tmp_maps.AddTmpVectMap(mapName, msg) - return tmpMap + return tmp_maps.AddTmpVectMap(mapName, msg) class SnappingNodes(wx.EvtHandler): diff --git a/gui/wxpython/vnet/vnet_data.py b/gui/wxpython/vnet/vnet_data.py index 9f2d11cf46a..e978a82314e 100644 --- a/gui/wxpython/vnet/vnet_data.py +++ b/gui/wxpython/vnet/vnet_data.py @@ -666,8 +666,7 @@ def _getInvalidParams(self, params): vectMaps = grass.list_grouped("vector")[mapSet] if not params["input"] or mapName not in vectMaps: - invParams = list(params.keys())[:] - return invParams + return list(params.keys())[:] # check arc/node layer layers = utils.GetVectorNumberOfLayers(params["input"]) @@ -1252,8 +1251,7 @@ def _parseValue(self, value, read=False): value[0] == "[" and value[-1] == "]" ): # TODO, possible wrong interpretation value = value[1:-1].split(",") - value = map(self._castValue, value) - return value + return map(self._castValue, value) if value == "True": value = True diff --git a/imagery/i.atcorr/create_iwave.py b/imagery/i.atcorr/create_iwave.py index b895d3d5125..243e7a22542 100644 --- a/imagery/i.atcorr/create_iwave.py +++ b/imagery/i.atcorr/create_iwave.py @@ -191,8 +191,7 @@ def pretty_print(filter_f): if i < len(filter_f): pstring += ", " # trim starting \n and trailing , - pstring = pstring.lstrip("\n").rstrip(", ") - return pstring + return pstring.lstrip("\n").rstrip(", ") def write_cpp(bands, values, sensor, folder): diff --git a/man/build_manual_gallery.py b/man/build_manual_gallery.py index 10fdb143989..414b36c8a85 100755 --- a/man/build_manual_gallery.py +++ b/man/build_manual_gallery.py @@ -123,8 +123,7 @@ def remove_module_name(string, module): string = string.replace(module.replace("wxGUI.", "g.gui."), "") string = string.replace(module.replace(".", "_"), "") # using _ string = string.replace(module.replace(".", ""), "") # using nothing - string = string.replace(module, "") # using original dots - return string + return string.replace(module, "") # using original dots def title_from_names(module_name, img_name): diff --git a/pyproject.toml b/pyproject.toml index ab792fe2ac6..2ca0e33f163 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -237,7 +237,13 @@ ignore = [ "PTH202", # os-path-getsize "PTH204", # os-path-getmtime "PTH207", # glob - "RET50", # flake8-return (RET) + "RET501", # unnecessary-return-none + "RET502", # implicit-return-value + "RET503", # implicit-return + "RET505", # superfluous-else-return + "RET506", # superfluous-else-raise + "RET507", # superfluous-else-continue + "RET508", # superfluous-else-break "RSE102", # unnecessary-paren-on-raise-exception "RUF002", # ambiguous-unicode-character-docstring "RUF003", # ambiguous-unicode-character-comment diff --git a/python/grass/grassdb/checks.py b/python/grass/grassdb/checks.py index c1ef7cc76c2..b8748de9876 100644 --- a/python/grass/grassdb/checks.py +++ b/python/grass/grassdb/checks.py @@ -605,9 +605,7 @@ def get_reasons_grassdb_not_removable(grassdb): locations = [] for g_location in g_locations: locations.append((grassdb, g_location)) - messages = get_reasons_locations_not_removable(locations) - - return messages + return get_reasons_locations_not_removable(locations) def get_list_of_locations(dbase): diff --git a/python/grass/grassdb/history.py b/python/grass/grassdb/history.py index ec7b1dc14eb..ba9ed818f36 100644 --- a/python/grass/grassdb/history.py +++ b/python/grass/grassdb/history.py @@ -53,8 +53,7 @@ def get_history_file_extension(history_path): :return str extension: None (plain text) or .json """ file_path = Path(history_path) - extension = file_path.suffix - return extension + return file_path.suffix def ensure_history_file(history_path): @@ -275,14 +274,13 @@ def get_initial_command_info(env_run): region_settings = gs.region(env=env_run) # Finalize the command info dictionary - cmd_info = { + return { "timestamp": exec_time, "mask2d": mask2d_present, "mask3d": mask3d_present, "region": region_settings, "status": Status.RUNNING.value, } - return cmd_info def _add_entry_to_JSON(history_path, entry): diff --git a/python/grass/gunittest/reporters.py b/python/grass/gunittest/reporters.py index e5455c33b39..63bffc655e1 100644 --- a/python/grass/gunittest/reporters.py +++ b/python/grass/gunittest/reporters.py @@ -295,7 +295,7 @@ def get_html_test_authors_table(directory, tests_authors): if not not_testing_authors: not_testing_authors = ["all recent authors contributed tests"] - test_authors = ( + return ( "

Code and test authors

" '

' "Note that determination of authors is approximate and only" @@ -311,7 +311,6 @@ def get_html_test_authors_table(directory, tests_authors): not_testing=", ".join(sorted(not_testing_authors)), ) ) - return test_authors class GrassTestFilesMultiReporter: @@ -1212,7 +1211,7 @@ def report_for_dir(self, root, directory, test_files): page.close() status = success_to_html_text(total=file_total, successes=file_successes) - row = ( + return ( "

" '' "" @@ -1231,7 +1230,6 @@ def report_for_dir(self, root, directory, test_files): ptests=dir_pass_per, ) ) - return row def report_for_dirs(self, root, directories): # TODO: this will need changes according to potential changes in diff --git a/python/grass/imaging/images2gif.py b/python/grass/imaging/images2gif.py index b80f452cf8c..ba681dca77d 100644 --- a/python/grass/imaging/images2gif.py +++ b/python/grass/imaging/images2gif.py @@ -1105,8 +1105,7 @@ def convert(self, *color): def inxsearch(self, r, g, b): """Search for BGR values 0..255 and return colour index""" dists = self.colormap[:, :3] - np.array([r, g, b]) - a = np.argmin((dists * dists).sum(1)) - return a + return np.argmin((dists * dists).sum(1)) if __name__ == "__main__": diff --git a/python/grass/jupyter/utils.py b/python/grass/jupyter/utils.py index f06adfa36d1..4d76b166361 100644 --- a/python/grass/jupyter/utils.py +++ b/python/grass/jupyter/utils.py @@ -113,8 +113,7 @@ def estimate_resolution(raster, mapset, location, dbase, env): output = gs.parse_key_val(output, val_type=float) cell_ns = (output["n"] - output["s"]) / output["rows"] cell_ew = (output["e"] - output["w"]) / output["cols"] - estimate = (cell_ew + cell_ns) / 2.0 - return estimate + return (cell_ew + cell_ns) / 2.0 def setup_location(name, path, epsg, src_env): diff --git a/python/grass/pydispatch/saferef.py b/python/grass/pydispatch/saferef.py index 6a65f262714..43be1175b19 100644 --- a/python/grass/pydispatch/saferef.py +++ b/python/grass/pydispatch/saferef.py @@ -28,8 +28,7 @@ def safeRef(target, onDelete=None): """but no %s, don't know how """ """to create reference""" % (target, im_self, im_func) ) - reference = BoundMethodWeakref(target=target, onDelete=onDelete) - return reference + return BoundMethodWeakref(target=target, onDelete=onDelete) if onDelete is not None: return weakref.ref(target, onDelete) else: diff --git a/python/grass/pygrass/modules/interface/env.py b/python/grass/pygrass/modules/interface/env.py index 4fbef04dae3..ad6e4c4800e 100644 --- a/python/grass/pygrass/modules/interface/env.py +++ b/python/grass/pygrass/modules/interface/env.py @@ -14,13 +14,12 @@ def get_env(): if gisrc is None: raise RuntimeError("You are not in a GRASS session, GISRC not found.") with open(gisrc, mode="r") as grc: - env = dict( + return dict( [ (k.strip(), v.strip()) for k, v in [row.split(":", 1) for row in grc if row] ] ) - return env def get_debug_level(): diff --git a/python/grass/pygrass/shell/show.py b/python/grass/pygrass/shell/show.py index 94b0807debe..20b721e726b 100644 --- a/python/grass/pygrass/shell/show.py +++ b/python/grass/pygrass/shell/show.py @@ -7,5 +7,4 @@ def raw_figure(figpath): with open(figpath, mode="rb") as data: - res = data.read() - return res + return data.read() diff --git a/python/grass/pygrass/vector/geometry.py b/python/grass/pygrass/vector/geometry.py index 5b39d547bec..6ddbd70db7d 100644 --- a/python/grass/pygrass/vector/geometry.py +++ b/python/grass/pygrass/vector/geometry.py @@ -1380,8 +1380,7 @@ def _centroid(self, side, idonly=False): if idonly: return v_id else: - cntr = Centroid(v_id=v_id, c_mapinfo=self.c_mapinfo) - return cntr + return Centroid(v_id=v_id, c_mapinfo=self.c_mapinfo) def left_centroid(self, idonly=False): """Return left centroid diff --git a/python/grass/script/core.py b/python/grass/script/core.py index 5458ea1cb78..b22815bafb6 100644 --- a/python/grass/script/core.py +++ b/python/grass/script/core.py @@ -983,9 +983,7 @@ def tempname(length, lowercase=False): if not lowercase: chars += string.ascii_uppercase random_part = "".join(random.choice(chars) for _ in range(length)) - randomname = "tmp_" + random_part - - return randomname + return "tmp_" + random_part def _compare_projection(dic): diff --git a/python/grass/script/task.py b/python/grass/script/task.py index 3dfe8049841..9ab76eedcb8 100644 --- a/python/grass/script/task.py +++ b/python/grass/script/task.py @@ -429,8 +429,7 @@ def _get_node_text(self, node, tag, default=""): """Get node text""" p = node.find(tag) if p is not None: - res = " ".join(p.text.split()) - return res + return " ".join(p.text.split()) return default @@ -453,9 +452,7 @@ def convert_xml_to_utf8(xml_text): # modify: change the encoding to "utf-8", for correct parsing xml_text_utf8 = xml_text.decode(enc.decode("ascii")).encode("utf-8") p = re.compile(b'encoding="' + enc + b'"', re.IGNORECASE) - xml_text_utf8 = p.sub(b'encoding="utf-8"', xml_text_utf8) - - return xml_text_utf8 + return p.sub(b'encoding="utf-8"', xml_text_utf8) def get_interface_description(cmd): @@ -508,13 +505,12 @@ def get_interface_description(cmd): ) desc = convert_xml_to_utf8(cmdout) - desc = desc.replace( + return desc.replace( b"grass-interface.dtd", os.path.join(os.getenv("GISBASE"), "gui", "xml", "grass-interface.dtd").encode( "utf-8" ), ) - return desc def parse_interface(name, parser=processTask, blackList=None): diff --git a/python/grass/script/utils.py b/python/grass/script/utils.py index 0269ff15d2d..13d049412a4 100644 --- a/python/grass/script/utils.py +++ b/python/grass/script/utils.py @@ -91,8 +91,7 @@ def diff_files(filename_a, filename_b): differ = difflib.Differ() fh_a = open(filename_a, "r") fh_b = open(filename_b, "r") - result = list(differ.compare(fh_a.readlines(), fh_b.readlines())) - return result + return list(differ.compare(fh_a.readlines(), fh_b.readlines())) def try_remove(path): diff --git a/python/grass/temporal/abstract_space_time_dataset.py b/python/grass/temporal/abstract_space_time_dataset.py index 7f8209d8072..6376261cf72 100644 --- a/python/grass/temporal/abstract_space_time_dataset.py +++ b/python/grass/temporal/abstract_space_time_dataset.py @@ -98,10 +98,7 @@ def create_map_register_name(self): uuid_rand = str(uuid.uuid4()).replace("-", "") - table_name = ( - self.get_new_map_instance(None).get_type() + "_map_register_" + uuid_rand - ) - return table_name + return self.get_new_map_instance(None).get_type() + "_map_register_" + uuid_rand @abstractmethod def get_new_map_instance(self, ident=None): diff --git a/python/grass/temporal/mapcalc.py b/python/grass/temporal/mapcalc.py index 1d098f33647..5f29cd32747 100644 --- a/python/grass/temporal/mapcalc.py +++ b/python/grass/temporal/mapcalc.py @@ -481,9 +481,7 @@ def _operator_parser(expr, first, current): expr = _parse_start_time_operator(expr, is_time_absolute, first, current) expr = _parse_end_time_operator(expr, is_time_absolute, first, current) expr = _parse_start_operators(expr, is_time_absolute, current) - expr = _parse_end_operators(expr, is_time_absolute, current) - - return expr + return _parse_end_operators(expr, is_time_absolute, current) ############################################################################### diff --git a/python/grass/temporal/spatial_extent.py b/python/grass/temporal/spatial_extent.py index b30aaa799af..3bf67b3d533 100644 --- a/python/grass/temporal/spatial_extent.py +++ b/python/grass/temporal/spatial_extent.py @@ -294,7 +294,7 @@ def intersect_2d(self, extent): if S < eS: nS = eS - new = SpatialExtent( + return SpatialExtent( north=nN, south=nS, east=nE, @@ -303,7 +303,6 @@ def intersect_2d(self, extent): bottom=0, proj=self.get_projection(), ) - return new def intersect(self, extent): """Return the three dimensional intersection as spatial_extent @@ -459,7 +458,7 @@ def disjoint_union_2d(self, extent): if S > eS: nS = eS - new = SpatialExtent( + return SpatialExtent( north=nN, south=nS, east=nE, @@ -468,7 +467,6 @@ def disjoint_union_2d(self, extent): bottom=0, proj=self.get_projection(), ) - return new def union(self, extent): """Return the three dimensional union as spatial_extent diff --git a/python/grass/temporal/temporal_algebra.py b/python/grass/temporal/temporal_algebra.py index 66a24a3a154..fefb4e00f60 100644 --- a/python/grass/temporal/temporal_algebra.py +++ b/python/grass/temporal/temporal_algebra.py @@ -1179,8 +1179,7 @@ def set_temporal_extent_list(self, maplist, topolist=["EQUAL"], temporal="l"): # resultlist.append(map_new) # Get sorted map objects as values from result dictionary. resultlist = resultdict.values() - resultlist = sorted(resultlist, key=AbstractDatasetComparisonKeyStartTime) - return resultlist + return sorted(resultlist, key=AbstractDatasetComparisonKeyStartTime) def remove_maps(self): """Removes empty or intermediate maps of different type.""" @@ -1641,9 +1640,7 @@ def build_spatio_temporal_topology_list( resultlist = resultdict.values() # Sort list of maps chronological. - resultlist = sorted(resultlist, key=AbstractDatasetComparisonKeyStartTime) - - return resultlist + return sorted(resultlist, key=AbstractDatasetComparisonKeyStartTime) def assign_bool_value( self, map_i, temporal_topo_list=["EQUAL"], spatial_topo_list=[] @@ -1871,8 +1868,7 @@ def perform_temporal_selection( # map_i.condition_value.append(False) # Sort list of maps chronological. - resultlist = sorted(resultlist, key=AbstractDatasetComparisonKeyStartTime) - return resultlist + return sorted(resultlist, key=AbstractDatasetComparisonKeyStartTime) def set_granularity(self, maplistA, maplistB, toperator="l", topolist=["EQUAL"]): """This function sets the temporal extends of a list of maps based on @@ -2006,13 +2002,11 @@ def set_granularity(self, maplistA, maplistB, toperator="l", topolist=["EQUAL"]) resultlist = resultdict.values() # Sort list of maps chronological. - resultlist = sorted(resultlist, key=AbstractDatasetComparisonKeyStartTime) # Get relations to maplistB per map in A. # Loop over all relations from list # temporal extent = map.temporal_intersection(map) # if temporal extend is None = delete map. - - return resultlist + return sorted(resultlist, key=AbstractDatasetComparisonKeyStartTime) def get_temporal_func_dict(self, map): """This function creates a dictionary containing temporal functions for a @@ -2196,18 +2190,15 @@ def eval_map_list(self, maplist, thenlist, topolist=["EQUAL"]): """ # Get topology of then statement map list in relation to the other maplist # and assign boolean values of the maplist to the thenlist. - containlist = self.perform_temporal_selection( - thenlist, maplist, assign_val=True, topolist=topolist - ) # Inverse selection of maps from thenlist and assigning False values. # excludelist = self.perform_temporal_selection(thenlist, maplist, # assign_val = True, # inverse = True, # topolist = topolist) # Combining the selection and inverse selection list. - resultlist = containlist # + excludelist - - return resultlist + return self.perform_temporal_selection( + thenlist, maplist, assign_val=True, topolist=topolist + ) def build_condition_list(self, tvarexpr, thenlist, topolist=["EQUAL"]): """This function evaluates temporal variable expressions of a conditional @@ -2285,9 +2276,7 @@ def build_condition_list(self, tvarexpr, thenlist, topolist=["EQUAL"]): resultlist = self.eval_global_var(expr, thenlist) # Sort resulting list of maps chronological. - resultlist = sorted(resultlist, key=AbstractDatasetComparisonKeyStartTime) - - return resultlist + return sorted(resultlist, key=AbstractDatasetComparisonKeyStartTime) def eval_condition_list(self, maplist, inverse=False): """This function evaluates conditional values of a map list. @@ -2336,9 +2325,7 @@ def recurse_compare(conditionlist): conditionlist[ele_index - 2] = result recurse_compare(conditionlist) - resultlist = conditionlist - - return resultlist + return conditionlist resultlist = [] inverselist = [] diff --git a/python/grass/temporal/temporal_raster_base_algebra.py b/python/grass/temporal/temporal_raster_base_algebra.py index f9333d79044..4ca921adaaf 100644 --- a/python/grass/temporal/temporal_raster_base_algebra.py +++ b/python/grass/temporal/temporal_raster_base_algebra.py @@ -354,9 +354,7 @@ def build_spatio_temporal_topology_list( resultlist = resultdict.values() # Sort list of maps chronological. - resultlist = sorted(resultlist, key=AbstractDatasetComparisonKeyStartTime) - - return resultlist + return sorted(resultlist, key=AbstractDatasetComparisonKeyStartTime) def build_command_string(self, map_i, relmap, operator=None, cmd_type=None): """This function build the r.mapcalc command string for conditionals, @@ -636,9 +634,7 @@ def set_temporal_extent_list( # resultlist.append(map_new) # Get sorted map objects as values from result dictionary. resultlist = resultdict.values() - resultlist = sorted(resultlist, key=AbstractDatasetComparisonKeyStartTime) - - return resultlist + return sorted(resultlist, key=AbstractDatasetComparisonKeyStartTime) def build_condition_cmd_list( self, @@ -739,14 +735,13 @@ def build_condition_cmd_list( conditiontopolist = self.build_spatio_temporal_topology_list( iflist, conclusionlist, topolist=condition_topolist ) - resultlist = self.set_temporal_extent_list( + return self.set_temporal_extent_list( conditiontopolist, topolist=condition_topolist, temporal="r", cmd_bool=True, cmd_type="condition", ) - return resultlist def p_statement_assign(self, t): # This function executes the processing of raster/raster3d algebra diff --git a/python/grass/temporal/temporal_vector_algebra.py b/python/grass/temporal/temporal_vector_algebra.py index 7afc9978df6..d8e8474f26e 100644 --- a/python/grass/temporal/temporal_vector_algebra.py +++ b/python/grass/temporal/temporal_vector_algebra.py @@ -291,9 +291,7 @@ def build_spatio_temporal_topology_list( resultlist = resultdict.values() # Sort list of maps chronological. - resultlist = sorted(resultlist, key=AbstractDatasetComparisonKeyStartTime) - - return resultlist + return sorted(resultlist, key=AbstractDatasetComparisonKeyStartTime) def overlay_cmd_value(self, map_i, tbrelations, function, topolist=["EQUAL"]): """Function to evaluate two map lists by given overlay operator. @@ -411,8 +409,7 @@ def set_temporal_extent_list(self, maplist, topolist=["EQUAL"], temporal="l"): # resultlist.append(map_new) # Get sorted map objects as values from result dictionary. resultlist = resultdict.values() - resultlist = sorted(resultlist, key=AbstractDatasetComparisonKeyStartTime) - return resultlist + return sorted(resultlist, key=AbstractDatasetComparisonKeyStartTime) def p_statement_assign(self, t): # The expression should always return a list of maps. diff --git a/scripts/r.semantic.label/testsuite/test_r_semantic_label.py b/scripts/r.semantic.label/testsuite/test_r_semantic_label.py index 04be5d5b7bf..b7456e22633 100644 --- a/scripts/r.semantic.label/testsuite/test_r_semantic_label.py +++ b/scripts/r.semantic.label/testsuite/test_r_semantic_label.py @@ -24,9 +24,7 @@ def tearDownClass(cls): def read_semantic_label(self): with RasterRow(self.map) as rast: - semantic_label = rast.info.semantic_label - - return semantic_label + return rast.info.semantic_label def test_semantic_label_assign_not_current_mapset(self): if not self.mapset == "PERMANENT": diff --git a/scripts/v.import/v.import.py b/scripts/v.import/v.import.py index 9dfe7b2149a..42e5952c76f 100755 --- a/scripts/v.import/v.import.py +++ b/scripts/v.import/v.import.py @@ -125,8 +125,7 @@ def cleanup(): def gdal_version(): """Returns the GDAL version as tuple""" - version = gs.parse_command("g.version", flags="reg")["gdal"] - return version + return gs.parse_command("g.version", flags="reg")["gdal"] def GDAL_COMPUTE_VERSION(maj, min, rev): diff --git a/utils/md_isvalid.py b/utils/md_isvalid.py index d7b2fbe6296..8b963bb953b 100644 --- a/utils/md_isvalid.py +++ b/utils/md_isvalid.py @@ -40,9 +40,7 @@ def check_module(module): ) p.wait() - returncode = p.returncode - - return returncode + return p.returncode if __name__ == "__main__": diff --git a/utils/mkhtml.py b/utils/mkhtml.py index 69a1ba1db76..c5a7519872d 100644 --- a/utils/mkhtml.py +++ b/utils/mkhtml.py @@ -514,8 +514,7 @@ def get_last_git_commit(src_dir, addon_path, is_addon): def read_file(name): try: with open(name) as f: - s = f.read() - return s + return f.read() except OSError: return "" diff --git a/utils/test_generate_last_commit_file.py b/utils/test_generate_last_commit_file.py index 3183b786c78..e6218b9cc4f 100644 --- a/utils/test_generate_last_commit_file.py +++ b/utils/test_generate_last_commit_file.py @@ -28,8 +28,7 @@ @pytest.fixture def json_file(): - file_name = "core_modules_with_last_commit.json" - return file_name + return "core_modules_with_last_commit.json" @pytest.fixture From d690d0c3da772c274ac483a4ccde6b9d1404eb43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Sat, 13 Jul 2024 10:11:12 -0400 Subject: [PATCH 024/471] style: Fix yoda-conditions (SIM300) (#4044) --- gui/wxpython/core/gcmd.py | 2 +- gui/wxpython/core/utils.py | 2 +- gui/wxpython/iclass/frame.py | 13 ++- gui/wxpython/lmgr/layertree.py | 2 +- gui/wxpython/web_services/widgets.py | 2 +- pyproject.toml | 1 - python/grass/temporal/spatial_extent.py | 144 ++++++++++++------------ scripts/g.extension/g.extension.py | 2 +- 8 files changed, 84 insertions(+), 84 deletions(-) diff --git a/gui/wxpython/core/gcmd.py b/gui/wxpython/core/gcmd.py index 5d845193938..7d4ed678d79 100644 --- a/gui/wxpython/core/gcmd.py +++ b/gui/wxpython/core/gcmd.py @@ -201,7 +201,7 @@ def kill(self): import win32api handle = win32api.OpenProcess(1, 0, self.pid) - return 0 != win32api.TerminateProcess(handle, 0) + return win32api.TerminateProcess(handle, 0) != 0 else: try: os.kill(-self.pid, signal.SIGTERM) # kill whole group diff --git a/gui/wxpython/core/utils.py b/gui/wxpython/core/utils.py index 47977a1a27b..5e52fc0532d 100644 --- a/gui/wxpython/core/utils.py +++ b/gui/wxpython/core/utils.py @@ -100,7 +100,7 @@ def GetLayerNameFromCmd(dcmd, fullyQualified=False, param=None, layerType=None): if len(dcmd) < 1: return mapname, False - if "d.grid" == dcmd[0]: + if dcmd[0] == "d.grid": mapname = "grid" elif "d.geodesic" in dcmd[0]: mapname = "geodesic" diff --git a/gui/wxpython/iclass/frame.py b/gui/wxpython/iclass/frame.py index 3a8626a76ee..14c44aba5ea 100644 --- a/gui/wxpython/iclass/frame.py +++ b/gui/wxpython/iclass/frame.py @@ -857,8 +857,9 @@ def ExportAreas(self, vectorName, withTable): % {"band": i + 1, "stat": statistic, "format": format} ) - if 0 != RunCommand( - "v.db.addtable", map=vectorName, columns=columns, parent=self + if ( + RunCommand("v.db.addtable", map=vectorName, columns=columns, parent=self) + != 0 ): wx.EndBusyCursor() return False @@ -1305,10 +1306,10 @@ def CheckInput(self, group, vector): rasterInfo = gs.raster_info(groupLayers[0]) if ( - regionBox.N > rasterInfo["north"] - or regionBox.S < rasterInfo["south"] - or regionBox.E > rasterInfo["east"] - or regionBox.W < rasterInfo["west"] + rasterInfo["north"] < regionBox.N + or rasterInfo["south"] > regionBox.S + or rasterInfo["east"] < regionBox.E + or rasterInfo["west"] > regionBox.W ): GMessage( parent=self, diff --git a/gui/wxpython/lmgr/layertree.py b/gui/wxpython/lmgr/layertree.py index e935af8ce5d..38751e35b55 100644 --- a/gui/wxpython/lmgr/layertree.py +++ b/gui/wxpython/lmgr/layertree.py @@ -1093,7 +1093,7 @@ def OnCopyMap(self, event): return kwargs = {key: "%s,%s" % (lnameSrc, lnameDst)} - if 0 != RunCommand("g.copy", overwrite=True, **kwargs): + if RunCommand("g.copy", overwrite=True, **kwargs) != 0: GError(_("Unable to make copy of <%s>") % lnameSrc, parent=self) return diff --git a/gui/wxpython/web_services/widgets.py b/gui/wxpython/web_services/widgets.py index 811b718a012..200d5521b70 100644 --- a/gui/wxpython/web_services/widgets.py +++ b/gui/wxpython/web_services/widgets.py @@ -662,7 +662,7 @@ def UpdateWidgetsByCmd(self, cmd): if "bgcolor" in dcmd and self.params["bgcolor"]: bgcolor = dcmd["bgcolor"].strip().lower() - if len(bgcolor) == 8 and "0x" == bgcolor[:2]: + if len(bgcolor) == 8 and bgcolor[:2] == "0x": colour = "#" + bgcolor[2:] self.params["bgcolor"].SetColour(colour) diff --git a/pyproject.toml b/pyproject.toml index 2ca0e33f163..50e9f651b0f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -290,7 +290,6 @@ ignore = [ "SIM118", # in-dict-keys "SIM201", # negate-equal-op "SIM223", # expr-and-false - "SIM300", # yoda-conditions "SIM401", # if-else-block-instead-of-dict-get "SLF001", # private-member-access "TRY002", # raise-vanilla-class diff --git a/python/grass/temporal/spatial_extent.py b/python/grass/temporal/spatial_extent.py index 3bf67b3d533..6c025fb7a40 100644 --- a/python/grass/temporal/spatial_extent.py +++ b/python/grass/temporal/spatial_extent.py @@ -183,11 +183,11 @@ def overlapping_2d(self, extent): # Adjust the east and west in case of LL projection if self.get_projection() == "LL": - while E < self.get_west(): + while self.get_west() > E: E += 360.0 W += 360.0 - while W > self.get_east(): + while self.get_east() < W: E -= 360.0 W -= 360.0 @@ -285,13 +285,13 @@ def intersect_2d(self, extent): nE = E nW = W - if W < eW: + if eW > W: nW = eW - if E > eE: + if eE < E: nE = eE - if N > eN: + if eN < N: nN = eN - if S < eS: + if eS > S: nS = eS return SpatialExtent( @@ -395,9 +395,9 @@ def intersect(self, extent): nT = T nB = B - if B < eB: + if eB > B: nB = eB - if T > eT: + if eT < T: nT = eT new.set_top(nT) @@ -449,13 +449,13 @@ def disjoint_union_2d(self, extent): nE = E nW = W - if W > eW: + if eW < W: nW = eW - if E < eE: + if eE > E: nE = eE - if N < eN: + if eN > N: nN = eN - if S > eS: + if eS < S: nS = eS return SpatialExtent( @@ -582,9 +582,9 @@ def disjoint_union(self, extent): nT = T nB = B - if B > eB: + if eB < B: nB = eB - if T < eT: + if eT > T: nT = eT new.set_top(nT) @@ -636,13 +636,13 @@ def is_in_2d(self, extent): eE -= 360.0 eW -= 360.0 - if W <= eW: + if eW >= W: return False - if E >= eE: + if eE <= E: return False - if N >= eN: + if eN <= N: return False - if S <= eS: + if eS >= S: return False return True @@ -678,9 +678,9 @@ def is_in(self, extent): T = self.get_top() B = self.get_bottom() - if B <= eB: + if eB >= B: return False - if T >= eT: + if eT <= T: return False return True @@ -776,13 +776,13 @@ def equivalent_2d(self, extent): eE -= 360.0 eW -= 360.0 - if W != eW: + if eW != W: return False - if E != eE: + if eE != E: return False - if N != eN: + if eN != N: return False - if S != eS: + if eS != S: return False return True @@ -819,9 +819,9 @@ def equivalent(self, extent): T = self.get_top() B = self.get_bottom() - if B != eB: + if eB != B: return False - if T != eT: + if eT != T: return False return True @@ -892,28 +892,28 @@ def cover_2d(self, extent): eW -= 360.0 # Edges of extent located outside of self are not allowed - if E <= eW: + if eW >= E: return False - if W >= eE: + if eE <= W: return False - if N <= eS: + if eS >= N: return False - if S >= eN: + if eN <= S: return False # First we check that at least one edge of extent meets an edge of self - if W != eW and E != eE and N != eN and S != eS: + if eW != W and eE != E and eN != N and eS != S: return False # We check that at least one edge of extent is located in self edge_count = 0 - if W < eW and E > eW: + if eW > W and eW < E: edge_count += 1 - if E > eE and W < eE: + if eE < E and eE > W: edge_count += 1 - if N > eN and S < eN: + if eN < N and eN > S: edge_count += 1 - if S < eS and N > eS: + if eS > S and eS < N: edge_count += 1 if edge_count == 0: @@ -974,40 +974,40 @@ def cover(self, extent): eW -= 360.0 # Edges of extent located outside of self are not allowed - if E <= eW: + if eW >= E: return False - if W >= eE: + if eE <= W: return False - if N <= eS: + if eS >= N: return False - if S >= eN: + if eN <= S: return False - if T <= eB: + if eB >= T: return False - if B >= eT: + if eT <= B: return False # First we check that at least one edge of extent meets an edge of self - if W != eW and E != eE and N != eN and S != eS and B != eB and T != eT: + if eW != W and eE != E and eN != N and eS != S and eB != B and eT != T: return False # We check that at least one edge of extent is located in self edge_count = 0 - if W < eW and E > eW: + if eW > W and eW < E: edge_count += 1 - if E > eE and W < eE: + if eE < E and eE > W: edge_count += 1 - if N > eN and S < eN: + if eN < N and eN > S: edge_count += 1 - if S < eS and N > eS: + if eS > S and eS < N: edge_count += 1 - if N > eN and S < eN: + if eN < N and eN > S: edge_count += 1 - if S < eS and N > eS: + if eS > S and eS < N: edge_count += 1 - if T > eT and B < eT: + if eT < T and eT > B: edge_count += 1 - if B < eB and T > eB: + if eB > B and eB < T: edge_count += 1 if edge_count == 0: @@ -1095,11 +1095,11 @@ def overlap_2d(self, extent): # Adjust the east and west in case of LL projection if self.get_projection() == "LL": - while E < self.get_west(): + while self.get_west() > E: E += 360.0 W += 360.0 - while W > self.get_east(): + while self.get_east() < W: E -= 360.0 W -= 360.0 @@ -1157,11 +1157,11 @@ def overlap(self, extent): # Adjust the east and west in case of LL projection if self.get_projection() == "LL": - while E < self.get_west(): + while self.get_west() > E: E += 360.0 W += 360.0 - while W > self.get_east(): + while self.get_east() < W: E -= 360.0 W -= 360.0 @@ -1242,16 +1242,16 @@ def meet_2d(self, extent): edge = None edge_count = 0 - if E == eW: + if eW == E: edge = "E" edge_count += 1 - if W == eE: + if eE == W: edge = "W" edge_count += 1 - if N == eS: + if eS == N: edge = "N" edge_count += 1 - if S == eN: + if eN == S: edge = "S" edge_count += 1 @@ -1261,11 +1261,11 @@ def meet_2d(self, extent): # Check boundaries of the faces if edge in {"E", "W"}: - if N < eS or S > eN: + if eS > N or eN < S: return False if edge in {"N", "S"}: - if E < eW or W > eE: + if eW > E or eE < W: return False return True @@ -1306,22 +1306,22 @@ def meet(self, extent): edge = None edge_count = 0 - if E == eW: + if eW == E: edge = "E" edge_count += 1 - if W == eE: + if eE == W: edge = "W" edge_count += 1 - if N == eS: + if eS == N: edge = "N" edge_count += 1 - if S == eN: + if eN == S: edge = "S" edge_count += 1 - if T == eB: + if eB == T: edge = "T" edge_count += 1 - if B == eT: + if eT == B: edge = "B" edge_count += 1 @@ -1331,21 +1331,21 @@ def meet(self, extent): # Check boundaries of the faces if edge in {"E", "W"}: - if N < eS or S > eN: + if eS > N or eN < S: return False - if T < eB or B > eT: + if eB > T or eT < B: return False if edge in {"N", "S"}: - if E < eW or W > eE: + if eW > E or eE < W: return False - if T < eB or B > eT: + if eB > T or eT < B: return False if edge in {"T", "B"}: - if E < eW or W > eE: + if eW > E or eE < W: return False - if N < eS or S > eN: + if eS > N or eN < S: return False return True diff --git a/scripts/g.extension/g.extension.py b/scripts/g.extension/g.extension.py index 4c64a9f9ff8..a944701f77a 100644 --- a/scripts/g.extension/g.extension.py +++ b/scripts/g.extension/g.extension.py @@ -2078,7 +2078,7 @@ def install_extension_std_platforms(name, source, url, branch): if not os.path.exists(os.path.join(gisbase, "include", "Make", "Module.make")): gs.fatal(_("Please install GRASS development package")) - if 0 != gs.call(make_cmd, stdout=outdev): + if gs.call(make_cmd, stdout=outdev) != 0: gs.fatal(_("Compilation failed, sorry. Please check above error messages.")) if flags["i"]: From 98222a25c92ed58db7c6cebaac27830771c53b0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Sat, 13 Jul 2024 17:56:08 -0400 Subject: [PATCH 025/471] style: Fix various FURB (small count) (#4046) * style: Fix reimplemented-starmap (FURB140) Ruff rule: https://docs.astral.sh/ruff/rules/reimplemented-starmap/ * style: Fix delete-full-slice (FURB131) Ruff rule: https://docs.astral.sh/ruff/rules/delete-full-slice/ * style: Fix unnecessary-enumerate (FURB148) Ruff rule: https://docs.astral.sh/ruff/rules/unnecessary-enumerate * style: Fix for-loop-set-mutations (FURB142) Ruff rule: https://docs.astral.sh/ruff/rules/for-loop-set-mutations * style: Fix single-item-membership-test (FURB171) Ruff rule: https://docs.astral.sh/ruff/rules/single-item-membership-test --- gui/wxpython/animation/controller.py | 6 ++---- gui/wxpython/animation/temporal_manager.py | 3 +-- gui/wxpython/animation/utils.py | 3 +-- gui/wxpython/core/render.py | 2 +- gui/wxpython/core/toolboxes.py | 2 +- gui/wxpython/mapwin/buffered.py | 2 +- gui/wxpython/psmap/dialogs.py | 4 ++-- lib/init/grass.py | 2 +- pyproject.toml | 5 ----- python/grass/grassdb/history.py | 8 ++++---- scripts/g.extension/g.extension.py | 2 +- 11 files changed, 15 insertions(+), 24 deletions(-) diff --git a/gui/wxpython/animation/controller.py b/gui/wxpython/animation/controller.py index 3e914e68112..abb5ef202ea 100644 --- a/gui/wxpython/animation/controller.py +++ b/gui/wxpython/animation/controller.py @@ -36,6 +36,7 @@ HashCmds, ) from animation.data import AnimationData +from itertools import starmap class AnimationController(wx.EvtHandler): @@ -368,10 +369,7 @@ def _updateAnimations(self, activeIndices, mapNamesDict=None): if anim.viewMode == "3d": regions = [None] * len(regions) self.animations[i].SetFrames( - [ - HashCmds(cmdList, region) - for cmdList, region in zip(anim.cmdMatrix, regions) - ] + list(starmap(HashCmds, zip(anim.cmdMatrix, regions))) ) self.animations[i].SetActive(True) else: diff --git a/gui/wxpython/animation/temporal_manager.py b/gui/wxpython/animation/temporal_manager.py index 4c1816fea19..2e7b14cefa5 100644 --- a/gui/wxpython/animation/temporal_manager.py +++ b/gui/wxpython/animation/temporal_manager.py @@ -116,8 +116,7 @@ def _setTemporalState(self): # check for units for relative type if relative: units = set() - for infoDict in self.timeseriesInfo.values(): - units.add(infoDict["unit"]) + units.update(infoDict["unit"] for infoDict in self.timeseriesInfo.values()) if len(units) > 1: message = _( "It is not allowed to display data with different units (%s)." diff --git a/gui/wxpython/animation/utils.py b/gui/wxpython/animation/utils.py index 7f20c74f4cc..9e5f685545e 100644 --- a/gui/wxpython/animation/utils.py +++ b/gui/wxpython/animation/utils.py @@ -214,8 +214,7 @@ def checkSeriesCompatibility(mapSeriesList=None, timeseriesList=None): if mapSeriesList: count = set() - for mapSeries in mapSeriesList: - count.add(len(mapSeries)) + count.update(len(mapSeries) for mapSeries in mapSeriesList) if len(count) > 1: raise GException( _( diff --git a/gui/wxpython/core/render.py b/gui/wxpython/core/render.py index f9fcb4fd4d4..6fe9d7f4592 100644 --- a/gui/wxpython/core/render.py +++ b/gui/wxpython/core/render.py @@ -883,7 +883,7 @@ def _projInfo(self): for line in ret.splitlines(): if ":" in line: key, val = (x.strip() for x in line.split(":", 1)) - if key in {"units"}: + if key == "units": val = val.lower() projinfo[key] = val elif "XY location (unprojected)" in line: diff --git a/gui/wxpython/core/toolboxes.py b/gui/wxpython/core/toolboxes.py index c10167b639a..e9960ac2e62 100644 --- a/gui/wxpython/core/toolboxes.py +++ b/gui/wxpython/core/toolboxes.py @@ -82,7 +82,7 @@ def getMessages(): def clearMessages(): - del _MESSAGES[:] + _MESSAGES.clear() def _debug(level, message): diff --git a/gui/wxpython/mapwin/buffered.py b/gui/wxpython/mapwin/buffered.py index d8d7a3fb667..6ce7f98731e 100644 --- a/gui/wxpython/mapwin/buffered.py +++ b/gui/wxpython/mapwin/buffered.py @@ -1530,7 +1530,7 @@ def OnDragging(self, event): self.mouse["end"] = event.GetPosition() if event.LeftIsDown() and not ( digitToolbar - and digitToolbar.GetAction() in {"moveLine"} + and digitToolbar.GetAction() == "moveLine" and len(self.digit.GetDisplay().GetSelected()) > 0 ): self.MouseDraw(pdc=self.pdcTmp) diff --git a/gui/wxpython/psmap/dialogs.py b/gui/wxpython/psmap/dialogs.py index 8ee3661d8b5..cfc9f85bed8 100644 --- a/gui/wxpython/psmap/dialogs.py +++ b/gui/wxpython/psmap/dialogs.py @@ -3978,7 +3978,7 @@ def OnIsLegend(self, event): if page == 0 or event is None: children = self.panelRaster.GetChildren() if self.isRLegend.GetValue(): - for i, widget in enumerate(children): + for widget in children: widget.Enable() self.OnRaster(None) self.OnRange(None) @@ -3990,7 +3990,7 @@ def OnIsLegend(self, event): if page == 1 or event is None: children = self.panelVector.GetChildren() if self.isVLegend.GetValue(): - for i, widget in enumerate(children): + for widget in children: widget.Enable() self.OnSpan(None) self.OnBorder(None) diff --git a/lib/init/grass.py b/lib/init/grass.py index 00baa12f1b3..3548eab2b08 100755 --- a/lib/init/grass.py +++ b/lib/init/grass.py @@ -2520,7 +2520,7 @@ def main(): ) if sh in {"csh", "tcsh"}: shell_process = csh_startup(mapset_settings.full_mapset, grass_env_file) - elif sh in {"zsh"}: + elif sh == "zsh": shell_process = sh_like_startup( mapset_settings.full_mapset, mapset_settings.location, diff --git a/pyproject.toml b/pyproject.toml index 50e9f651b0f..4cc2f9a3d8d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -154,13 +154,8 @@ ignore = [ "FURB101", # read-whole-file "FURB103", # write-whole-file. "FURB118", # reimplemented-operator - "FURB131", # delete-full-slice - "FURB140", # reimplemented-starmap - "FURB142", # for-loop-set-mutations - "FURB148", # unnecessary-enumerate "FURB152", # math-constant "FURB154", # repeated-global - "FURB171", # single-item-membership-test "I001", # unsorted-imports "ISC003", # explicit-string-concatenation "PERF203", # try-except-in-loop diff --git a/python/grass/grassdb/history.py b/python/grass/grassdb/history.py index ba9ed818f36..85ebdb5b452 100644 --- a/python/grass/grassdb/history.py +++ b/python/grass/grassdb/history.py @@ -162,7 +162,7 @@ def filter(json_data, command, timestamp): return None -def _remove_entry_from_plain_text(history_path, index): +def _remove_entry_from_plain_text(history_path, index: int): """Remove entry from plain text history file. :param str history_path: path to the history log file @@ -174,7 +174,7 @@ def _remove_entry_from_plain_text(history_path, index): file_history.seek(0) file_history.truncate() for number, line in enumerate(lines): - if number not in [index]: + if number != index: file_history.write(line) except OSError as e: raise OSError( @@ -184,7 +184,7 @@ def _remove_entry_from_plain_text(history_path, index): ) from e -def _remove_entry_from_JSON(history_path, index): +def _remove_entry_from_JSON(history_path, index: int): """Remove entry from JSON history file. :param str history_path: path to the history log file @@ -214,7 +214,7 @@ def _remove_entry_from_JSON(history_path, index): ) from e -def remove_entry(history_path, index): +def remove_entry(history_path, index: int): """Remove entry from history file. :param str history_path: path to the history log file diff --git a/scripts/g.extension/g.extension.py b/scripts/g.extension/g.extension.py index a944701f77a..7103a9e4f15 100644 --- a/scripts/g.extension/g.extension.py +++ b/scripts/g.extension/g.extension.py @@ -1900,7 +1900,7 @@ def download_source_code( ) ) download_source_code_svn(url, name, outdev, directory) - elif source in {"remote_zip"}: + elif source == "remote_zip": gs.message( _("Fetching <{name}> from <{url}> (be patient)...").format( name=name, url=url From 76958057df4013ce30f0d6d15b1699832e0aadab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Sat, 13 Jul 2024 18:53:25 -0400 Subject: [PATCH 026/471] style: Fix read-whole-file (FURB101) and write-whole-file (FURB103) (#4047) * style: Fix read-whole-file (FURB101) Ruff rule: https://docs.astral.sh/ruff/rules/read-whole-file/ * style: Extract repeated config_directory variable in generate_release_notes * style: Fix write-whole-file (FURB103) Ruff rule: https://docs.astral.sh/ruff/rules/write-whole-file/ --- display/d.text/test.py | 5 +- gui/wxpython/core/render.py | 8 +- gui/wxpython/core/toolboxes.py | 4 +- gui/wxpython/gui_core/pyedit.py | 9 +- man/build_manual_gallery.py | 6 +- pyproject.toml | 2 - python/grass/benchmark/results.py | 7 +- python/grass/grassdb/history.py | 39 ++++--- python/grass/gunittest/invoker.py | 33 +++--- python/grass/gunittest/reporters.py | 12 +-- .../gunittest/testsuite/test_assertions.py | 12 +-- python/grass/jupyter/baseseriesmap.py | 4 +- python/grass/jupyter/interactivemap.py | 6 +- python/grass/pygrass/raster/category.py | 14 +-- python/grass/pygrass/shell/show.py | 5 +- .../benchmark/benchmark_r_mfilter_nprocs.py | 9 +- .../r.neighbors/testsuite/test_r_neighbors.py | 6 +- scripts/g.extension/g.extension.py | 15 +-- .../testsuite/test_addons_download.py | 3 +- scripts/r.in.wms/wms_base.py | 4 +- scripts/r.unpack/r.unpack.py | 4 +- scripts/v.db.addcolumn/v.db.addcolumn.py | 4 +- scripts/v.in.wfs/v.in.wfs.py | 11 +- .../testsuite/test_distr_tgis_db_raster.py | 7 +- .../testsuite/test_distr_tgis_db_raster3d.py | 7 +- .../testsuite/test_distr_tgis_db_vector.py | 7 +- .../testsuite/test_t_register_raster_file.py | 102 +++++++++--------- utils/g.html2man/g.html2man.py | 6 +- utils/generate_release_notes.py | 16 ++- utils/mkhtml.py | 4 +- utils/update_version.py | 4 +- 31 files changed, 175 insertions(+), 200 deletions(-) diff --git a/display/d.text/test.py b/display/d.text/test.py index ffc50cfc8aa..c68449ed7be 100755 --- a/display/d.text/test.py +++ b/display/d.text/test.py @@ -2,6 +2,7 @@ # Author: Owen Smith - Rewritten from test.pl by Huidae Cho # Run: d.mon start=wx0 && ./test.py | d.text at=0,100 import math +from pathlib import Path import re # Quiet black syntax checking for fonts and colors to keep the code printed to @@ -66,8 +67,8 @@ def text(in_text): color("gray") rc(1, 1) -with open(__file__) as f: - src = f.read() + +src = Path(__file__).read_text() print( ".L 0\n" diff --git a/gui/wxpython/core/render.py b/gui/wxpython/core/render.py index 6fe9d7f4592..2021f491fc1 100644 --- a/gui/wxpython/core/render.py +++ b/gui/wxpython/core/render.py @@ -22,6 +22,7 @@ """ import os +from pathlib import Path import sys import glob import math @@ -725,10 +726,9 @@ def OnRenderDone(self, env): continue if os.path.isfile(layer._legrow) and not layer.hidden: - with open(layer._legrow) as infile: - line = infile.read() - outfile.write(line) - new_legend.append(line) + line = Path(layer._legrow).read_text() + outfile.write(line) + new_legend.append(line) self._rendering = False if wx.IsBusy(): diff --git a/gui/wxpython/core/toolboxes.py b/gui/wxpython/core/toolboxes.py index e9960ac2e62..798df081e06 100644 --- a/gui/wxpython/core/toolboxes.py +++ b/gui/wxpython/core/toolboxes.py @@ -13,6 +13,7 @@ """ import os +from pathlib import Path import sys import copy import shutil @@ -846,8 +847,7 @@ def module_test(): return 0 menudataFile = "data/test_toolboxes_menudata_ref.xml" - with open(menudataFile) as correctMenudata: - correct = str(correctMenudata.read()) + correct = str(Path(menudataFile).read_text()) import difflib diff --git a/gui/wxpython/gui_core/pyedit.py b/gui/wxpython/gui_core/pyedit.py index 573496cbbfc..b7c8baf4001 100644 --- a/gui/wxpython/gui_core/pyedit.py +++ b/gui/wxpython/gui_core/pyedit.py @@ -10,6 +10,7 @@ :authors: Martin Landa """ +from pathlib import Path import sys import os import stat @@ -300,8 +301,7 @@ def _openFile(self, file_path): :return str or None: file content or None """ try: - with open(file_path, "r") as f: - return f.read() + return Path(file_path).read_text() except PermissionError: GError( message=_( @@ -327,9 +327,8 @@ def _writeFile(self, file_path, content, additional_err_message=""): :return None or True: file written or None """ try: - with open(file_path, "w") as f: - f.write(content) - return True + Path(file_path).write_text(content) + return True except PermissionError: GError( message=_( diff --git a/man/build_manual_gallery.py b/man/build_manual_gallery.py index 414b36c8a85..ea1ffb449c2 100755 --- a/man/build_manual_gallery.py +++ b/man/build_manual_gallery.py @@ -14,6 +14,7 @@ ############################################################################# import os +from pathlib import Path import sys import fnmatch import re @@ -97,9 +98,8 @@ def img_in_html(filename, imagename): # for some reason, calling search just once is much faster # than calling it on every line (time is spent in _compile) pattern = re.compile("".format(imagename)) - with open(filename) as file: - if re.search(pattern, file.read()): - return True + if re.search(pattern, Path(filename).read_text()): + return True return False diff --git a/pyproject.toml b/pyproject.toml index 4cc2f9a3d8d..7d544473f60 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -151,8 +151,6 @@ ignore = [ "FBT001", # boolean-type-hint-positional-argument "FBT002", # boolean-default-value-positional-argument "FBT003", # boolean-positional-value-in-call - "FURB101", # read-whole-file - "FURB103", # write-whole-file. "FURB118", # reimplemented-operator "FURB152", # math-constant "FURB154", # repeated-global diff --git a/python/grass/benchmark/results.py b/python/grass/benchmark/results.py index c89d8f1c80b..123e5dd8458 100644 --- a/python/grass/benchmark/results.py +++ b/python/grass/benchmark/results.py @@ -15,6 +15,7 @@ import copy import json +from pathlib import Path from types import SimpleNamespace @@ -48,8 +49,7 @@ def save_results_to_file(results, filename): See :func:`save_results` for details. """ text = save_results(results) - with open(filename, "w", encoding="utf-8") as file: - file.write(text) + Path(filename).write_text(text, encoding="utf-8") def load_results(data): @@ -67,8 +67,7 @@ def load_results_from_file(filename): See :func:`load_results` for details. """ - with open(filename, "r", encoding="utf-8") as file: - return load_results(file.read()) + return load_results(Path(filename).read_text(encoding="utf-8")) def join_results(results, prefixes=None, select=None, prefixes_as_labels=False): diff --git a/python/grass/grassdb/history.py b/python/grass/grassdb/history.py index 85ebdb5b452..60b5459700d 100644 --- a/python/grass/grassdb/history.py +++ b/python/grass/grassdb/history.py @@ -103,27 +103,24 @@ def _read_from_JSON(history_path): """ content_list = [] try: - with open( - history_path, encoding="utf-8", mode="r", errors="replace" - ) as file_history: - content = file_history.read() - if content: - try: - history_entries = json.loads(content) - except ValueError as ve: - raise ValueError( - _("Error decoding content of JSON history file {}").format( - history_path - ) - ) from ve - # Process the content as a list of dictionaries - content_list = [ - { - "command": entry["command"], - "command_info": entry["command_info"], - } - for entry in history_entries - ] + content = Path(history_path).read_text(encoding="utf-8", errors="replace") + if content: + try: + history_entries = json.loads(content) + except ValueError as ve: + raise ValueError( + _("Error decoding content of JSON history file {}").format( + history_path + ) + ) from ve + # Process the content as a list of dictionaries + content_list = [ + { + "command": entry["command"], + "command_info": entry["command_info"], + } + for entry in history_entries + ] except OSError as e: raise OSError( _("Unable to read from JSON history file {}").format(history_path) diff --git a/python/grass/gunittest/invoker.py b/python/grass/gunittest/invoker.py index 2b2de7f5e40..9a19be4c85d 100644 --- a/python/grass/gunittest/invoker.py +++ b/python/grass/gunittest/invoker.py @@ -11,6 +11,7 @@ import collections import os +from pathlib import Path import shutil import subprocess import sys @@ -39,8 +40,7 @@ # TODO: this might be more extend then update def update_keyval_file(filename, module, returncode): if os.path.exists(filename): - with open(filename, "r") as keyval_file: - keyval = text_to_keyvalue(keyval_file.read(), sep="=") + keyval = text_to_keyvalue(Path(filename).read_text(), sep="=") else: keyval = {} @@ -62,8 +62,7 @@ def update_keyval_file(filename, module, returncode): keyval["returncode"] = returncode keyval["test_file_authors"] = test_file_authors - with open(filename, "w") as keyval_file: - keyval_file.write(keyvalue_to_text(keyval)) + Path(filename).write_text(keyvalue_to_text(keyval)) return keyval @@ -243,8 +242,7 @@ def try_decode(data, encodings): stdout = try_decode(stdout, encodings=encodings) stderr = try_decode(stderr, encodings=encodings) - with open(stdout_path, "w") as stdout_file: - stdout_file.write(stdout) + Path(stdout_path).write_text(stdout) with open(stderr_path, "w") as stderr_file: if type(stderr) == "bytes": stderr_file.write(decode(stderr)) @@ -337,18 +335,17 @@ def run_in_location(self, gisdbase, location, location_type, results_dir, exclud # TODO: move this to some (new?) reporter # TODO: add basic summary of linked files so that the page is not empty - with open(os.path.join(results_dir, "index.html"), "w") as main_index: - main_index.write( - "" - "

Tests for <{location}>" - " using <{type}> type tests

" - "
    " - '
  • Results by testsuites' - " (testsuite directories)
  • " - '
  • Results by test files
  • ' - "
      " - "".format(location=location, type=location_type) - ) + Path(os.path.join(results_dir, "index.html")).write_text( + "" + "

      Tests for <{location}>" + " using <{type}> type tests

      " + "
        " + '
      • Results by testsuites' + " (testsuite directories)
      • " + '
      • Results by test files
      • ' + "
          " + "".format(location=location, type=location_type) + ) testsuite_dir_reporter = TestsuiteDirReporter( main_page_name="testsuites.html", diff --git a/python/grass/gunittest/reporters.py b/python/grass/gunittest/reporters.py index 63bffc655e1..21afce314e1 100644 --- a/python/grass/gunittest/reporters.py +++ b/python/grass/gunittest/reporters.py @@ -11,6 +11,7 @@ import os import datetime +from pathlib import Path from xml.sax import saxutils import xml.etree.ElementTree as et import subprocess @@ -906,9 +907,8 @@ def finish(self): summary[key] = value summary_filename = os.path.join(self.result_dir, "test_keyvalue_result.txt") - with open(summary_filename, "w") as summary_file: - text = keyvalue_to_text(summary, sep="=", vsep="\n", isep=",") - summary_file.write(text) + text = keyvalue_to_text(summary, sep="=", vsep="\n", isep=",") + Path(summary_filename).write_text(text) def end_file_test( self, module, cwd, returncode, stdout, stderr, test_summary, timed_out=None @@ -1025,8 +1025,7 @@ def end_file_test( width = 72 self._stream.write(width * "=") self._stream.write("\n") - with open(stderr) as text: - self._stream.write(text.read()) + self._stream.write(Path(stderr).read_text()) self._stream.write(width * "=") self._stream.write("\n") self._stream.write(f"FAILED {module.file_path}") @@ -1116,8 +1115,7 @@ def report_for_dir(self, root, directory, test_files): root, directory, test_file_name, "test_keyvalue_result.txt" ) # if os.path.exists(summary_filename): - with open(summary_filename, "r") as keyval_file: - summary = text_to_keyvalue(keyval_file.read(), sep="=") + summary = text_to_keyvalue(Path(summary_filename).read_text(), sep="=") # else: # TODO: write else here # summary = None diff --git a/python/grass/gunittest/testsuite/test_assertions.py b/python/grass/gunittest/testsuite/test_assertions.py index 51720a56b76..3af538132f3 100644 --- a/python/grass/gunittest/testsuite/test_assertions.py +++ b/python/grass/gunittest/testsuite/test_assertions.py @@ -3,6 +3,7 @@ """ import os +from pathlib import Path import grass.script.core as gcore from grass.pygrass.modules import Module @@ -348,20 +349,19 @@ def setUpClass(cls): open(cls.emtpy_file, "w").close() cls.file_with_md5 = cls.__name__ + "_this_is_a_file_with_known_md5" file_content = "Content of the file with known MD5.\n" - with open(cls.file_with_md5, "w") as f: - f.write(file_content) + Path(cls.file_with_md5).write_text(file_content) # MD5 sum created using: # echo 'Content of the file with known MD5.' > some_file.txt # md5sum some_file.txt cls.file_md5 = "807bba4ffac4bb351bc3f27853009949" cls.file_with_same_content = cls.__name__ + "_file_with_same_content" - with open(cls.file_with_same_content, "w") as f: - f.write(file_content) + Path(cls.file_with_same_content).write_text(file_content) cls.file_with_different_content = cls.__name__ + "_file_with_different_content" - with open(cls.file_with_different_content, "w") as f: - f.write(file_content + " Something else here.") + Path(cls.file_with_different_content).write_text( + file_content + " Something else here." + ) @classmethod def tearDownClass(cls): diff --git a/python/grass/jupyter/baseseriesmap.py b/python/grass/jupyter/baseseriesmap.py index e28572e1f3e..50f28f58049 100644 --- a/python/grass/jupyter/baseseriesmap.py +++ b/python/grass/jupyter/baseseriesmap.py @@ -1,6 +1,7 @@ """Base class for SeriesMap and TimeSeriesMap""" import os +from pathlib import Path import tempfile import weakref import shutil @@ -177,8 +178,7 @@ def change_slider(change): # Display image associated with datetime def change_image(index): filename = self._base_filename_dict[index] - with open(filename, "rb") as rfile: - out_img.value = rfile.read() + out_img.value = Path(filename).read_bytes() widgets.interactive_output(change_image, {"index": slider}) diff --git a/python/grass/jupyter/interactivemap.py b/python/grass/jupyter/interactivemap.py index f5eae8588ff..082122d24d0 100644 --- a/python/grass/jupyter/interactivemap.py +++ b/python/grass/jupyter/interactivemap.py @@ -15,6 +15,7 @@ import base64 import json +from pathlib import Path from .reprojection_renderer import ReprojectionRenderer @@ -119,9 +120,8 @@ def add_to(self, interactive_map): # ImageOverlays don't work well with local files, # they need relative address and behavior differs # for notebooks and jupyterlab - with open(self._filename, "rb") as file: - data = base64.b64encode(file.read()).decode("ascii") - url = "data:image/png;base64," + data + data = base64.b64encode(Path(self._filename).read_bytes()).decode("ascii") + url = "data:image/png;base64," + data image = ipyleaflet.ImageOverlay( url=url, bounds=self._bounds, name=self._title, **self._layer_kwargs ) diff --git a/python/grass/pygrass/raster/category.py b/python/grass/pygrass/raster/category.py index 2cee672ed32..31ef5f3ed76 100644 --- a/python/grass/pygrass/raster/category.py +++ b/python/grass/pygrass/raster/category.py @@ -6,6 +6,7 @@ import ctypes from operator import itemgetter +from pathlib import Path import grass.lib.raster as libraster from grass.exceptions import ImplementationError @@ -325,13 +326,12 @@ def write_rules(self, filename, sep=":"): :param str filename: the name of file with categories rules :param str sep: the separator used to divide values and category """ - with open(filename, "w") as f: - cats = [] - for cat in self.__iter__(): - if cat[-1] is None: - cat = cat[:-1] - cats.append(sep.join([str(i) for i in cat])) - f.write("\n".join(cats)) + cats = [] + for cat in self.__iter__(): + if cat[-1] is None: + cat = cat[:-1] + cats.append(sep.join([str(i) for i in cat])) + Path(filename).write_text("\n".join(cats)) def sort(self): libraster.Rast_sort_cats(ctypes.byref(self.c_cats)) diff --git a/python/grass/pygrass/shell/show.py b/python/grass/pygrass/shell/show.py index 20b721e726b..8465dc9e92d 100644 --- a/python/grass/pygrass/shell/show.py +++ b/python/grass/pygrass/shell/show.py @@ -4,7 +4,8 @@ @author: pietro """ +from pathlib import Path + def raw_figure(figpath): - with open(figpath, mode="rb") as data: - return data.read() + return Path(figpath).read_bytes() diff --git a/raster/r.mfilter/benchmark/benchmark_r_mfilter_nprocs.py b/raster/r.mfilter/benchmark/benchmark_r_mfilter_nprocs.py index 7212fc7e193..2dc85b411d8 100644 --- a/raster/r.mfilter/benchmark/benchmark_r_mfilter_nprocs.py +++ b/raster/r.mfilter/benchmark/benchmark_r_mfilter_nprocs.py @@ -3,6 +3,8 @@ @author Aaron Saw Min Sern """ +from pathlib import Path + from grass.exceptions import CalledModuleError from grass.pygrass.modules import Module from grass.script import tempfile @@ -27,9 +29,8 @@ def benchmark(size, label, results): reference = "r_mfilter_reference_map" output = "benchmark_r_mfilter_nprocs" filter = tempfile() - with open(filter, "w") as w: - w.write( - """MATRIX 9 + Path(filter).write_text( + """MATRIX 9 1 1 1 1 1 1 1 1 1 1 2 1 2 1 2 1 2 1 1 1 3 1 3 1 3 1 1 @@ -41,7 +42,7 @@ def benchmark(size, label, results): 1 1 1 1 1 1 1 1 1 DIVISOR 81 TYPE P""" - ) + ) generate_map(rows=size, cols=size, fname=reference) module = Module( diff --git a/raster/r.neighbors/testsuite/test_r_neighbors.py b/raster/r.neighbors/testsuite/test_r_neighbors.py index b447eabe1d2..c333a3abdaa 100644 --- a/raster/r.neighbors/testsuite/test_r_neighbors.py +++ b/raster/r.neighbors/testsuite/test_r_neighbors.py @@ -1,3 +1,4 @@ +from pathlib import Path from grass.gunittest.case import TestCase from grass.gunittest.main import test from grass.script.raster import raster_info @@ -628,8 +629,9 @@ def test_weighting_file(self): self.to_remove.extend(outputs_threaded) weights = tempfile() - with open(weights, "w") as w: - w.write("0 1 1 1 0\n1 0 0 0 1\n1 0 0 0 1\n1 0 0 0 1\n0 1 1 1 0") + Path(weights).write_text( + "0 1 1 1 0\n1 0 0 0 1\n1 0 0 0 1\n1 0 0 0 1\n0 1 1 1 0" + ) self.assertModule( "r.neighbors", diff --git a/scripts/g.extension/g.extension.py b/scripts/g.extension/g.extension.py index 7103a9e4f15..c02b8b8e3a4 100644 --- a/scripts/g.extension/g.extension.py +++ b/scripts/g.extension/g.extension.py @@ -489,8 +489,7 @@ def urlretrieve(url, filename, *args, **kwargs): """ request = urlrequest.Request(url, headers=HEADERS) response = urlrequest.urlopen(request, *args, **kwargs) - with open(filename, "wb") as f: - f.write(response.read()) + Path(filename).write_bytes(response.read()) def urlopen(url, *args, **kwargs): @@ -572,8 +571,7 @@ def get_default_branch(full_url): def etree_fromfile(filename): """Create XML element tree from a given file name""" - with open(filename, "r") as file_: - return etree.fromstring(file_.read()) + return etree.fromstring(Path(filename).read_text()) def etree_fromurl(url): @@ -1294,8 +1292,7 @@ def install_toolbox_xml(url, name): write_xml_modules(xml_file) # read XML file - with open(xml_file, "r") as xml: - tree = etree.fromstring(xml.read()) + tree = etree.fromstring(Path(xml_file).read_text()) # update tree tnode = None @@ -1798,16 +1795,14 @@ def is_binary_string(bytes): continue # ignore binary files # read content of text file - with open(filename, "rb") as fd: - data = fd.read() + data = Path(filename).read_bytes() # we don't expect there would be CRLF file by # purpose if we want to allow CRLF files we would # have to whitelite .py etc newdata = data.replace(b"\r\n", b"\n") if newdata != data: - with open(filename, "wb") as newfile: - newfile.write(newdata) + Path(filename).write_bytes(newdata) def extract_zip(name, directory, tmpdir): diff --git a/scripts/g.extension/testsuite/test_addons_download.py b/scripts/g.extension/testsuite/test_addons_download.py index 384c6a4950f..c4618306d12 100644 --- a/scripts/g.extension/testsuite/test_addons_download.py +++ b/scripts/g.extension/testsuite/test_addons_download.py @@ -203,8 +203,7 @@ def test_github_official_module_man_page_src_code_links_exists(self): ) html_man_page = self.install_prefix / "docs" / "html" / "db.join.html" self.assertFileExists(str(html_man_page)) - with open(html_man_page) as f: - content = f.read() + content = Path(html_man_page).read_text() for link_name in [f"{extension} source code", "history"]: url = re.search(rf"{link_name}", content).group(1) self.assertTrue(url) diff --git a/scripts/r.in.wms/wms_base.py b/scripts/r.in.wms/wms_base.py index 1847ca05783..a14e15dc786 100644 --- a/scripts/r.in.wms/wms_base.py +++ b/scripts/r.in.wms/wms_base.py @@ -19,6 +19,7 @@ import os from http.client import HTTPException from math import ceil +from pathlib import Path from urllib.request import Request, urlopen from urllib.error import HTTPError @@ -304,8 +305,7 @@ def GetCapabilities(self, options): # save to file if capfile_output: try: - with open(capfile_output, "w") as temp: - temp.write(cap) + Path(capfile_output).write_text(cap) return except OSError as error: gs.fatal(_("Unable to open file '%s'.\n%s\n" % (capfile_output, error))) diff --git a/scripts/r.unpack/r.unpack.py b/scripts/r.unpack/r.unpack.py index a19123b28f0..053ac96988b 100644 --- a/scripts/r.unpack/r.unpack.py +++ b/scripts/r.unpack/r.unpack.py @@ -41,6 +41,7 @@ # %end import os +from pathlib import Path import sys import shutil import tarfile @@ -243,8 +244,7 @@ def main(): if maps: if vrt_file and os.path.exists(vrt_file): files = "\n".join(maps) - with open(vrt_file, "w") as f: - f.write(files) + Path(vrt_file).write_text(files) grass.message(_("Raster map <{name}> unpacked".format(name=map_name))) diff --git a/scripts/v.db.addcolumn/v.db.addcolumn.py b/scripts/v.db.addcolumn/v.db.addcolumn.py index 0e7b8a35c1a..2d6c88cd2e5 100755 --- a/scripts/v.db.addcolumn/v.db.addcolumn.py +++ b/scripts/v.db.addcolumn/v.db.addcolumn.py @@ -42,6 +42,7 @@ import atexit import os +from pathlib import Path import re from grass.exceptions import CalledModuleError @@ -124,8 +125,7 @@ def main(): sql_file = gs.tempfile() rm_files.append(sql_file) cols_add_str = ",".join([col[0] for col in columns]) - with open(sql_file, "w") as write_file: - write_file.write(add_str) + Path(sql_file).write_text(add_str) try: gs.run_command( "db.execute", diff --git a/scripts/v.in.wfs/v.in.wfs.py b/scripts/v.in.wfs/v.in.wfs.py index a983b87cab4..bd127c3bea0 100755 --- a/scripts/v.in.wfs/v.in.wfs.py +++ b/scripts/v.in.wfs/v.in.wfs.py @@ -106,6 +106,7 @@ import os +from pathlib import Path import sys from grass.script.utils import try_remove from grass.script import core as grass @@ -164,17 +165,15 @@ def main(): if options["username"] and options["password"]: grass.message(_("Setting username and password...")) if os.path.isfile(options["username"]): - with open(options["username"]) as f: - filecontent = f.read() - user = filecontent.strip() + filecontent = Path(options["username"]).read_text() + user = filecontent.strip() elif options["username"] in os.environ: user = os.environ[options["username"]] else: user = options["username"] if os.path.isfile(options["password"]): - with open(options["password"]) as f: - filecontent = f.read() - pw = filecontent.strip() + filecontent = Path(options["password"]).read_text() + pw = filecontent.strip() elif options["password"] in os.environ: pw = os.environ[options["password"]] else: diff --git a/temporal/t.connect/testsuite/test_distr_tgis_db_raster.py b/temporal/t.connect/testsuite/test_distr_tgis_db_raster.py index 834a5a9ad65..412a926f01d 100644 --- a/temporal/t.connect/testsuite/test_distr_tgis_db_raster.py +++ b/temporal/t.connect/testsuite/test_distr_tgis_db_raster.py @@ -9,6 +9,7 @@ """ import os +from pathlib import Path from grass.gunittest.case import TestCase from grass.gunittest.gmodules import SimpleModule @@ -107,8 +108,7 @@ def test_tlist(self): ) self.assertModule(t_list) self.assertFileExists(self.outfile) - with open(self.outfile, "r") as f: - read_data = f.read() + read_data = Path(self.outfile).read_text() for a, b in zip(list_string.split("\n"), read_data.split("\n")): self.assertEqual(a.strip(), b.strip()) # self.assertLooksLike(reference=read_data, actual=list_string) @@ -183,8 +183,7 @@ def test_trast_list(self): ) self.assertModule(trast_list) self.assertFileExists(self.outfile) - with open(self.outfile, "r") as f: - read_data = f.read() + read_data = Path(self.outfile).read_text() for a, b in zip(list_string.split("\n"), read_data.split("\n")): self.assertEqual(a.strip(), b.strip()) if os.path.isfile(self.outfile): diff --git a/temporal/t.connect/testsuite/test_distr_tgis_db_raster3d.py b/temporal/t.connect/testsuite/test_distr_tgis_db_raster3d.py index ed8e3c31cfd..23bc11c271e 100644 --- a/temporal/t.connect/testsuite/test_distr_tgis_db_raster3d.py +++ b/temporal/t.connect/testsuite/test_distr_tgis_db_raster3d.py @@ -9,6 +9,7 @@ """ import os +from pathlib import Path from grass.gunittest.case import TestCase from grass.gunittest.gmodules import SimpleModule @@ -107,8 +108,7 @@ def test_tlist(self): ) self.assertModule(t_list) self.assertFileExists(self.outfile) - with open(self.outfile, "r") as f: - read_data = f.read() + read_data = Path(self.outfile).read_text() for a, b in zip(list_string.split("\n"), read_data.split("\n")): self.assertEqual(a.strip(), b.strip()) # self.assertLooksLike(reference=read_data, actual=list_string) @@ -183,8 +183,7 @@ def test_trast_list(self): ) self.assertModule(trast_list) self.assertFileExists(self.outfile) - with open(self.outfile, "r") as f: - read_data = f.read() + read_data = Path(self.outfile).read_text() for a, b in zip(list_string.split("\n"), read_data.split("\n")): self.assertEqual(a.strip(), b.strip()) if os.path.isfile(self.outfile): diff --git a/temporal/t.connect/testsuite/test_distr_tgis_db_vector.py b/temporal/t.connect/testsuite/test_distr_tgis_db_vector.py index 7616db89fd5..fd26cc93e26 100644 --- a/temporal/t.connect/testsuite/test_distr_tgis_db_vector.py +++ b/temporal/t.connect/testsuite/test_distr_tgis_db_vector.py @@ -9,6 +9,7 @@ """ import os +from pathlib import Path from grass.gunittest.case import TestCase from grass.gunittest.gmodules import SimpleModule @@ -107,8 +108,7 @@ def test_tlist(self): ) self.assertModule(t_list) self.assertFileExists(self.outfile) - with open(self.outfile, "r") as f: - read_data = f.read() + read_data = Path(self.outfile).read_text() for a, b in zip(list_string.split("\n"), read_data.split("\n")): self.assertEqual(a.strip(), b.strip()) # self.assertLooksLike(reference=read_data, actual=list_string) @@ -200,8 +200,7 @@ def test_tvect_list(self): ) self.assertModule(trast_list) self.assertFileExists(self.outfile) - with open(self.outfile, "r") as f: - read_data = f.read() + read_data = Path(self.outfile).read_text() for a, b in zip(list_string.split("\n"), read_data.split("\n")): self.assertEqual(a.strip(), b.strip()) if os.path.isfile(self.outfile): diff --git a/temporal/t.register/testsuite/test_t_register_raster_file.py b/temporal/t.register/testsuite/test_t_register_raster_file.py index 927d465df56..c8f8abeb55c 100755 --- a/temporal/t.register/testsuite/test_t_register_raster_file.py +++ b/temporal/t.register/testsuite/test_t_register_raster_file.py @@ -22,6 +22,7 @@ """ from datetime import datetime +from pathlib import Path import grass.script as gs import grass.temporal as tgis @@ -79,8 +80,7 @@ def tearDownClass(cls): def test_with_file_and_increment(self): tmp_file = gs.tempfile() - with open(tmp_file, "w") as register_file: - register_file.write("prec_1\nprec_2\nprec_3\nprec_4\nprec_5\nprec_6") + Path(tmp_file).write_text("prec_1\nprec_2\nprec_3\nprec_4\nprec_5\nprec_6") register_module = SimpleModule( "t.register", @@ -122,8 +122,7 @@ def test_with_file_and_increment(self): def test_with_file_and_no_increment(self): tmp_file = gs.tempfile() - with open(tmp_file, "w") as register_file: - register_file.write("prec_1\nprec_2\nprec_3\nprec_4\nprec_5\nprec_6") + Path(tmp_file).write_text("prec_1\nprec_2\nprec_3\nprec_4\nprec_5\nprec_6") register_module = SimpleModule( "t.register", @@ -164,8 +163,7 @@ def test_with_file_and_no_increment(self): def test_with_file_increment_and_intervall(self): tmp_file = gs.tempfile() - with open(tmp_file, "w") as register_file: - register_file.write("prec_1\nprec_2\nprec_3\nprec_4\nprec_5\nprec_6") + Path(tmp_file).write_text("prec_1\nprec_2\nprec_3\nprec_4\nprec_5\nprec_6") register_module = SimpleModule( "t.register", @@ -209,19 +207,18 @@ def test_with_file_increment_and_intervall(self): def test_with_start_in_file(self): tmp_file = gs.tempfile() - with open(tmp_file, "w") as register_file: - register_file.write( - "\n".join( - [ - "prec_1|2001-01-01", - "prec_2|2001-02-01", - "prec_3|2001-03-01", - "prec_4|2001-04-01", - "prec_5|2001-05-01", - "prec_6|2001-06-01", - ] - ) + Path(tmp_file).write_text( + "\n".join( + [ + "prec_1|2001-01-01", + "prec_2|2001-02-01", + "prec_3|2001-03-01", + "prec_4|2001-04-01", + "prec_5|2001-05-01", + "prec_6|2001-06-01", + ] ) + ) register_module = SimpleModule( "t.register", @@ -261,19 +258,18 @@ def test_with_start_in_file(self): def test_with_start_in_file_and_increment(self): tmp_file = gs.tempfile() - with open(tmp_file, "w") as register_file: - register_file.write( - "\n".join( - [ - "prec_1|2001-01-01", - "prec_2|2001-02-01", - "prec_3|2001-03-01", - "prec_4|2001-04-01", - "prec_5|2001-05-01", - "prec_6|2001-06-01", - ] - ) + Path(tmp_file).write_text( + "\n".join( + [ + "prec_1|2001-01-01", + "prec_2|2001-02-01", + "prec_3|2001-03-01", + "prec_4|2001-04-01", + "prec_5|2001-05-01", + "prec_6|2001-06-01", + ] ) + ) register_module = SimpleModule( "t.register", @@ -290,19 +286,18 @@ def test_with_start_in_file_and_increment(self): def test_with_start_and_end_in_file_and_interval(self): tmp_file = gs.tempfile() - with open(tmp_file, "w") as register_file: - register_file.write( - "\n".join( - [ - "prec_1|2001-01-01|2001-04-01", - "prec_2|2001-04-01|2001-07-01", - "prec_3|2001-07-01|2001-10-01", - "prec_4|2001-10-01|2002-01-01", - "prec_5|2002-01-01|2002-04-01", - "prec_6|2002-04-01|2002-07-01", - ] - ) + Path(tmp_file).write_text( + "\n".join( + [ + "prec_1|2001-01-01|2001-04-01", + "prec_2|2001-04-01|2001-07-01", + "prec_3|2001-07-01|2001-10-01", + "prec_4|2001-10-01|2002-01-01", + "prec_5|2002-01-01|2002-04-01", + "prec_6|2002-04-01|2002-07-01", + ] ) + ) register_module = SimpleModule( "t.register", @@ -320,19 +315,18 @@ def test_with_start_and_end_in_file_and_interval(self): def test_with_mapset_and_semantic_label(self): mapset = gs.gisenv()["MAPSET"] tmp_file = gs.tempfile() - with open(tmp_file, "w") as register_file: - register_file.write( - "\n".join( - [ - f"prec_1@{mapset}|2001-01-01|2001-04-01|semantic_label", - f"prec_2@{mapset}|2001-04-01|2001-07-01|semantic_label", - f"prec_3@{mapset}|2001-07-01|2001-10-01|semantic_label", - f"prec_4@{mapset}|2001-10-01|2002-01-01|semantic_label", - f"prec_5@{mapset}|2002-01-01|2002-04-01|semantic_label", - f"prec_6@{mapset}|2002-04-01|2002-07-01|semantic_label", - ] - ) + Path(tmp_file).write_text( + "\n".join( + [ + f"prec_1@{mapset}|2001-01-01|2001-04-01|semantic_label", + f"prec_2@{mapset}|2001-04-01|2001-07-01|semantic_label", + f"prec_3@{mapset}|2001-07-01|2001-10-01|semantic_label", + f"prec_4@{mapset}|2001-10-01|2002-01-01|semantic_label", + f"prec_5@{mapset}|2002-01-01|2002-04-01|semantic_label", + f"prec_6@{mapset}|2002-04-01|2002-07-01|semantic_label", + ] ) + ) register_module = SimpleModule( "t.register", diff --git a/utils/g.html2man/g.html2man.py b/utils/g.html2man/g.html2man.py index e134160d240..498ce34dfe1 100755 --- a/utils/g.html2man/g.html2man.py +++ b/utils/g.html2man/g.html2man.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +from pathlib import Path import sys import re from ghtml import HTMLParser @@ -53,9 +54,8 @@ def main(): s = s.lstrip() # write groff - with open(sys.argv[2], "wb") as outf: - s = s.encode("UTF-8") - outf.write(s) + s = s.encode("UTF-8") + Path(sys.argv[2]).write_bytes(s) if __name__ == "__main__": diff --git a/utils/generate_release_notes.py b/utils/generate_release_notes.py index 40511c3dc44..3cfffe77bd2 100755 --- a/utils/generate_release_notes.py +++ b/utils/generate_release_notes.py @@ -26,6 +26,7 @@ " date: %ad%n" " message: |-%n %s" ) +CONFIG_DIRECTORY = Path("utils") def remove_excluded_changes(changes, exclude): @@ -266,18 +267,17 @@ def notes_from_git_log(start_tag, end_tag, categories, exclude): if not commits: raise RuntimeError("No commits retrieved from git log (try different tags)") - config_directory = Path("utils") svn_name_by_git_author = csv_to_dict( - config_directory / "svn_name_git_author.csv", + CONFIG_DIRECTORY / "svn_name_git_author.csv", key="git_author", value="svn_name", ) github_name_by_svn_name = csv_to_dict( - config_directory / "svn_name_github_name.csv", + CONFIG_DIRECTORY / "svn_name_github_name.csv", key="svn_name", value="github_name", ) - github_name_by_git_author_file = config_directory / "git_author_github_name.csv" + github_name_by_git_author_file = CONFIG_DIRECTORY / "git_author_github_name.csv" github_name_by_git_author = csv_to_dict( github_name_by_git_author_file, key="git_author", @@ -343,9 +343,8 @@ def create_release_notes(args): check=True, ).stdout.strip() - config_directory = Path("utils") - with open(config_directory / "release.yml", encoding="utf-8") as file: - config = yaml.safe_load(file.read())["notes"] + config_file = CONFIG_DIRECTORY / "release.yml" + config = yaml.safe_load(config_file.read_text(encoding="utf-8"))["notes"] if args.backend == "api": notes_from_gh_api( @@ -391,8 +390,7 @@ def main(): args = parser.parse_args() if args.backend == "check": config_file = Path("utils") / "release.yml" - with open(config_file, encoding="utf-8") as file: - config = yaml.safe_load(file.read()) + config = yaml.safe_load(Path(config_file).read_text(encoding="utf-8")) has_match = False for category in config["notes"]["categories"]: if re.match(category["regexp"], args.branch): diff --git a/utils/mkhtml.py b/utils/mkhtml.py index c5a7519872d..1e9a335bcc2 100644 --- a/utils/mkhtml.py +++ b/utils/mkhtml.py @@ -26,6 +26,7 @@ import json import pathlib import subprocess +from pathlib import Path from html.parser import HTMLParser @@ -513,8 +514,7 @@ def get_last_git_commit(src_dir, addon_path, is_addon): def read_file(name): try: - with open(name) as f: - return f.read() + return Path(name).read_text() except OSError: return "" diff --git a/utils/update_version.py b/utils/update_version.py index 386c42482f5..c66a34a62cd 100755 --- a/utils/update_version.py +++ b/utils/update_version.py @@ -5,14 +5,14 @@ import sys import datetime from types import SimpleNamespace +from pathlib import Path import argparse def read_version_file(): """Return version file content as object instance with attributes""" - with open("include/VERSION", encoding="utf-8") as file: - lines = file.read().splitlines() + lines = Path("include/VERSION").read_text(encoding="utf-8").splitlines() return SimpleNamespace( major=lines[0], minor=lines[1], micro=lines[2], year=lines[3] ) From b565924894c4ec35ab4584ef3a23c35087116b1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Sun, 14 Jul 2024 05:59:20 -0400 Subject: [PATCH 027/471] style: repeated-global (FURB154) (#4048) --- gui/wxpython/gcp/manager.py | 22 +++++------------ gui/wxpython/image2target/ii2t_manager.py | 22 +++++------------ gui/wxpython/location_wizard/wizard.py | 18 ++------------ gui/wxpython/photo2image/ip2i_manager.py | 12 +++------ pyproject.toml | 3 +-- python/grass/script/vector.py | 3 +-- python/grass/temporal/core.py | 30 +++++++---------------- 7 files changed, 28 insertions(+), 82 deletions(-) diff --git a/gui/wxpython/gcp/manager.py b/gui/wxpython/gcp/manager.py index 692de174f55..ed55bd7c553 100644 --- a/gui/wxpython/gcp/manager.py +++ b/gui/wxpython/gcp/manager.py @@ -73,9 +73,7 @@ # # global variables # -global src_map -global tgt_map -global maptype +global maptype, src_map, tgt_map src_map = "" tgt_map = {"raster": "", "vector": ""} @@ -138,9 +136,7 @@ def __init__(self, parent, giface): # mapset for xy map to georectify self.newmapset = "" - global maptype - global src_map - global tgt_map + global maptype, src_map, tgt_map # src_map = '' # tgt_map = '' @@ -875,8 +871,7 @@ def __init__(self, wizard, parent): def OnSrcSelection(self, event): """Source map to display selected""" - global src_map - global maptype + global maptype, src_map src_map = self.srcselection.GetValue() @@ -912,8 +907,7 @@ def OnTgtVectSelection(self, event): tgt_map["vector"] = self.tgtvectselection.GetValue() def OnPageChanging(self, event=None): - global src_map - global tgt_map + global src_map, tgt_map if event.GetDirection() and (src_map == ""): GMessage( @@ -925,9 +919,7 @@ def OnPageChanging(self, event=None): self.parent.SwitchEnv("target") def OnEnterPage(self, event=None): - global maptype - global src_map - global tgt_map + global maptype, src_map, tgt_map self.srcselection.SetElementList(maptype) @@ -3369,9 +3361,7 @@ def OnExtension(self, event): self.parent.extension = self.ext_txt.GetValue() def UpdateSettings(self): - global src_map - global tgt_map - global maptype + global maptype, src_map, tgt_map layers = None diff --git a/gui/wxpython/image2target/ii2t_manager.py b/gui/wxpython/image2target/ii2t_manager.py index 19a66f01dba..39e77d9a7e4 100644 --- a/gui/wxpython/image2target/ii2t_manager.py +++ b/gui/wxpython/image2target/ii2t_manager.py @@ -79,9 +79,7 @@ # # global variables # -global src_map -global tgt_map -global maptype +global src_map, tgt_map, maptype src_map = "" tgt_map = {"raster": "", "vector": ""} @@ -178,9 +176,7 @@ def __init__(self, parent, giface): # mapset for xy map to georectify self.newmapset = "" - global maptype - global src_map - global tgt_map + global maptype, src_map, tgt_map # src_map = '' # tgt_map = '' @@ -879,8 +875,7 @@ def __init__(self, wizard, parent): def OnSrcSelection(self, event): """Source map to display selected""" - global src_map - global maptype + global src_map, maptype src_map = self.srcselection.GetValue() @@ -916,8 +911,7 @@ def OnTgtVectSelection(self, event): tgt_map["vector"] = self.tgtvectselection.GetValue() def OnPageChanging(self, event=None): - global src_map - global tgt_map + global src_map, tgt_map if event.GetDirection() and (src_map == ""): GMessage( @@ -929,9 +923,7 @@ def OnPageChanging(self, event=None): self.parent.SwitchEnv("target") def OnEnterPage(self, event=None): - global maptype - global src_map - global tgt_map + global maptype, src_map, tgt_map self.srcselection.SetElementList(maptype) @@ -3318,9 +3310,7 @@ def OnExtension(self, event): self.parent.extension = self.ext_txt.GetValue() def UpdateSettings(self): - global src_map - global tgt_map - global maptype + global src_map, tgt_map, maptype layers = None diff --git a/gui/wxpython/location_wizard/wizard.py b/gui/wxpython/location_wizard/wizard.py index 41c84d4ca70..670710a6ebf 100644 --- a/gui/wxpython/location_wizard/wizard.py +++ b/gui/wxpython/location_wizard/wizard.py @@ -75,14 +75,7 @@ from grass.script import core as grass from grass.exceptions import OpenError -global coordsys -global north -global south -global east -global west -global resolution -global wizerror -global translist +global coordsys, north, south, east, west, resolution, wizerror, translist if globalvar.CheckWxVersion(version=[4, 1, 0]): search_cancel_evt = wx.EVT_SEARCH_CANCEL @@ -2539,14 +2532,7 @@ def __init__(self, parent, grassdatabase): self.__cleanUp() def __cleanUp(self): - global coordsys - global north - global south - global east - global west - global resolution - global wizerror - global translist + global coordsys, north, south, east, west, resolution, wizerror, translist coordsys = None north = None diff --git a/gui/wxpython/photo2image/ip2i_manager.py b/gui/wxpython/photo2image/ip2i_manager.py index c3b87ac3928..c8a4eb85105 100644 --- a/gui/wxpython/photo2image/ip2i_manager.py +++ b/gui/wxpython/photo2image/ip2i_manager.py @@ -59,9 +59,7 @@ # # global variables # -global src_map -global tgt_map -global maptype +global src_map, tgt_map, maptype src_map = "" tgt_map = "" @@ -95,9 +93,7 @@ class GCPWizard: def __init__( self, parent, giface, group, raster, raster1, camera, order, extension ): - global maptype - global src_map - global tgt_map + global maptype, src_map, tgt_map maptype = "raster" rendertype = "raster" self.parent = parent # GMFrame @@ -2406,9 +2402,7 @@ def OnExtension(self, event): self.parent.extension = self.ext_txt.GetValue() def UpdateSettings(self): - global src_map - global tgt_map - global maptype + global src_map, tgt_map, maptype layers = None diff --git a/pyproject.toml b/pyproject.toml index 7d544473f60..64b7c033708 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -153,7 +153,6 @@ ignore = [ "FBT003", # boolean-positional-value-in-call "FURB118", # reimplemented-operator "FURB152", # math-constant - "FURB154", # repeated-global "I001", # unsorted-imports "ISC003", # explicit-string-concatenation "PERF203", # try-except-in-loop @@ -389,7 +388,7 @@ ignore = [ "python/grass/temporal/univar_statistics.py" = ["INT002"] "raster3d/r3.flow/testsuite/r3flow_test.py" = ["FLY002"] "raster3d/r3.gradient/testsuite/r3gradient_test.py" = ["FLY002"] -"scripts/d.polar/d.polar.py" = ["INT002"] +"scripts/d.polar/d.polar.py" = ["FURB154", "INT002"] "scripts/g.extension.all/g.extension.all.py" = ["INT002"] "scripts/g.extension/g.extension.py" = ["INT002"] "scripts/i.oif/i.oif.py" = ["INT003"] diff --git a/python/grass/script/vector.py b/python/grass/script/vector.py index 4a57b5db07b..ca3caf18471 100644 --- a/python/grass/script/vector.py +++ b/python/grass/script/vector.py @@ -430,8 +430,7 @@ def vector_what( return data # lazy import - global json - global orderedDict + global json, orderedDict if json is None: import json if orderedDict is None: diff --git a/python/grass/temporal/core.py b/python/grass/temporal/core.py index 9ae898cb536..12dafeb4fd5 100644 --- a/python/grass/temporal/core.py +++ b/python/grass/temporal/core.py @@ -454,8 +454,7 @@ def stop_subprocesses(): """Stop the messenger and C-interface subprocesses that are started by tgis.init() """ - global message_interface - global c_library_interface + global message_interface, c_library_interface if message_interface: message_interface.stop() if c_library_interface: @@ -473,8 +472,7 @@ def get_available_temporal_mapsets(): :returns: A dictionary, mapset names are keys, the tuple (driver, database) are the values """ - global c_library_interface - global message_interface + global c_library_interface, message_interface mapsets = c_library_interface.available_mapsets() @@ -562,17 +560,11 @@ def init(raise_fatal_error=False, skip_db_version_check=False): """ # We need to set the correct database backend and several global variables # from the GRASS mapset specific environment variables of g.gisenv and t.connect - global tgis_backend - global tgis_database - global tgis_database_string - global tgis_dbmi_paramstyle - global tgis_db_version - global raise_on_error - global enable_mapset_check - global enable_timestamp_write - global current_mapset - global current_location - global current_gisdbase + global tgis_backend, tgis_database, tgis_database_string # noqa: FURB154 + global tgis_dbmi_paramstyle, tgis_db_version # noqa: FURB154 + global raise_on_error # noqa: FURB154 + global enable_mapset_check, enable_timestamp_write # noqa: FURB154 + global current_mapset, current_location, current_gisdbase # noqa: FURB154 raise_on_error = raise_fatal_error @@ -846,10 +838,7 @@ def create_temporal_database(dbif): :param dbif: The database interface to be used """ - global tgis_backend - global tgis_version - global tgis_db_version - global tgis_database_string + global tgis_backend, tgis_version, tgis_db_version, tgis_database_string template_path = get_sql_template_path() msgr = get_tgis_message_interface() @@ -977,8 +966,7 @@ def upgrade_temporal_database(dbif): :param dbif: The database interface to be used """ - global tgis_database_string - global tgis_db_version + global tgis_database_string, tgis_db_version metadata = get_tgis_metadata(dbif) From 1936cf0d6be1d6eed8f42a774890ed76191eb387 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Sun, 14 Jul 2024 09:14:48 -0400 Subject: [PATCH 028/471] style: Fix math-constant (FURB152) (#4049) * style: Fix math-constant (FURB152) Ruff rule: https://docs.astral.sh/ruff/rules/math-constant/ * style: Fix math-constant (FURB152) in lib/imagery/testsuite/test_imagery_sigsetfile.py * style: Fix math-constant (FURB152) Ruff rule: https://docs.astral.sh/ruff/rules/math-constant/ * Revert "style: Fix math-constant (FURB152) in lib/imagery/testsuite/test_imagery_sigsetfile.py" * style: Ignore math-constant (FURB152) in lib/imagery/testsuite/test_imagery_sigsetfile.py --- display/d.text/test.py | 4 ++-- pyproject.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/display/d.text/test.py b/display/d.text/test.py index c68449ed7be..6e7fe676a65 100755 --- a/display/d.text/test.py +++ b/display/d.text/test.py @@ -56,8 +56,8 @@ def text(in_text): rotate(i * 10) color(colors[i % len(colors)]) xy( - (80 + 10 * math.cos(i * 10 / 180 * 3.141593)), - (50 + 10 * 640 / 480 * math.sin(i * 10 / 180 * 3.141593)), + (80 + 10 * math.cos(i * 10 / 180 * math.pi)), + (50 + 10 * 640 / 480 * math.sin(i * 10 / 180 * math.pi)), ) text(fonts[int(i % len(fonts))]) diff --git a/pyproject.toml b/pyproject.toml index 64b7c033708..b4ff3583cf4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -152,7 +152,6 @@ ignore = [ "FBT002", # boolean-default-value-positional-argument "FBT003", # boolean-positional-value-in-call "FURB118", # reimplemented-operator - "FURB152", # math-constant "I001", # unsorted-imports "ISC003", # explicit-string-concatenation "PERF203", # try-except-in-loop @@ -354,6 +353,7 @@ ignore = [ "gui/wxpython/web_services/widgets.py" = ["INT003"] "gui/wxpython/wxgui.py" = ["INT002"] "gui/wxpython/wxplot/profile.py" = ["NPY001"] +"lib/imagery/testsuite/test_imagery_sigsetfile.py" = ["FURB152"] "lib/init/grass.py" = ["INT003"] "python/grass/__init__.py" = ["PYI056"] "python/grass/exp*/tests/grass_script_mapset_session_test.py" = ["SIM117"] From 951b71b3559b737cfc62d4e4b1e6003580360d27 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 15 Jul 2024 05:13:18 -0400 Subject: [PATCH 029/471] CI(deps): Lock file maintenance (#4057) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 2237ffc975b..c30f9b90775 100644 --- a/flake.lock +++ b/flake.lock @@ -19,11 +19,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1720571246, - "narHash": "sha256-nkUXwunTck+hNMt2wZuYRN+jf2ySRjKTzI0fo5TDH78=", + "lastModified": 1720955038, + "narHash": "sha256-GaliJqfFwyYxReFywxAa8orCO+EnDq2NK2F+5aSc8vo=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "16e401f01842c5bb2499e78c1fe227f939c0c474", + "rev": "aa247c0c90ecf4ae7a032c54fdc21b91ca274062", "type": "github" }, "original": { From 793cc1bd9298d062aae9504f39cd147f8370d7fe Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 15 Jul 2024 12:53:23 -0400 Subject: [PATCH 030/471] CI(deps): Update docker/build-push-action action to v6.4.0 (#4059) --- .github/workflows/docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index a10ec7545a0..ddf1fdb07d9 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -76,7 +76,7 @@ jobs: password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build and push id: docker_build - uses: docker/build-push-action@1a162644f9a7e87d8f4b053101d1d9a712edc18c # v6.3.0 + uses: docker/build-push-action@a254f8ca60a858f3136a2f1f23a60969f2c402dd # v6.4.0 with: push: true pull: true From 2d055ab4b4f6c755ab605c71c0a3b86b6de69ad1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Mon, 15 Jul 2024 16:36:58 -0400 Subject: [PATCH 031/471] CI: Configure Renovate update groups (#4053) * CI: Configure Renovate update groups * CI: Use Renovate preset "group:githubArtifactActions" instead --- renovate.json5 | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/renovate.json5 b/renovate.json5 index 76c6cf5cf13..b5c0aeba9b8 100644 --- a/renovate.json5 +++ b/renovate.json5 @@ -22,10 +22,34 @@ // weekly update of lock files (flake.lock) ":maintainLockFilesWeekly", + "group:githubArtifactActions", ], // enable Nix lock file update (flake.lock) "nix": { - "enabled": true - } + "enabled": true, + }, + + "packageRules": [ + { + "groupName": "ruff", + "matchPackageNames": [ + "astral-sh/ruff", + "astral-sh/ruff-pre-commit", + "ruff", + ], + }, + { + "groupName": "flake8", + "matchPackageNames": ["pycqa/flake8", "flake8"], + }, + { + "groupName": "black", + "matchPackageNames": [ + "psf/black-pre-commit-mirror", + "psf/black", + "black", + ], + }, + ], } From f3681c5958d1f536c9514ee097b445ecfa94fdea Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 15 Jul 2024 21:59:37 +0000 Subject: [PATCH 032/471] CI(deps): Update ruff to v0.5.2 (#4061) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/python-code-quality.yml | 2 +- .pre-commit-config.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index 581cfaddd35..81f74056f9d 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -36,7 +36,7 @@ jobs: # renovate: datasource=pypi depName=bandit BANDIT_VERSION: "1.7.9" # renovate: datasource=pypi depName=ruff - RUFF_VERSION: "0.5.1" + RUFF_VERSION: "0.5.2" runs-on: ${{ matrix.os }} permissions: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8146d2b1a9c..9843a6f6616 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -37,7 +37,7 @@ repos: ) - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.5.1 + rev: v0.5.2 hooks: # Run the linter. - id: ruff From f0497c5f6c0470d0704d7551710175839879a12a Mon Sep 17 00:00:00 2001 From: Stefan Blumentrath Date: Tue, 16 Jul 2024 02:14:27 +0200 Subject: [PATCH 033/471] style: fix numpy-deprecation-rules (NPY) (#4062) Fix the last cases of ruff rules: * NPY001 * NPY002 --- gui/wxpython/wxplot/profile.py | 2 +- pyproject.toml | 6 +----- python/grass/pygrass/raster/__init__.py | 16 ++++------------ .../grass/pygrass/raster/testsuite/test_numpy.py | 6 +++--- .../grass/pygrass/vector/testsuite/test_table.py | 7 ++++--- .../pygrass/vector/testsuite/test_vector3d.py | 7 ++++--- 6 files changed, 17 insertions(+), 27 deletions(-) diff --git a/gui/wxpython/wxplot/profile.py b/gui/wxpython/wxplot/profile.py index 1941022fb59..1e79379b08b 100644 --- a/gui/wxpython/wxplot/profile.py +++ b/gui/wxpython/wxplot/profile.py @@ -472,7 +472,7 @@ def OnStats(self, event): statstr = "Profile of %s\n\n" % rast iterable = (i[1] for i in self.raster[r]["datalist"]) - a = np.fromiter(iterable, np.float) + a = np.fromiter(iterable, float) statstr += "n: %f\n" % a.size statstr += "minimum: %f\n" % np.amin(a) diff --git a/pyproject.toml b/pyproject.toml index b4ff3583cf4..b9739df49c9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -352,7 +352,6 @@ ignore = [ "gui/wxpython/web_services/dialogs.py" = ["INT003"] "gui/wxpython/web_services/widgets.py" = ["INT003"] "gui/wxpython/wxgui.py" = ["INT002"] -"gui/wxpython/wxplot/profile.py" = ["NPY001"] "lib/imagery/testsuite/test_imagery_sigsetfile.py" = ["FURB152"] "lib/init/grass.py" = ["INT003"] "python/grass/__init__.py" = ["PYI056"] @@ -365,14 +364,11 @@ ignore = [ "python/grass/jupyter/tests/reprojection_renderer_test.py" = ["PT013"] "python/grass/jupyter/testsuite/interactivemap_test.py" = ["PGH004"] "python/grass/jupyter/testsuite/map_test.py" = ["PGH004"] -"python/grass/pygrass/raster/__init__.py" = ["NPY002"] "python/grass/pygrass/raster/category.py" = ["INT002"] -"python/grass/pygrass/raster/testsuite/test_numpy.py" = ["NPY002"] "python/grass/pygrass/vector/__init__.py" = ["INT003"] "python/grass/pygrass/vector/geometry.py" = ["PYI024"] "python/grass/pygrass/vector/sql.py" = ["FLY002"] -"python/grass/pygrass/vector/testsuite/test_table.py" = ["NPY002", "PLW0108"] -"python/grass/pygrass/vector/testsuite/test_vector3d.py" = ["NPY002"] +"python/grass/pygrass/vector/testsuite/test_table.py" = ["PLW0108"] "python/grass/script/raster.py" = ["INT003"] "python/grass/temporal/abstract_space_time_dataset.py" = ["INT003"] "python/grass/temporal/aggregation.py" = ["INT003"] diff --git a/python/grass/pygrass/raster/__init__.py b/python/grass/pygrass/raster/__init__.py index 818d9576514..0356513e017 100644 --- a/python/grass/pygrass/raster/__init__.py +++ b/python/grass/pygrass/raster/__init__.py @@ -580,15 +580,11 @@ def close(self, rm_temp_files=True): def random_map_only_columns(mapname, mtype, overwrite=True, factor=100): region = Region() random_map = RasterRow(mapname) + rng = np.random.default_rng() row_buf = Buffer( (region.cols,), mtype, - buffer=( - np.random.random( - region.cols, - ) - * factor - ).data, + buffer=(rng.random(region.cols) * factor).data, ) random_map.open("w", mtype, overwrite) for _ in range(region.rows): @@ -601,16 +597,12 @@ def random_map(mapname, mtype, overwrite=True, factor=100): region = Region() random_map = RasterRow(mapname) random_map.open("w", mtype, overwrite) + rng = np.random.default_rng() for _ in range(region.rows): row_buf = Buffer( (region.cols,), mtype, - buffer=( - np.random.random( - region.cols, - ) - * factor - ).data, + buffer=(rng.random(region.cols) * factor).data, ) random_map.put_row(row_buf) random_map.close() diff --git a/python/grass/pygrass/raster/testsuite/test_numpy.py b/python/grass/pygrass/raster/testsuite/test_numpy.py index a2a6e1cdd26..5f0b2309544 100644 --- a/python/grass/pygrass/raster/testsuite/test_numpy.py +++ b/python/grass/pygrass/raster/testsuite/test_numpy.py @@ -6,7 +6,7 @@ from grass.gunittest.case import TestCase from grass.gunittest.main import test -from numpy.random import random +from numpy.random import default_rng from grass.pygrass.raster import raster2numpy, numpy2raster, RasterRow @@ -49,8 +49,8 @@ def test_len(self): self.assertTrue(len(self.numpy_obj[0]), 60) def test_write(self): - ran = random([40, 60]) - numpy2raster(ran, "FCELL", self.name, True) + rng = default_rng() + numpy2raster(rng.random([40, 60]), "FCELL", self.name, True) self.assertTrue(check_raster(self.name)) diff --git a/python/grass/pygrass/vector/testsuite/test_table.py b/python/grass/pygrass/vector/testsuite/test_table.py index d603817de98..1acb410a10f 100644 --- a/python/grass/pygrass/vector/testsuite/test_table.py +++ b/python/grass/pygrass/vector/testsuite/test_table.py @@ -18,11 +18,12 @@ # dictionary that generate random data +RNG = np.random.default_rng() COL2VALS = { - "INT": lambda n: np.random.randint(9, size=n), - "INTEGER": lambda n: np.random.randint(9, size=n), + "INT": lambda n: RNG.integers(low=0, high=9, size=n), + "INTEGER": lambda n: RNG.integers(low=0, high=9, size=n), "INTEGER PRIMARY KEY": lambda n: np.arange(1, n + 1, dtype=int), - "REAL": lambda n: np.random.rand(n), + "REAL": lambda n: RNG.random(n), "TEXT": lambda n: np.array([randstr() for _ in range(n)]), } diff --git a/python/grass/pygrass/vector/testsuite/test_vector3d.py b/python/grass/pygrass/vector/testsuite/test_vector3d.py index 16ee0fd969e..796f85ca529 100644 --- a/python/grass/pygrass/vector/testsuite/test_vector3d.py +++ b/python/grass/pygrass/vector/testsuite/test_vector3d.py @@ -19,11 +19,12 @@ def generate_coordinates(number, bbox=None, with_z=False): """Return 2 or 3 random arrays of coordinates""" + rng = np.random.default_rng() bbox = Region() if bbox is None else bbox - x = bbox.south + (bbox.north - bbox.south) * np.random.random(number) - y = bbox.west + (bbox.east - bbox.west) * np.random.random(number) + x = bbox.south + (bbox.north - bbox.south) * rng.random(number) + y = bbox.west + (bbox.east - bbox.west) * rng.random(number) if with_z: - z = np.random.random(number) * 1000 + z = rng.random(number) * 1000 return x, y, z return x, y From 692e330ecb8c074001d5380a8ada6be00881eb7c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 17 Jul 2024 07:07:04 -0400 Subject: [PATCH 034/471] CI(deps): Update docker/build-push-action action to v6.4.1 (#4064) --- .github/workflows/docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index ddf1fdb07d9..89c3baec8c6 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -76,7 +76,7 @@ jobs: password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build and push id: docker_build - uses: docker/build-push-action@a254f8ca60a858f3136a2f1f23a60969f2c402dd # v6.4.0 + uses: docker/build-push-action@1ca370b3a9802c92e886402e0dd88098a2533b12 # v6.4.1 with: push: true pull: true From f99780c11f8d4d1633e469fb32da2b6c75852b4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Wed, 17 Jul 2024 12:06:34 -0400 Subject: [PATCH 035/471] style: Fix collapsible-else-if (PLR5501) (#4045) * style: Fix collapsible-else-if (PLR5501) Ruff rule: https://docs.astral.sh/ruff/rules/collapsible-else-if/ * style: Fix collapsible-else-if (PLR5501) Ruff rule: https://docs.astral.sh/ruff/rules/collapsible-else-if/ * style: Fix collapsible-else-if (PLR5501) Ruff rule: https://docs.astral.sh/ruff/rules/collapsible-else-if/ * Apply suggestions from code review Co-authored-by: Stefan Blumentrath * style: Combine if conditions in wxpython/gmodeler/model.py * style: Simplify if conditions in wxpython/gui_core --------- Co-authored-by: Stefan Blumentrath --- gui/wxpython/animation/anim.py | 2 +- gui/wxpython/animation/controller.py | 25 +- gui/wxpython/animation/dialogs.py | 10 +- gui/wxpython/animation/frame.py | 2 +- gui/wxpython/animation/nviztask.py | 29 +-- gui/wxpython/animation/temporal_manager.py | 23 +- gui/wxpython/core/globalvar.py | 9 +- gui/wxpython/core/render.py | 15 +- gui/wxpython/core/settings.py | 33 +-- gui/wxpython/core/toolboxes.py | 5 +- gui/wxpython/core/treemodel.py | 5 +- gui/wxpython/core/units.py | 2 +- gui/wxpython/datacatalog/tree.py | 33 ++- gui/wxpython/dbmgr/base.py | 7 +- gui/wxpython/dbmgr/dialogs.py | 16 +- gui/wxpython/dbmgr/vinfo.py | 7 +- gui/wxpython/gcp/manager.py | 9 +- gui/wxpython/gmodeler/model.py | 15 +- gui/wxpython/gui_core/dialogs.py | 2 +- gui/wxpython/gui_core/forms.py | 65 +++-- gui/wxpython/gui_core/goutput.py | 5 +- gui/wxpython/gui_core/gselect.py | 164 ++++++------ gui/wxpython/gui_core/mapdisp.py | 2 +- gui/wxpython/gui_core/pyedit.py | 17 +- gui/wxpython/gui_core/pystc.py | 12 +- gui/wxpython/gui_core/toolbars.py | 11 +- gui/wxpython/gui_core/wrap.py | 2 +- gui/wxpython/history/tree.py | 40 ++- gui/wxpython/iclass/digit.py | 2 +- gui/wxpython/iclass/frame.py | 5 +- gui/wxpython/icons/icon.py | 7 +- gui/wxpython/image2target/ii2t_manager.py | 9 +- gui/wxpython/lmgr/layertree.py | 104 ++++---- gui/wxpython/location_wizard/wizard.py | 45 ++-- gui/wxpython/mapdisp/statusbar.py | 67 +++-- gui/wxpython/mapswipe/frame.py | 11 +- gui/wxpython/mapwin/decorations.py | 14 +- gui/wxpython/modules/colorrules.py | 23 +- gui/wxpython/modules/extensions.py | 5 +- gui/wxpython/nviz/mapwindow.py | 33 ++- gui/wxpython/nviz/tools.py | 72 +++--- gui/wxpython/photo2image/ip2i_manager.py | 4 +- gui/wxpython/psmap/dialogs.py | 20 +- gui/wxpython/psmap/frame.py | 11 +- gui/wxpython/psmap/instructions.py | 2 +- gui/wxpython/rdigit/g.gui.rdigit.py | 31 ++- gui/wxpython/startup/guiutils.py | 2 +- gui/wxpython/tplot/frame.py | 7 +- gui/wxpython/vdigit/mapwindow.py | 77 +++--- gui/wxpython/vdigit/preferences.py | 20 +- gui/wxpython/vdigit/toolbars.py | 10 +- gui/wxpython/vdigit/wxdisplay.py | 102 ++++---- gui/wxpython/vnet/dialogs.py | 2 +- gui/wxpython/vnet/vnet_data.py | 13 +- gui/wxpython/wxplot/dialogs.py | 5 +- gui/wxpython/wxplot/histogram.py | 7 +- lib/init/grass.py | 234 +++++++++--------- man/parser_standard_options.py | 23 +- pyproject.toml | 1 - python/grass/gunittest/checkers.py | 11 +- python/grass/gunittest/invoker.py | 7 +- python/grass/jupyter/baseseriesmap.py | 9 +- python/grass/jupyter/region.py | 17 +- python/grass/pygrass/utils.py | 2 +- python/grass/pygrass/vector/geometry.py | 15 +- python/grass/script/core.py | 24 +- python/grass/script/task.py | 5 +- python/grass/semantic_label/reader.py | 2 +- python/grass/temporal/abstract_map_dataset.py | 16 +- .../temporal/abstract_space_time_dataset.py | 2 +- python/grass/temporal/base.py | 11 +- python/grass/temporal/core.py | 23 +- python/grass/temporal/datetime_math.py | 2 +- python/grass/temporal/list_stds.py | 9 +- python/grass/temporal/temporal_algebra.py | 12 +- python/grass/temporal/temporal_granularity.py | 8 +- .../temporal/temporal_raster_base_algebra.py | 5 +- scripts/d.frame/d.frame.py | 27 +- scripts/g.extension/g.extension.py | 41 ++- scripts/m.proj/m.proj.py | 17 +- scripts/r.semantic.label/r.semantic.label.py | 5 +- .../v.db.reconnect.all/v.db.reconnect.all.py | 17 +- scripts/v.in.lines/v.in.lines.py | 2 +- scripts/v.report/v.report.py | 18 +- temporal/t.rast.accdetect/t.rast.accdetect.py | 2 +- temporal/t.rast.aggregate/t.rast.aggregate.py | 2 +- temporal/t.rast.gapfill/t.rast.gapfill.py | 5 +- temporal/t.vect.db.select/t.vect.db.select.py | 31 ++- utils/gitlog2changelog.py | 7 +- utils/mkhtml.py | 24 +- 90 files changed, 871 insertions(+), 1042 deletions(-) diff --git a/gui/wxpython/animation/anim.py b/gui/wxpython/animation/anim.py index 1ac3140d36e..591bbff151c 100644 --- a/gui/wxpython/animation/anim.py +++ b/gui/wxpython/animation/anim.py @@ -119,7 +119,7 @@ def _arrivedToEnd(self): self.orientation = Orientation.BACKWARD self.currentIndex = self.count - 2 # -1 self.callbackOrientationChanged(Orientation.BACKWARD) - else: + else: # noqa: PLR5501 if self.replayMode == ReplayMode.REPEAT: self.currentIndex = self.count - 1 elif self.replayMode == ReplayMode.REVERSE: diff --git a/gui/wxpython/animation/controller.py b/gui/wxpython/animation/controller.py index abb5ef202ea..1a920f77de7 100644 --- a/gui/wxpython/animation/controller.py +++ b/gui/wxpython/animation/controller.py @@ -120,7 +120,7 @@ def PauseAnimation(self, paused): if self.timer.IsRunning(): self.timer.Stop() self.DisableSliderIfNeeded() - else: + else: # noqa: PLR5501 if not self.timer.IsRunning(): self.timer.Start(int(self.timeTick)) self.DisableSliderIfNeeded() @@ -555,9 +555,8 @@ def _export(self, exportInfo, decorations): if frameId is not None: bitmap = self.bitmapProvider.GetBitmap(frameId) lastBitmaps[i] = bitmap - else: - if i not in lastBitmaps: - lastBitmaps[i] = wx.NullBitmap() + elif i not in lastBitmaps: + lastBitmaps[i] = wx.NullBitmap() else: bitmap = self.bitmapProvider.GetBitmap(frameId) lastBitmaps[i] = bitmap @@ -593,17 +592,15 @@ def _export(self, exportInfo, decorations): "dash": "\u2013", "to": timeLabel[1], } + elif ( + self.temporalManager.GetTemporalType() == TemporalType.ABSOLUTE + ): + text = timeLabel[0] else: - if ( - self.temporalManager.GetTemporalType() - == TemporalType.ABSOLUTE - ): - text = timeLabel[0] - else: - text = _("%(start)s %(unit)s") % { - "start": timeLabel[0], - "unit": timeLabel[2], - } + text = _("%(start)s %(unit)s") % { + "start": timeLabel[0], + "unit": timeLabel[2], + } decImage = RenderText( text, decoration["font"], bgcolor, fgcolor diff --git a/gui/wxpython/animation/dialogs.py b/gui/wxpython/animation/dialogs.py index d9130d41e3e..91b67429858 100644 --- a/gui/wxpython/animation/dialogs.py +++ b/gui/wxpython/animation/dialogs.py @@ -671,9 +671,8 @@ def GetOptData(self, dcmd, layer, params, propwin): if not self.legend.IsChecked(): self.legend.SetValue(True) - else: - if not self._tmpLegendCmd and not self.animationData.legendCmd: - self.legend.SetValue(False) + elif not self._tmpLegendCmd and not self.animationData.legendCmd: + self.legend.SetValue(False) def _update(self): if self.nDChoice.GetSelection() == 1 and len(self._layerList) > 1: @@ -1623,9 +1622,8 @@ def SetStdsProperties(self, layer): else: signal = self.cmdChanged signal.emit(index=self._layerList.GetLayerIndex(layer), layer=layer) - else: - if hidden: - self._layerList.RemoveLayer(layer) + elif hidden: + self._layerList.RemoveLayer(layer) dlg.Destroy() self._update() self.anyChange.emit() diff --git a/gui/wxpython/animation/frame.py b/gui/wxpython/animation/frame.py index 1cf3b419b74..cec08546366 100644 --- a/gui/wxpython/animation/frame.py +++ b/gui/wxpython/animation/frame.py @@ -642,7 +642,7 @@ def _updateFrameIndex(self, index): } else: label = _("to %(to)s") % {"to": self.timeLabels[index][1]} - else: + else: # noqa: PLR5501 if self.temporalType == TemporalType.ABSOLUTE: label = start else: diff --git a/gui/wxpython/animation/nviztask.py b/gui/wxpython/animation/nviztask.py index fb3dfe501a1..92a6e767ce0 100644 --- a/gui/wxpython/animation/nviztask.py +++ b/gui/wxpython/animation/nviztask.py @@ -111,11 +111,10 @@ def _processSurface(self, surface, mapName): mapname = surface["attribute"][attr]["value"] else: const = surface["attribute"][attr]["value"] - else: - if attr == "transp": - const = 0 - elif attr == "color": - mapname = mapName + elif attr == "transp": + const = 0 + elif attr == "color": + mapname = mapName if mapname: self._setMultiTaskParam(params[0], mapname) @@ -194,21 +193,19 @@ def _processVolume(self, volume, mapName): mapname = isosurface[attr]["value"] else: const = float(isosurface[attr]["value"]) - else: - if attr == "transp": - const = 0 - elif attr == "color": - mapname = mapName + elif attr == "transp": + const = 0 + elif attr == "color": + mapname = mapName if mapname: self._setMultiTaskParam(params[0], mapname) + elif attr == "topo": + # TODO: we just assume it's the first volume, what + # to do else? + self._setMultiTaskParam(params[1], "1:" + str(const)) else: - if attr == "topo": - # TODO: we just assume it's the first volume, what - # to do else? - self._setMultiTaskParam(params[1], "1:" + str(const)) - else: - self._setMultiTaskParam(params[1], const) + self._setMultiTaskParam(params[1], const) if isosurface["inout"]["value"]: self.task.set_flag("n", True) # slices diff --git a/gui/wxpython/animation/temporal_manager.py b/gui/wxpython/animation/temporal_manager.py index 2e7b14cefa5..2273ccfd9d6 100644 --- a/gui/wxpython/animation/temporal_manager.py +++ b/gui/wxpython/animation/temporal_manager.py @@ -295,20 +295,19 @@ def _getLabelsAndMaps(self, timeseries): # map exists, stop point mode listOfMaps.append(series) afterPoint = False - else: + elif afterPoint: # check point mode - if afterPoint: - if followsPoint: - # skip this one, already there - followsPoint = False - continue - else: - # append the last one (of point time) - listOfMaps.append(lastTimeseries) - end = None + if followsPoint: + # skip this one, already there + followsPoint = False + continue else: - # append series which is None - listOfMaps.append(series) + # append the last one (of point time) + listOfMaps.append(lastTimeseries) + end = None + else: + # append series which is None + listOfMaps.append(series) timeLabels.append((start, end, unit)) return timeLabels, listOfMaps diff --git a/gui/wxpython/core/globalvar.py b/gui/wxpython/core/globalvar.py index 74138058ee6..60fdf7077ba 100644 --- a/gui/wxpython/core/globalvar.py +++ b/gui/wxpython/core/globalvar.py @@ -230,11 +230,10 @@ def UpdateGRASSAddOnCommands(eList=None): and name not in grassScripts[ext] ): grassScripts[ext].append(name) - else: - if fname not in grassCmd: - grassCmd.add(fname) - Debug.msg(3, "AddOn commands: %s", fname) - nCmd += 1 + elif fname not in grassCmd: + grassCmd.add(fname) + Debug.msg(3, "AddOn commands: %s", fname) + nCmd += 1 Debug.msg(1, "Number of GRASS AddOn commands: %d", nCmd) diff --git a/gui/wxpython/core/render.py b/gui/wxpython/core/render.py index 2021f491fc1..6e74ade9dc1 100644 --- a/gui/wxpython/core/render.py +++ b/gui/wxpython/core/render.py @@ -264,14 +264,13 @@ def GetName(self, fullyQualified=True): """ if fullyQualified: return self.name - else: - if "@" in self.name: - return { - "name": self.name.split("@")[0], - "mapset": self.name.split("@")[1], - } - else: - return {"name": self.name, "mapset": ""} + + if "@" in self.name: + return { + "name": self.name.split("@")[0], + "mapset": self.name.split("@")[1], + } + return {"name": self.name, "mapset": ""} def GetRenderedSize(self): """Get currently rendered size of layer as tuple, None if not rendered""" diff --git a/gui/wxpython/core/settings.py b/gui/wxpython/core/settings.py index 271c5260cae..45620220aed 100644 --- a/gui/wxpython/core/settings.py +++ b/gui/wxpython/core/settings.py @@ -1032,7 +1032,7 @@ def _parseValue(self, value, read=False): value = float(value) except ValueError: pass - else: # -> write settings + else: # -> write settings # noqa: PLR5501 if isinstance(value, type(())): # -> color value = str(value[0]) + ":" + str(value[1]) + ":" + str(value[2]) @@ -1062,13 +1062,13 @@ def Get(self, group, key=None, subkey=None, settings_type="user"): if subkey is None: if key is None: return settings[group] - else: - return settings[group][key] - else: - if isinstance(subkey, tuple) or isinstance(subkey, list): - return settings[group][key][subkey[0]][subkey[1]] - else: - return settings[group][key][subkey] + + return settings[group][key] + + if isinstance(subkey, (list, tuple)): + return settings[group][key][subkey[0]][subkey[1]] + + return settings[group][key][subkey] except KeyError: print( @@ -1099,13 +1099,16 @@ def Set(self, group, value, key=None, subkey=None, settings_type="user"): if subkey is None: if key is None: settings[group] = value - else: - settings[group][key] = value - else: - if isinstance(subkey, tuple) or isinstance(subkey, list): - settings[group][key][subkey[0]][subkey[1]] = value - else: - settings[group][key][subkey] = value + return + settings[group][key] = value + return + + if isinstance(subkey, (list, tuple)): + settings[group][key][subkey[0]][subkey[1]] = value + return + settings[group][key][subkey] = value + return + except KeyError: raise GException( "%s '%s:%s:%s'" % (_("Unable to set "), group, key, subkey) diff --git a/gui/wxpython/core/toolboxes.py b/gui/wxpython/core/toolboxes.py index 798df081e06..2fca91f8a90 100644 --- a/gui/wxpython/core/toolboxes.py +++ b/gui/wxpython/core/toolboxes.py @@ -347,9 +347,8 @@ def _indent(elem, level=0): _indent(_elem, level + 1) if not elem.tail or not elem.tail.strip(): elem.tail = i - else: - if level and (not elem.tail or not elem.tail.strip()): - elem.tail = i + elif level and (not elem.tail or not elem.tail.strip()): + elem.tail = i def expandAddons(tree): diff --git a/gui/wxpython/core/treemodel.py b/gui/wxpython/core/treemodel.py index c891dafe25c..3bcbd6c6ae2 100644 --- a/gui/wxpython/core/treemodel.py +++ b/gui/wxpython/core/treemodel.py @@ -322,11 +322,10 @@ def match(self, key, value, case_sensitive=False): # start supported but unused, so testing last if value in text or value == "*": return True - else: + elif value.lower() in text.lower() or value == "*": # this works fully only for English and requires accents # to be exact match (even Python 3 casefold() does not help) - if value.lower() in text.lower() or value == "*": - return True + return True return False diff --git a/gui/wxpython/core/units.py b/gui/wxpython/core/units.py index 3947d205760..46b8f91266b 100644 --- a/gui/wxpython/core/units.py +++ b/gui/wxpython/core/units.py @@ -110,7 +110,7 @@ def ConvertValue(value, type, units): f = 6.21371192237334e-4 elif units == "ft": f = 3.28083989501312 - else: # -> area + else: # -> area # noqa: PLR5501 if units == "me": f = 1.0 elif units == "km": diff --git a/gui/wxpython/datacatalog/tree.py b/gui/wxpython/datacatalog/tree.py index 6d5530733d1..9ea22c1c327 100644 --- a/gui/wxpython/datacatalog/tree.py +++ b/gui/wxpython/datacatalog/tree.py @@ -1293,25 +1293,24 @@ def OnPasteMap(self, event): if not new_name: return # within one location, different mapsets - else: - if map_exists( - new_name, - element=self.copy_layer[i].data["type"], + elif map_exists( + new_name, + element=self.copy_layer[i].data["type"], + env=env, + mapset=self.selected_mapset[0].data["name"], + ): + new_name = self._getNewMapName( + _("New name for <{n}>").format( + n=self.copy_layer[i].data["name"] + ), + _("Select new name"), + self.copy_layer[i].data["name"], env=env, mapset=self.selected_mapset[0].data["name"], - ): - new_name = self._getNewMapName( - _("New name for <{n}>").format( - n=self.copy_layer[i].data["name"] - ), - _("Select new name"), - self.copy_layer[i].data["name"], - env=env, - mapset=self.selected_mapset[0].data["name"], - element=self.copy_layer[i].data["type"], - ) - if not new_name: - return + element=self.copy_layer[i].data["type"], + ) + if not new_name: + return string = ( self.copy_layer[i].data["name"] diff --git a/gui/wxpython/dbmgr/base.py b/gui/wxpython/dbmgr/base.py index abef5b8daec..5aa5011ce9a 100644 --- a/gui/wxpython/dbmgr/base.py +++ b/gui/wxpython/dbmgr/base.py @@ -2165,11 +2165,10 @@ def OnApplySqlStatement(self, event): # sort by key column if sql and "order by" in sql.lower(): pass # don't order by key column + elif keyColumn > -1: + listWin.SortListItems(col=keyColumn, ascending=True) else: - if keyColumn > -1: - listWin.SortListItems(col=keyColumn, ascending=True) - else: - listWin.SortListItems(col=0, ascending=True) + listWin.SortListItems(col=0, ascending=True) wx.EndBusyCursor() diff --git a/gui/wxpython/dbmgr/dialogs.py b/gui/wxpython/dbmgr/dialogs.py index 5512ca7349c..c5b63d0fe13 100644 --- a/gui/wxpython/dbmgr/dialogs.py +++ b/gui/wxpython/dbmgr/dialogs.py @@ -250,21 +250,19 @@ def GetSQLString(self, updateValues=False): ) sqlCommands.append(None) continue - else: - if self.action == "add": - continue + elif self.action == "add": + continue if newvalue != value: updatedColumns.append(name) if newvalue == "": updatedValues.append("NULL") + elif ctype != str: + updatedValues.append(str(newvalue)) else: - if ctype != str: - updatedValues.append(str(newvalue)) - else: - updatedValues.append( - "'" + newvalue.replace("'", "''") + "'" - ) + updatedValues.append( + "'" + newvalue.replace("'", "''") + "'" + ) columns[name]["values"][idx] = newvalue if self.action != "add" and len(updatedValues) == 0: diff --git a/gui/wxpython/dbmgr/vinfo.py b/gui/wxpython/dbmgr/vinfo.py index 7788a4e6576..afb0a716063 100644 --- a/gui/wxpython/dbmgr/vinfo.py +++ b/gui/wxpython/dbmgr/vinfo.py @@ -136,11 +136,10 @@ def SelectByPoint(self, queryCoords, qdist): for key, value in record["Attributes"].items(): if len(value) < 1: value = None + elif self.tables[table][key]["ctype"] != str: + value = self.tables[table][key]["ctype"](value) else: - if self.tables[table][key]["ctype"] != str: - value = self.tables[table][key]["ctype"](value) - else: - value = GetUnicodeValue(value) + value = GetUnicodeValue(value) self.tables[table][key]["values"].append(value) for key, value in record.items(): diff --git a/gui/wxpython/gcp/manager.py b/gui/wxpython/gcp/manager.py index ed55bd7c553..afc3b239037 100644 --- a/gui/wxpython/gcp/manager.py +++ b/gui/wxpython/gcp/manager.py @@ -1452,11 +1452,10 @@ def SetGCPSatus(self, item, itemIndex): wxPen = "highest" else: wxPen = "default" + elif self.mapcoordlist[key][5] > self.rmsthresh: + wxPen = "highest" else: - if self.mapcoordlist[key][5] > self.rmsthresh: - wxPen = "highest" - else: - wxPen = "default" + wxPen = "default" if itemIndex == self.list.selectedkey: wxPen = "selected" @@ -3538,7 +3537,7 @@ def UpdateSettings(self): self.parent.activemap.SetSelection(0) self.parent.activemap.Enable(False) self.parent.GetMapToolbar().Enable("zoommenu", enable=False) - else: + else: # noqa: PLR5501 if not self.parent.show_target: self.parent.show_target = True self.parent._mgr.GetPane("target").Show() diff --git a/gui/wxpython/gmodeler/model.py b/gui/wxpython/gmodeler/model.py index f0599b70d61..72e1e0419d3 100644 --- a/gui/wxpython/gmodeler/model.py +++ b/gui/wxpython/gmodeler/model.py @@ -947,12 +947,10 @@ def GetRelations(self, fdir=None): result = [] for rel in self.rels: - if fdir == "from": - if rel.GetFrom() == self: - result.append(rel) - else: - if rel.GetTo() == self: - result.append(rel) + if fdir == "from" and rel.GetFrom() == self: + result.append(rel) + elif rel.GetTo() == self: + result.append(rel) return result @@ -1040,10 +1038,7 @@ def __init__( if cmd: self.task = GUI(show=None).ParseCommand(cmd=cmd) else: - if task: - self.task = task - else: - self.task = None + self.task = task or None self.propWin = None diff --git a/gui/wxpython/gui_core/dialogs.py b/gui/wxpython/gui_core/dialogs.py index 9a19000e6e4..05bb71ed7fa 100644 --- a/gui/wxpython/gui_core/dialogs.py +++ b/gui/wxpython/gui_core/dialogs.py @@ -1330,7 +1330,7 @@ def ShowResult(self, group, returnCode, create): label = _("Group <%s> was successfully created.") % group else: label = _("Group <%s> was successfully changed.") % group - else: + else: # noqa: PLR5501 if create: label = _("Creating of new group <%s> failed.") % group else: diff --git a/gui/wxpython/gui_core/forms.py b/gui/wxpython/gui_core/forms.py index 1202d699eeb..89aa24c5844 100644 --- a/gui/wxpython/gui_core/forms.py +++ b/gui/wxpython/gui_core/forms.py @@ -322,16 +322,12 @@ def run(self): native = False break # TODO: update only if needed - if native: - if map: - self.data[win.InsertLayers] = {"vector": map} - else: - self.data[win.InsertLayers] = {} + if map: + self.data[win.InsertLayers] = ( + {"vector": map} if native else {"dsn": map.rstrip("@OGR")} + ) else: - if map: - self.data[win.InsertLayers] = {"dsn": map.rstrip("@OGR")} - else: - self.data[win.InsertLayers] = {} + self.data[win.InsertLayers] = {} elif name == "TableSelect": self.data[win.InsertTables] = {"driver": driver, "database": db} @@ -351,17 +347,17 @@ def run(self): "layer": layer, "dbInfo": cparams[map]["dbInfo"], } - else: # table - if driver and db: - self.data[win.GetParent().InsertTableColumns] = { - "table": pTable.get("value"), - "driver": driver, - "database": db, - } - elif pTable: - self.data[win.GetParent().InsertTableColumns] = { - "table": pTable.get("value") - } + # table + elif driver and db: + self.data[win.GetParent().InsertTableColumns] = { + "table": pTable.get("value"), + "driver": driver, + "database": db, + } + elif pTable: + self.data[win.GetParent().InsertTableColumns] = { + "table": pTable.get("value") + } elif name == "SubGroupSelect": self.data[win.Insert] = {"group": p.get("value", "")} @@ -2762,14 +2758,12 @@ def OnVerbosity(self, event): verbose = self.FindWindowById(self.task.get_flag("verbose")["wxId"][0]) quiet = self.FindWindowById(self.task.get_flag("quiet")["wxId"][0]) if event.IsChecked(): - if event.GetId() == verbose.GetId(): - if quiet.IsChecked(): - quiet.SetValue(False) - self.task.get_flag("quiet")["value"] = False - else: - if verbose.IsChecked(): - verbose.SetValue(False) - self.task.get_flag("verbose")["value"] = False + if event.GetId() == verbose.GetId() and quiet.IsChecked(): + quiet.SetValue(False) + self.task.get_flag("quiet")["value"] = False + elif verbose.IsChecked(): + verbose.SetValue(False) + self.task.get_flag("verbose")["value"] = False event.Skip() @@ -2925,15 +2919,14 @@ def OnSetValue(self, event): pLayer = self.task.get_param("layer", element="name", raiseError=False) if pLayer: pLayer["value"] = "" + elif isinstance(me, SpinCtrl): + porf["value"] = str(me.GetValue()) + elif isinstance(me, wx.ComboBox): + porf["value"] = me.GetValue() + elif isinstance(me, wx.Choice): + porf["value"] = me.GetStringSelection() else: - if isinstance(me, SpinCtrl): - porf["value"] = str(me.GetValue()) - elif isinstance(me, wx.ComboBox): - porf["value"] = me.GetValue() - elif isinstance(me, wx.Choice): - porf["value"] = me.GetStringSelection() - else: - porf["value"] = me.GetValue() + porf["value"] = me.GetValue() self.OnUpdateValues(event) diff --git a/gui/wxpython/gui_core/goutput.py b/gui/wxpython/gui_core/goutput.py index b26f6611f3d..6aca497f365 100644 --- a/gui/wxpython/gui_core/goutput.py +++ b/gui/wxpython/gui_core/goutput.py @@ -573,9 +573,8 @@ def AddTextWrapped(self, txt, wrap=None): if wrap: txt = textwrap.fill(txt, wrap) + "\n" - else: - if txt[-1] != "\n": - txt += "\n" + elif txt[-1] != "\n": + txt += "\n" if "\r" in txt: self.linePos = -1 diff --git a/gui/wxpython/gui_core/gselect.py b/gui/wxpython/gui_core/gselect.py index dc553ae74bb..215a7bba23d 100644 --- a/gui/wxpython/gui_core/gselect.py +++ b/gui/wxpython/gui_core/gselect.py @@ -753,14 +753,13 @@ def _selectTreeItem(self, item): if self.multiple: self.value.append(fullName) - else: - if self.nmaps > 1: # see key_desc - if len(self.value) >= self.nmaps: - self.value = [fullName] - else: - self.value.append(fullName) - else: + elif self.nmaps > 1: # see key_desc + if len(self.value) >= self.nmaps: self.value = [fullName] + else: + self.value.append(fullName) + else: + self.value = [fullName] def _onItemConfirmed(self, event): item = event.GetItem() @@ -2334,52 +2333,43 @@ def getProjMatchCaption(projectionMatch): (layerId, layerName, featureType, int(projectionMatch), grassName) ) layerId += 1 - else: - if self._sourceType == "file": - baseName = os.path.basename(dsn) + elif self._sourceType == "file": + baseName = os.path.basename(dsn) + grassName = GetValidLayerName(baseName.split(".", -1)[0]) + projectionMatch = hasRastSameProjAsLocation(dsn) + projectionMatchCaption = getProjMatchCaption(projectionMatch) + listData.append((layerId, baseName, projectionMatchCaption, grassName)) + data.append((layerId, baseName, int(projectionMatch), grassName)) + elif self._sourceType == "dir": + ext = self.dirWidgets["extension"].GetValue() + for filename in glob.glob( + os.path.join(dsn, "%s") % self._getExtPatternGlob(ext) + ): + baseName = os.path.basename(filename) grassName = GetValidLayerName(baseName.split(".", -1)[0]) - projectionMatch = hasRastSameProjAsLocation(dsn) + projectionMatch = hasRastSameProjAsLocation(filename) projectionMatchCaption = getProjMatchCaption(projectionMatch) listData.append((layerId, baseName, projectionMatchCaption, grassName)) data.append((layerId, baseName, int(projectionMatch), grassName)) - elif self._sourceType == "dir": - ext = self.dirWidgets["extension"].GetValue() - for filename in glob.glob( - os.path.join(dsn, "%s") % self._getExtPatternGlob(ext) - ): - baseName = os.path.basename(filename) - grassName = GetValidLayerName(baseName.split(".", -1)[0]) - projectionMatch = hasRastSameProjAsLocation(filename) - projectionMatchCaption = getProjMatchCaption(projectionMatch) - listData.append( - (layerId, baseName, projectionMatchCaption, grassName) - ) - data.append((layerId, baseName, int(projectionMatch), grassName)) - layerId += 1 - elif ( - self.dbWidgets["format"].GetStringSelection() == "PostGIS Raster driver" - ): - rasters = self._getPGDBRasters(dsn) - for raster in rasters: - grassName = GetValidLayerName(raster) - projectionMatch = hasRastSameProjAsLocation(dsn, table=raster) - projectionMatchCaption = getProjMatchCaption(projectionMatch) - listData.append( - (layerId, raster, projectionMatchCaption, grassName) - ) - data.append((layerId, raster, int(projectionMatch), grassName)) - layerId += 1 - elif self.dbWidgets["format"].GetStringSelection() == "Rasterlite": - rasters = self._getRasterliteDBRasters(dsn) - for raster in rasters: - grassName = GetValidLayerName(raster) - projectionMatch = hasRastSameProjAsLocation(dsn) - projectionMatchCaption = getProjMatchCaption(projectionMatch) - listData.append( - (layerId, raster, projectionMatchCaption, grassName) - ) - data.append((layerId, raster, int(projectionMatch), grassName)) - layerId += 1 + layerId += 1 + elif self.dbWidgets["format"].GetStringSelection() == "PostGIS Raster driver": + rasters = self._getPGDBRasters(dsn) + for raster in rasters: + grassName = GetValidLayerName(raster) + projectionMatch = hasRastSameProjAsLocation(dsn, table=raster) + projectionMatchCaption = getProjMatchCaption(projectionMatch) + listData.append((layerId, raster, projectionMatchCaption, grassName)) + data.append((layerId, raster, int(projectionMatch), grassName)) + layerId += 1 + elif self.dbWidgets["format"].GetStringSelection() == "Rasterlite": + rasters = self._getRasterliteDBRasters(dsn) + for raster in rasters: + grassName = GetValidLayerName(raster) + projectionMatch = hasRastSameProjAsLocation(dsn) + projectionMatchCaption = getProjMatchCaption(projectionMatch) + listData.append((layerId, raster, projectionMatchCaption, grassName)) + data.append((layerId, raster, int(projectionMatch), grassName)) + layerId += 1 # emit signal self.reloadDataRequired.emit(listData=listData, data=data) @@ -2460,17 +2450,15 @@ def OnHelp(self, event): cmd = "v.external.out" else: cmd = "r.external.out" - else: - if self.link: - if self.ogr: - cmd = "v.external" - else: - cmd = "r.external" + elif self.link: + if self.ogr: + cmd = "v.external" else: - if self.ogr: - cmd = "v.in.ogr" - else: - cmd = "r.in.gdal" + cmd = "r.external" + elif self.ogr: + cmd = "v.in.ogr" + else: + cmd = "r.in.gdal" RunCommand("g.manual", entry=cmd) @@ -2887,12 +2875,11 @@ def _onClick(self, event): self.registered = True self._giface.GetMapDisplay().Raise() - else: - if self.mapWin and self.mapWin.UnregisterMouseEventHandler( - wx.EVT_LEFT_DOWN, self._onMapClickHandler - ): - self.registered = False - return + elif self.mapWin and self.mapWin.UnregisterMouseEventHandler( + wx.EVT_LEFT_DOWN, self._onMapClickHandler + ): + self.registered = False + return def drawCleanUp(self): if self.drawMapWin: @@ -3040,27 +3027,22 @@ def _chckMap(self): return False def _onClick(self, evt=None): - if self.task is not None: - if not self._chckMap(): - self.buttonVecSelect.SetValue(False) - return - else: - if not self._isMapSelected(): - self.buttonVecSelect.SetValue(False) + if (self.task is not None and not self._chckMap()) or not self._isMapSelected(): + self.buttonVecSelect.SetValue(False) + return + + if self._vectorSelect is None and self.mapdisp: + if self.buttonVecSelect.IsEnabled(): + switcher = self.mapdisp.GetToolSwitcher() + switcher.ToolChanged(self.buttonVecSelect.GetId()) + + self._vectorSelect = VectorSelectBase(self.mapdisp, self.giface) + if not self.mapdisp.GetWindow().RegisterMouseEventHandler( + wx.EVT_LEFT_DOWN, self._onMapClickHandler, "cross" + ): return - if self._vectorSelect is None: - if self.mapdisp: - if self.buttonVecSelect.IsEnabled(): - switcher = self.mapdisp.GetToolSwitcher() - switcher.ToolChanged(self.buttonVecSelect.GetId()) - - self._vectorSelect = VectorSelectBase(self.mapdisp, self.giface) - if not self.mapdisp.GetWindow().RegisterMouseEventHandler( - wx.EVT_LEFT_DOWN, self._onMapClickHandler, "cross" - ): - return - self.registered = True - self.mapdisp.Raise() + self.registered = True + self.mapdisp.Raise() else: self.OnClose() @@ -3081,16 +3063,10 @@ def _onMapClickHandler(self, event): if event == "unregistered": return - if self.task is None: - if not self._isMapSelected(): - self.OnClose() - else: - self.catsField.SetValue(self._vectorSelect.GetLineStringSelectedCats()) + if (self.task is None and not self._isMapSelected()) or not self._chckMap(): + self.OnClose() else: - if not self._chckMap(): - self.OnClose() - else: - self.catsField.SetValue(self._vectorSelect.GetLineStringSelectedCats()) + self.catsField.SetValue(self._vectorSelect.GetLineStringSelectedCats()) def GetTextWin(self): return self.catsField diff --git a/gui/wxpython/gui_core/mapdisp.py b/gui/wxpython/gui_core/mapdisp.py index e74f526fa71..4fe1c25ac18 100644 --- a/gui/wxpython/gui_core/mapdisp.py +++ b/gui/wxpython/gui_core/mapdisp.py @@ -720,7 +720,7 @@ def SetBindRegions(self, on): self.firstMapWindow.zoomChanged.connect(self.OnZoomChangedFirstMap) else: self.secondMapWindow.zoomChanged.connect(self.OnZoomChangedSecondMap) - else: + else: # noqa: PLR5501 if self.MapWindow == self.firstMapWindow: self.firstMapWindow.zoomChanged.disconnect(self.OnZoomChangedFirstMap) else: diff --git a/gui/wxpython/gui_core/pyedit.py b/gui/wxpython/gui_core/pyedit.py index b7c8baf4001..9fa75fa74c1 100644 --- a/gui/wxpython/gui_core/pyedit.py +++ b/gui/wxpython/gui_core/pyedit.py @@ -514,14 +514,15 @@ def OpenRecentFile(self, path, file_exists, file_history): ), parent=self.guiparent, ) - else: - if self.CanReplaceContent(by_message="file"): - self.filename = path - content = self._openFile(file_path=path) - if content: - self.body.SetText(content) - file_history.AddFileToHistory(filename=path) # move up the list - self.tempfile = False + return + + if self.CanReplaceContent(by_message="file"): + self.filename = path + content = self._openFile(file_path=path) + if content: + self.body.SetText(content) + file_history.AddFileToHistory(filename=path) # move up the list + self.tempfile = False def IsEmpty(self): """Check if python script is empty""" diff --git a/gui/wxpython/gui_core/pystc.py b/gui/wxpython/gui_core/pystc.py index bf268f88f33..00db4d8a0c8 100644 --- a/gui/wxpython/gui_core/pystc.py +++ b/gui/wxpython/gui_core/pystc.py @@ -373,9 +373,8 @@ def Expand(self, line, doExpand, force=False, visLevels=0, level=-1): self.ShowLines(line, line) else: self.HideLines(line, line) - else: - if doExpand: - self.ShowLines(line, line) + elif doExpand: + self.ShowLines(line, line) if level == -1: level = self.GetFoldLevel(line) @@ -388,11 +387,10 @@ def Expand(self, line, doExpand, force=False, visLevels=0, level=-1): self.SetFoldExpanded(line, False) line = self.Expand(line, doExpand, force, visLevels - 1) + elif doExpand and self.GetFoldExpanded(line): + line = self.Expand(line, True, force, visLevels - 1) else: - if doExpand and self.GetFoldExpanded(line): - line = self.Expand(line, True, force, visLevels - 1) - else: - line = self.Expand(line, False, force, visLevels - 1) + line = self.Expand(line, False, force, visLevels - 1) else: line = line + 1 diff --git a/gui/wxpython/gui_core/toolbars.py b/gui/wxpython/gui_core/toolbars.py index 02e4fdecad5..47c00fd36fc 100644 --- a/gui/wxpython/gui_core/toolbars.py +++ b/gui/wxpython/gui_core/toolbars.py @@ -173,13 +173,10 @@ def EnableLongHelp(self, enable): if isinstance(tool[0], tuple): if tool[0][0] == "": # separator continue - else: - internal_label = tool[0][0] - else: - if tool[0] == "": # separator - continue - else: - internal_label = tool[0] + internal_label = tool[0][0] + elif tool[0] == "": # separator + continue + internal_label = tool[0] label = vars(self.widget)[internal_label] if enable: diff --git a/gui/wxpython/gui_core/wrap.py b/gui/wxpython/gui_core/wrap.py index 41ab1486c46..29299bbbbe2 100644 --- a/gui/wxpython/gui_core/wrap.py +++ b/gui/wxpython/gui_core/wrap.py @@ -144,7 +144,7 @@ def SetToolTip(self, tip): wx.Window.UnsetToolTip(self) else: wx.Window.SetToolTip(self, tipString=tip) - else: + else: # noqa: PLR5501 if tip is None: wx.Window.SetToolTip(self, tip) else: diff --git a/gui/wxpython/history/tree.py b/gui/wxpython/history/tree.py index 8bccc3e97a7..98ab8eb0994 100644 --- a/gui/wxpython/history/tree.py +++ b/gui/wxpython/history/tree.py @@ -244,24 +244,21 @@ def _initHistoryModel(self): if day: day = day[0] + # Create time period node if not found + elif not entry["command_info"]: + # Prepare it for entries without command info + day = self._model.AppendNode( + parent=self._model.root, + data={"type": TIME_PERIOD, "day": self._timestampToDay()}, + ) else: - # Create time period node if not found - if not entry["command_info"]: - # Prepare it for entries without command info - day = self._model.AppendNode( - parent=self._model.root, - data={"type": TIME_PERIOD, "day": self._timestampToDay()}, - ) - else: - day = self._model.AppendNode( - parent=self._model.root, - data={ - "type": TIME_PERIOD, - "day": self._timestampToDay( - entry["command_info"]["timestamp"] - ), - }, - ) + day = self._model.AppendNode( + parent=self._model.root, + data={ + "type": TIME_PERIOD, + "day": self._timestampToDay(entry["command_info"]["timestamp"]), + }, + ) # Determine status and create command node status = ( @@ -543,8 +540,9 @@ def OnDoubleClick(self, node): self.DefineItems([node]) if self.selected_command[0]: self.Run(node) + return + + if self.IsNodeExpanded(node): + self.CollapseNode(node, recursive=False) else: - if self.IsNodeExpanded(node): - self.CollapseNode(node, recursive=False) - else: - self.ExpandNode(node, recursive=False) + self.ExpandNode(node, recursive=False) diff --git a/gui/wxpython/iclass/digit.py b/gui/wxpython/iclass/digit.py index a6f0ef15948..85334993db1 100644 --- a/gui/wxpython/iclass/digit.py +++ b/gui/wxpython/iclass/digit.py @@ -160,7 +160,7 @@ def CopyMap(self, name, tmp=False, update=False): open_fn = Vect_open_update else: open_fn = Vect_open_new - else: + else: # noqa: PLR5501 if update: open_fn = Vect_open_tmp_update else: diff --git a/gui/wxpython/iclass/frame.py b/gui/wxpython/iclass/frame.py index 14c44aba5ea..3aede6480c8 100644 --- a/gui/wxpython/iclass/frame.py +++ b/gui/wxpython/iclass/frame.py @@ -959,9 +959,8 @@ def OnCategoryManager(self, event): dlg.CenterOnParent() dlg.Show() self.dialogs["classManager"] = dlg - else: - if not self.dialogs["classManager"].IsShown(): - self.dialogs["classManager"].Show() + elif not self.dialogs["classManager"].IsShown(): + self.dialogs["classManager"].Show() def CategoryChanged(self, currentCat): """Updates everything which depends on current category. diff --git a/gui/wxpython/icons/icon.py b/gui/wxpython/icons/icon.py index c87e5af5e24..77ef956278b 100644 --- a/gui/wxpython/icons/icon.py +++ b/gui/wxpython/icons/icon.py @@ -57,11 +57,10 @@ def __init__(self, img, label=None, desc=None): self.imagepath = iconSet.get(img, wx.ART_MISSING_IMAGE) if not self.imagepath: self.type = "unknown" + elif self.imagepath.find("wxART_") > -1: + self.type = "wx" else: - if self.imagepath.find("wxART_") > -1: - self.type = "wx" - else: - self.type = "img" + self.type = "img" self.label = label diff --git a/gui/wxpython/image2target/ii2t_manager.py b/gui/wxpython/image2target/ii2t_manager.py index 39e77d9a7e4..b4bc3d60f97 100644 --- a/gui/wxpython/image2target/ii2t_manager.py +++ b/gui/wxpython/image2target/ii2t_manager.py @@ -1435,11 +1435,10 @@ def SetGCPSatus(self, item, itemIndex): wxPen = "highest" else: wxPen = "default" + elif self.mapcoordlist[key][7] > self.rmsthresh: + wxPen = "highest" else: - if self.mapcoordlist[key][7] > self.rmsthresh: - wxPen = "highest" - else: - wxPen = "default" + wxPen = "default" if itemIndex == self.list.selectedkey: wxPen = "selected" @@ -3459,7 +3458,7 @@ def UpdateSettings(self): self.parent.activemap.SetSelection(0) self.parent.activemap.Enable(False) self.parent.GetMapToolbar().Enable("zoommenu", enable=False) - else: + else: # noqa: PLR5501 if not self.parent.show_target: self.parent.show_target = True self.parent._mgr.GetPane("target").Show() diff --git a/gui/wxpython/lmgr/layertree.py b/gui/wxpython/lmgr/layertree.py index 38751e35b55..e2204555415 100644 --- a/gui/wxpython/lmgr/layertree.py +++ b/gui/wxpython/lmgr/layertree.py @@ -1502,28 +1502,27 @@ def AddLayer( if not parent: parent = self.root layer = self.AppendItem(parentId=parent, text="", ct_type=1, wnd=ctrl) - else: - if selectedLayer and selectedLayer != self.GetRootItem(): - if ( - selectedLayer - and self.GetLayerInfo(selectedLayer, key="type") == "group" - ): - # add to group (first child of self.layer_selected) - layer = self.PrependItem( - parent=selectedLayer, text="", ct_type=1, wnd=ctrl - ) - else: - # -> previous sibling of selected layer - parent = self.GetItemParent(selectedLayer) - layer = self.InsertItem( - parentId=parent, - input=self.GetPrevSibling(selectedLayer), - text="", - ct_type=1, - wnd=ctrl, - ) - else: # add first layer to the layer tree (first child of root) - layer = self.PrependItem(parent=self.root, text="", ct_type=1, wnd=ctrl) + elif selectedLayer and selectedLayer != self.GetRootItem(): + if ( + selectedLayer + and self.GetLayerInfo(selectedLayer, key="type") == "group" + ): + # add to group (first child of self.layer_selected) + layer = self.PrependItem( + parent=selectedLayer, text="", ct_type=1, wnd=ctrl + ) + else: + # -> previous sibling of selected layer + parent = self.GetItemParent(selectedLayer) + layer = self.InsertItem( + parentId=parent, + input=self.GetPrevSibling(selectedLayer), + text="", + ct_type=1, + wnd=ctrl, + ) + else: # add first layer to the layer tree (first child of root) + layer = self.PrependItem(parent=self.root, text="", ct_type=1, wnd=ctrl) # layer is initially unchecked as inactive (beside 'command') # use predefined value if given @@ -1543,21 +1542,20 @@ def AddLayer( self.SetItemImage(layer, self.folder, CT.TreeItemIcon_Normal) self.SetItemImage(layer, self.folder_open, CT.TreeItemIcon_Expanded) self.SetItemText(layer, grouptext) + elif ltype in self._icon: + self.SetItemImage(layer, self._icon[ltype]) + # do not use title() - will not work with ltype == 'raster_3d' + self.SetItemText( + layer, + "%s %s" + % ( + LMIcons["layer" + ltype[0].upper() + ltype[1:]].GetLabel(), + _("(double click to set properties)") + " " * 15, + ), + ) else: - if ltype in self._icon: - self.SetItemImage(layer, self._icon[ltype]) - # do not use title() - will not work with ltype == 'raster_3d' - self.SetItemText( - layer, - "%s %s" - % ( - LMIcons["layer" + ltype[0].upper() + ltype[1:]].GetLabel(), - _("(double click to set properties)") + " " * 15, - ), - ) - else: - self.SetItemImage(layer, self._icon["cmd"]) - self.SetItemText(layer, ltype) + self.SetItemImage(layer, self._icon["cmd"]) + self.SetItemText(layer, ltype) if ltype != "group": if lcmd and len(lcmd) > 1: @@ -1659,9 +1657,8 @@ def AddLayer( ctrl.SetValue(lname) else: self.SetItemText(layer, self._getLayerName(layer, lname)) - else: - if ltype == "group": - self.OnRenameLayer(None) + elif ltype == "group": + self.OnRenameLayer(None) return layer @@ -1849,7 +1846,7 @@ def OnLayerChecked(self, event): if (vInfo["lines"] + vInfo["boundaries"]) > 0: self.mapdisplay.MapWindow.LoadVector(item, points=False) - else: # disable + else: # disable # noqa: PLR5501 if mapLayer.type == "raster": self.mapdisplay.MapWindow.UnloadRaster(item) elif mapLayer.type == "raster_3d": @@ -2094,22 +2091,21 @@ def RecreateItem(self, dragItem, dropTarget, parent=None): image=image, data=data, ) - else: + elif self.flag & wx.TREE_HITTEST_ABOVE: # if dragItem not dropped on a layer or group, append or prepend it # to the layer tree - if self.flag & wx.TREE_HITTEST_ABOVE: - newItem = self.PrependItem( - self.root, text=text, ct_type=1, wnd=newctrl, image=image, data=data - ) - elif ( - (self.flag & wx.TREE_HITTEST_BELOW) - or (self.flag & wx.TREE_HITTEST_NOWHERE) - or (self.flag & wx.TREE_HITTEST_TOLEFT) - or (self.flag & wx.TREE_HITTEST_TORIGHT) - ): - newItem = self.AppendItem( - self.root, text=text, ct_type=1, wnd=newctrl, image=image, data=data - ) + newItem = self.PrependItem( + self.root, text=text, ct_type=1, wnd=newctrl, image=image, data=data + ) + elif ( + (self.flag & wx.TREE_HITTEST_BELOW) + or (self.flag & wx.TREE_HITTEST_NOWHERE) + or (self.flag & wx.TREE_HITTEST_TOLEFT) + or (self.flag & wx.TREE_HITTEST_TORIGHT) + ): + newItem = self.AppendItem( + self.root, text=text, ct_type=1, wnd=newctrl, image=image, data=data + ) # update new layer self.SetPyData(newItem, self.GetPyData(dragItem)) diff --git a/gui/wxpython/location_wizard/wizard.py b/gui/wxpython/location_wizard/wizard.py index 670710a6ebf..bdd955a7d30 100644 --- a/gui/wxpython/location_wizard/wizard.py +++ b/gui/wxpython/location_wizard/wizard.py @@ -803,11 +803,10 @@ def Search(self, index, pattern, firstOnly=True): return data[0] else: return data + elif firstOnly: + return None else: - if firstOnly: - return None - else: - return [] + return [] class ProjParamsPage(TitledPage): @@ -903,19 +902,18 @@ def OnPageChange(self, event=None): continue else: self.p4projparams += " +" + param["proj4"] + elif param["value"] is None: + wx.MessageBox( + parent=self, + message=_("You must enter a value for %s") % param["desc"], + caption=_("Error"), + style=wx.ICON_ERROR | wx.CENTRE, + ) + event.Veto() else: - if param["value"] is None: - wx.MessageBox( - parent=self, - message=_("You must enter a value for %s") % param["desc"], - caption=_("Error"), - style=wx.ICON_ERROR | wx.CENTRE, - ) - event.Veto() - else: - self.p4projparams += ( - " +" + param["proj4"] + "=" + str(param["value"]) - ) + self.p4projparams += ( + " +" + param["proj4"] + "=" + str(param["value"]) + ) def OnEnterPage(self, event): """Page entered""" @@ -1489,9 +1487,8 @@ def OnText(self, event): if len(self.georeffile) > 0 and os.path.isfile(self.georeffile): if not nextButton.IsEnabled(): nextButton.Enable(True) - else: - if nextButton.IsEnabled(): - nextButton.Enable(False) + elif nextButton.IsEnabled(): + nextButton.Enable(False) event.Skip() @@ -1566,9 +1563,8 @@ def OnText(self, event): if len(self.wktstring) == 0: if nextButton.IsEnabled(): nextButton.Enable(False) - else: - if not nextButton.IsEnabled(): - nextButton.Enable() + elif not nextButton.IsEnabled(): + nextButton.Enable() class EPSGPage(TitledPage): @@ -2160,9 +2156,8 @@ def GetProjstring(self, event): if len(self.customstring) == 0: if nextButton.IsEnabled(): nextButton.Enable(False) - else: - if not nextButton.IsEnabled(): - nextButton.Enable() + elif not nextButton.IsEnabled(): + nextButton.Enable() class SummaryPage(TitledPage): diff --git a/gui/wxpython/mapdisp/statusbar.py b/gui/wxpython/mapdisp/statusbar.py index 3c819c2d765..0c5bac10124 100644 --- a/gui/wxpython/mapdisp/statusbar.py +++ b/gui/wxpython/mapdisp/statusbar.py @@ -637,20 +637,19 @@ def GetCenterString(self, map): return "%.*f; %.*f" % (precision, coord[0], precision, coord[1]) else: raise SbException(_("Error in projection (check the settings)")) + elif self.mapFrame.GetMap().projinfo["proj"] == "ll" and format == "DMS": + return "%s" % utils.Deg2DMS( + region["center_easting"], + region["center_northing"], + precision=precision, + ) else: - if self.mapFrame.GetMap().projinfo["proj"] == "ll" and format == "DMS": - return "%s" % utils.Deg2DMS( - region["center_easting"], - region["center_northing"], - precision=precision, - ) - else: - return "%.*f; %.*f" % ( - precision, - region["center_easting"], - precision, - region["center_northing"], - ) + return "%.*f; %.*f" % ( + precision, + region["center_easting"], + precision, + region["center_northing"], + ) def SetCenter(self): """Set current map center as item value""" @@ -813,11 +812,10 @@ def ReprojectENFromMap(self, e, n, useDefinedProjection, precision, format): return "%.*f; %.*f" % (precision, e, precision, n) else: raise SbException(_("Error in projection (check the settings)")) + elif self.mapFrame.GetMap().projinfo["proj"] == "ll" and format == "DMS": + return utils.Deg2DMS(e, n, precision=precision) else: - if self.mapFrame.GetMap().projinfo["proj"] == "ll" and format == "DMS": - return utils.Deg2DMS(e, n, precision=precision) - else: - return "%.*f; %.*f" % (precision, e, precision, n) + return "%.*f; %.*f" % (precision, e, precision, n) class SbRegionExtent(SbTextItem): @@ -930,25 +928,24 @@ def ReprojectRegionFromMap(self, region, useDefinedProjection, precision, format else: raise SbException(_("Error in projection (check the settings)")) + elif self.mapFrame.GetMap().projinfo["proj"] == "ll" and format == "DMS": + w, s = utils.Deg2DMS( + region["w"], region["s"], string=False, precision=precision + ) + e, n = utils.Deg2DMS( + region["e"], region["n"], string=False, precision=precision + ) + ewres, nsres = utils.Deg2DMS( + region["ewres"], region["nsres"], string=False, precision=precision + ) + return self._formatRegion(w=w, s=s, e=e, n=n, ewres=ewres, nsres=nsres) else: - if self.mapFrame.GetMap().projinfo["proj"] == "ll" and format == "DMS": - w, s = utils.Deg2DMS( - region["w"], region["s"], string=False, precision=precision - ) - e, n = utils.Deg2DMS( - region["e"], region["n"], string=False, precision=precision - ) - ewres, nsres = utils.Deg2DMS( - region["ewres"], region["nsres"], string=False, precision=precision - ) - return self._formatRegion(w=w, s=s, e=e, n=n, ewres=ewres, nsres=nsres) - else: - w, s = region["w"], region["s"] - e, n = region["e"], region["n"] - ewres, nsres = region["ewres"], region["nsres"] - return self._formatRegion( - w=w, s=s, e=e, n=n, ewres=ewres, nsres=nsres, precision=precision - ) + w, s = region["w"], region["s"] + e, n = region["e"], region["n"] + ewres, nsres = region["ewres"], region["nsres"] + return self._formatRegion( + w=w, s=s, e=e, n=n, ewres=ewres, nsres=nsres, precision=precision + ) class SbCompRegionExtent(SbRegionExtent): diff --git a/gui/wxpython/mapswipe/frame.py b/gui/wxpython/mapswipe/frame.py index 1f4c3e964fe..14c1fd91e28 100644 --- a/gui/wxpython/mapswipe/frame.py +++ b/gui/wxpython/mapswipe/frame.py @@ -424,12 +424,11 @@ def OnSelectLayers(self, event): self._inputDialog = dlg dlg.CentreOnParent() dlg.Show() + elif self._inputDialog.IsShown(): + self._inputDialog.Raise() + self._inputDialog.SetFocus() else: - if self._inputDialog.IsShown(): - self._inputDialog.Raise() - self._inputDialog.SetFocus() - else: - self._inputDialog.Show() + self._inputDialog.Show() def _connectSimpleLmgr(self, lmgr, renderer): converter = LayerListToRendererConverter(renderer) @@ -569,7 +568,7 @@ def process(self2): im1 = wx.Image(filename1).GetSubImage((0, 0, -x, height)) im.Paste(im1, 0, 0) im.Paste(wx.Image(filename2), -x + lineWidth, -y) - else: + else: # noqa: PLR5501 if self.splitter.GetSplitMode() == wx.SPLIT_HORIZONTAL: im1 = wx.Image(filename1) im.Paste(im1, 0, 0) diff --git a/gui/wxpython/mapwin/decorations.py b/gui/wxpython/mapwin/decorations.py index 9e2051a435d..fef02581715 100644 --- a/gui/wxpython/mapwin/decorations.py +++ b/gui/wxpython/mapwin/decorations.py @@ -211,9 +211,8 @@ def CmdIsValid(self): param = param.split("=") if len(param) == 1: inputs += 1 - else: - if param[0] == "text" and len(param) == 2: - inputs += 1 + elif param[0] == "text" and len(param) == 2: + inputs += 1 if inputs >= 1: return True return False @@ -318,11 +317,10 @@ def CmdIsValid(self): param = param.split("=") if len(param) == 1: inputs += 1 - else: - if param[0] == "raster" and len(param) == 2: - inputs += 1 - elif param[0] == "raster_3d" and len(param) == 2: - inputs += 1 + elif param[0] == "raster" and len(param) == 2: + inputs += 1 + elif param[0] == "raster_3d" and len(param) == 2: + inputs += 1 if inputs == 1: return True return False diff --git a/gui/wxpython/modules/colorrules.py b/gui/wxpython/modules/colorrules.py index 5ded54c8be8..569bb4e5d7d 100644 --- a/gui/wxpython/modules/colorrules.py +++ b/gui/wxpython/modules/colorrules.py @@ -1695,7 +1695,7 @@ def SetRangeLabel(self): ) else: self.cr_label.SetLabel(_("Enter vector attribute values %s:") % range) - else: + else: # noqa: PLR5501 if self.colorTable: self.cr_label.SetLabel(_("Enter vector attribute values or percents:")) else: @@ -1809,11 +1809,10 @@ def CreateColorTable(self, tmp=False): """Create color rules (color table or color column)""" if self.colorTable: ret = ColorTable.CreateColorTable(self) + elif self.updateColumn: + ret = self.UpdateColorColumn(tmp) else: - if self.updateColumn: - ret = self.UpdateColorColumn(tmp) - else: - ret = True + ret = True return ret @@ -1939,17 +1938,13 @@ def _apply(self, updatePreview=True): value = None if self.properties["storeColumn"]: value = self.properties["storeColumn"] + if self.colorTable: + value = None - if not self.colorTable: - if self.attributeType == "color": - data["vector"][self.vectorType]["thematic"]["rgbcolumn"] = value - else: - data["vector"][self.vectorType]["thematic"]["sizecolumn"] = value + if self.attributeType == "color": + data["vector"][self.vectorType]["thematic"]["rgbcolumn"] = value else: - if self.attributeType == "color": - data["vector"][self.vectorType]["thematic"]["rgbcolumn"] = None - else: - data["vector"][self.vectorType]["thematic"]["sizecolumn"] = None + data["vector"][self.vectorType]["thematic"]["sizecolumn"] = value data["vector"][self.vectorType]["thematic"]["update"] = None diff --git a/gui/wxpython/modules/extensions.py b/gui/wxpython/modules/extensions.py index ed6159a5f41..f7822e0cb0e 100644 --- a/gui/wxpython/modules/extensions.py +++ b/gui/wxpython/modules/extensions.py @@ -385,9 +385,8 @@ def Load(self, url, full=True): mainNode = self.mainNodes[self._expandPrefix(prefix)] currentNode = self.model.AppendNode(parent=mainNode, label=value) currentNode.data = {"command": value} - else: - if currentNode is not None: - currentNode.data[key] = value + elif currentNode is not None: + currentNode.data[key] = value else: try: prefix, name = line.strip().split(".", 1) diff --git a/gui/wxpython/nviz/mapwindow.py b/gui/wxpython/nviz/mapwindow.py index 30a7682f6c7..bbd6bcaa3bb 100644 --- a/gui/wxpython/nviz/mapwindow.py +++ b/gui/wxpython/nviz/mapwindow.py @@ -567,11 +567,10 @@ def OnKeyDown(self, event): self.ProcessFlyByArrows(keyCode=key) # change speed of flight when using mouse - else: - if key == wx.WXK_UP: - self.ChangeFlySpeed(increase=True) - elif key == wx.WXK_DOWN: - self.ChangeFlySpeed(increase=False) + elif key == wx.WXK_UP: + self.ChangeFlySpeed(increase=True) + elif key == wx.WXK_DOWN: + self.ChangeFlySpeed(increase=False) elif key in {wx.WXK_HOME, wx.WXK_PAGEUP} and self.timerFly.IsRunning(): self.ChangeFlySpeed(increase=True) @@ -2077,14 +2076,13 @@ def UpdateVolumeProperties(self, id, data, isosurfId=None): ) ) self._display.SetIsosurfaceMode(id, mode) - else: - if data["draw"]["shading"]["slice"]["value"] < 0: # need to calculate - mode = data["draw"]["shading"]["slice"]["value"] = ( - self.nvizDefault.GetDrawMode( - shade=data["draw"]["shading"]["slice"], string=False - ) + elif data["draw"]["shading"]["slice"]["value"] < 0: # need to calculate + mode = data["draw"]["shading"]["slice"]["value"] = ( + self.nvizDefault.GetDrawMode( + shade=data["draw"]["shading"]["slice"], string=False ) - self._display.SetSliceMode(id, mode) + ) + self._display.SetSliceMode(id, mode) data["draw"]["shading"].pop("update") # @@ -2489,13 +2487,12 @@ def NvizCmdCommand(self): cmdColorMap += ( "%s," % self.tree.GetLayerInfo(item, key="maplayer").GetName() ) + elif nvizData["color"]["map"]: + cmdColorMap += "%s," % nvizData["color"]["value"] else: - if nvizData["color"]["map"]: - cmdColorMap += "%s," % nvizData["color"]["value"] - else: - cmdColorVal += "%s," % nvizData["color"]["value"] - # TODO - # transparency, shine, mask + cmdColorVal += "%s," % nvizData["color"]["value"] + # TODO + # transparency, shine, mask for item in self.constants: cmdColorVal += "%s," % item["constant"]["color"] if cmdColorMap.split("=")[1]: diff --git a/gui/wxpython/nviz/tools.py b/gui/wxpython/nviz/tools.py index 19d35b13c20..02341420cc3 100644 --- a/gui/wxpython/nviz/tools.py +++ b/gui/wxpython/nviz/tools.py @@ -3320,9 +3320,8 @@ def __GetWindowName(self, data, id): for win in data[name].values(): if win == id: return name - else: - if data[name] == id: - return name + elif data[name] == id: + return name return None @@ -3532,14 +3531,13 @@ def OnLookAt(self, event): self.PostViewEvent(zExag=True) self.UpdateSettings() self.mapWindow.Refresh(False) - else: # here - if self.FindWindowById(event.GetId()).GetValue(): - self.mapDisplay.Raise() - self.mapWindow.mouse["use"] = "lookHere" - self.mapWindow.SetNamedCursor("cross") - else: - self.mapWindow.mouse["use"] = "default" - self.mapWindow.SetNamedCursor("default") + elif self.FindWindowById(event.GetId()).GetValue(): + self.mapDisplay.Raise() + self.mapWindow.mouse["use"] = "lookHere" + self.mapWindow.SetNamedCursor("cross") + else: + self.mapWindow.mouse["use"] = "default" + self.mapWindow.SetNamedCursor("default") def OnResetView(self, event): """Reset to default view (view page)""" @@ -3694,14 +3692,10 @@ def EnablePage(self, name, enabled=True): for ssitem in self.win[name][key][skey].values(): if not isinstance(ssitem, bool) and isinstance(ssitem, int): self.FindWindowById(ssitem).Enable(enabled) - else: - # type(bool) != types.IntType but - # isinstance(bool) == types.IntType - if not isinstance(sitem, bool) and isinstance(sitem, int): - self.FindWindowById(sitem).Enable(enabled) - else: - if not isinstance(item, bool) and isinstance(item, int): - self.FindWindowById(item).Enable(enabled) + elif not isinstance(sitem, bool) and isinstance(sitem, int): + self.FindWindowById(sitem).Enable(enabled) + elif not isinstance(item, bool) and isinstance(item, int): + self.FindWindowById(item).Enable(enabled) def SetMapObjUseMap(self, nvizType, attrb, map=None): """Update dialog widgets when attribute type changed""" @@ -4060,11 +4054,10 @@ def UpdateVectorShow(self, vecType, enabled): self.FindWindowById( self.win["vector"][vecType][win][swin] ).Enable(False) + elif enabled: + self.FindWindowById(self.win["vector"][vecType][win]).Enable(True) else: - if enabled: - self.FindWindowById(self.win["vector"][vecType][win]).Enable(True) - else: - self.FindWindowById(self.win["vector"][vecType][win]).Enable(False) + self.FindWindowById(self.win["vector"][vecType][win]).Enable(False) return True @@ -4560,13 +4553,12 @@ def OnVolumeCheck(self, event): else: # disable -> make transparent self._display.SetIsosurfaceTransp(vid, id, False, "255") + elif list.IsChecked(index): + value = data["slice"][id]["transp"]["value"] + self._display.SetSliceTransp(vid, id, value) else: - if list.IsChecked(index): - value = data["slice"][id]["transp"]["value"] - self._display.SetSliceTransp(vid, id, value) - else: - # disable -> make transparent - self._display.SetSliceTransp(vid, id, 255) + # disable -> make transparent + self._display.SetSliceTransp(vid, id, 255) self.mapWindow.Refresh(False) @@ -4720,11 +4712,10 @@ def OnVolumeDelete(self, event): self.UpdateVolumeIsosurfPage(data["isosurface"][list.GetSelection()]) else: self.UpdateVolumeSlicePage(data["slice"][list.GetSelection()]) + elif mode == "isosurf": + self.UpdateVolumeIsosurfPage(data["attribute"]) else: - if mode == "isosurf": - self.UpdateVolumeIsosurfPage(data["attribute"]) - else: - self.UpdateVolumeSlicePage(None) + self.UpdateVolumeSlicePage(None) self.UpdateIsosurfButtons(list) self.mapWindow.Refresh(False) @@ -5746,18 +5737,17 @@ def UpdateVolumeIsosurfPage(self, data): self.FindWindowById(self.win["volume"][attrb]["const"]).SetColour( color ) - else: + else: # noqa: PLR5501 if data[attrb]["map"]: self.vetoGSelectEvt = True win = self.FindWindowById(self.win["volume"][attrb]["map"]) win.SetValue(value) - else: - if value: - win = self.FindWindowById(self.win["volume"][attrb]["const"]) - if attrb == "topo": - win.SetValue(float(value)) - else: - win.SetValue(self._getPercent(value)) + elif value: + win = self.FindWindowById(self.win["volume"][attrb]["const"]) + if attrb == "topo": + win.SetValue(float(value)) + else: + win.SetValue(self._getPercent(value)) self.SetMapObjUseMap(nvizType="volume", attrb=attrb, map=data[attrb]["map"]) # set inout diff --git a/gui/wxpython/photo2image/ip2i_manager.py b/gui/wxpython/photo2image/ip2i_manager.py index c8a4eb85105..b4d24d19430 100644 --- a/gui/wxpython/photo2image/ip2i_manager.py +++ b/gui/wxpython/photo2image/ip2i_manager.py @@ -802,7 +802,7 @@ def SetGCPSatus(self, item, itemIndex): wxPen = "highest" else: wxPen = "default" - else: + else: # noqa: PLR5501 if self.mapcoordlist[key][5] > self.rmsthresh: wxPen = "highest" else: @@ -2527,7 +2527,7 @@ def UpdateSettings(self): self.parent.activemap.SetSelection(0) self.parent.activemap.Enable(False) self.parent.GetMapToolbar().Enable("zoommenu", enable=False) - else: + else: # noqa: PLR5501 if not self.parent.show_target: self.parent.show_target = True self.parent._mgr.GetPane("target").Show() diff --git a/gui/wxpython/psmap/dialogs.py b/gui/wxpython/psmap/dialogs.py index cfc9f85bed8..42a3ef862e3 100644 --- a/gui/wxpython/psmap/dialogs.py +++ b/gui/wxpython/psmap/dialogs.py @@ -891,7 +891,7 @@ def updateDialog(self): self.mPanel.drawMap.SetValue(True) else: self.mPanel.drawMap.SetValue(False) - else: + else: # noqa: PLR5501 if "vector" in self.parent.openDialogs: found = False for each in self.parent.openDialogs["vector"].vPanel.vectorList: @@ -2053,9 +2053,8 @@ def update(self): vLayer["label"] = item[4] vLayer["lpos"] = item[3] - else: - if self.id in self.instruction: - del self.instruction[self.id] + elif self.id in self.instruction: + del self.instruction[self.id] if "map" in self.parent.parent.openDialogs: self.parent.parent.openDialogs["map"].updateDialog() @@ -4904,13 +4903,12 @@ def _scalebarPanel(self): unitName = self.unitConv.findName(self.scalebarDict["unitsLength"]) if unitName: self.unitsLength.SetStringSelection(unitName) - else: - if self.scalebarDict["unitsLength"] == "auto": - self.unitsLength.SetSelection(0) - elif self.scalebarDict["unitsLength"] == "nautmiles": - self.unitsLength.SetStringSelection( - self.unitConv.findName("nautical miles") - ) + elif self.scalebarDict["unitsLength"] == "auto": + self.unitsLength.SetSelection(0) + elif self.scalebarDict["unitsLength"] == "nautmiles": + self.unitsLength.SetStringSelection( + self.unitConv.findName("nautical miles") + ) self.unitsHeight.SetStringSelection( self.unitConv.findName(self.scalebarDict["unitsHeight"]) ) diff --git a/gui/wxpython/psmap/frame.py b/gui/wxpython/psmap/frame.py index 4f0a2220fd5..8725a0a957d 100644 --- a/gui/wxpython/psmap/frame.py +++ b/gui/wxpython/psmap/frame.py @@ -723,7 +723,7 @@ def OnAddMap(self, event, notebook=False): ) self.openDialogs["mapNotebook"] = dlg self.openDialogs["mapNotebook"].Show() - else: + else: # noqa: PLR5501 if "mapNotebook" in self.openDialogs: self.openDialogs["mapNotebook"].notebook.ChangeSelection(0) else: @@ -1690,11 +1690,10 @@ def OnMouseMoving(self, event): self.SetCursor(self.cursors["sizenwse"]) self.parent.SetStatusText(_("Click and drag to resize object"), 0) self.showResizeHelp = True - else: - if self.showResizeHelp: - self.parent.SetStatusText("", 0) - self.SetCursor(self.cursors["default"]) - self.showResizeHelp = False + elif self.showResizeHelp: + self.parent.SetStatusText("", 0) + self.SetCursor(self.cursors["default"]) + self.showResizeHelp = False def OnLeftDown(self, event): """Left mouse button pressed. diff --git a/gui/wxpython/psmap/instructions.py b/gui/wxpython/psmap/instructions.py index bc10a1c0e46..f52bad96069 100644 --- a/gui/wxpython/psmap/instructions.py +++ b/gui/wxpython/psmap/instructions.py @@ -2083,7 +2083,7 @@ def __str__(self): " rgbcolumn $rgbcolumn\n" ).substitute(dic) vInstruction += string.Template(" fcolor $fcolor\n").substitute(dic) - else: + else: # noqa: PLR5501 if dic["rgbcolumn"]: vInstruction += string.Template( " rgbcolumn $rgbcolumn\n" diff --git a/gui/wxpython/rdigit/g.gui.rdigit.py b/gui/wxpython/rdigit/g.gui.rdigit.py index ac68525cd6e..a05fdb547bb 100755 --- a/gui/wxpython/rdigit/g.gui.rdigit.py +++ b/gui/wxpython/rdigit/g.gui.rdigit.py @@ -199,23 +199,22 @@ def OnMapCreated(self, name, ltype): ) else: kwargs["edit_map"] = edit_map - else: - if kwargs["base_map"]: - base_map = gs.find_file( - name=kwargs["base_map"], - element="raster", - mapset=mapset, - )["fullname"] - if not base_map: - gs.fatal( - _( - "Base raster map <{}> not found in " - "current mapset.".format( - options["base"], - ), + elif kwargs["base_map"]: + base_map = gs.find_file( + name=kwargs["base_map"], + element="raster", + mapset=mapset, + )["fullname"] + if not base_map: + gs.fatal( + _( + "Base raster map <{}> not found in " + "current mapset.".format( + options["base"], ), - ) - kwargs["base_map"] = base_map + ), + ) + kwargs["base_map"] = base_map # allow immediate rendering driver = UserSettings.Get( diff --git a/gui/wxpython/startup/guiutils.py b/gui/wxpython/startup/guiutils.py index d9f59696375..fa4d0a8760a 100644 --- a/gui/wxpython/startup/guiutils.py +++ b/gui/wxpython/startup/guiutils.py @@ -715,7 +715,7 @@ def switch_mapset_interactively( giface.currentMapsetChanged.emit( dbase=None, location=location, mapset=mapset ) - else: + else: # noqa: PLR5501 if RunCommand("g.mapset", parent=guiparent, mapset=mapset) == 0: if show_confirmation: GMessage( diff --git a/gui/wxpython/tplot/frame.py b/gui/wxpython/tplot/frame.py index 4a1cd9f1ca6..2f738c93644 100755 --- a/gui/wxpython/tplot/frame.py +++ b/gui/wxpython/tplot/frame.py @@ -739,11 +739,10 @@ def _setLabels(self, x): """Function to set the right labels""" if self.drawX != "": self.axes2d.set_xlabel(self.drawX) + elif self.temporalType == "absolute": + self.axes2d.set_xlabel(_("Temporal resolution: %s" % x)) else: - if self.temporalType == "absolute": - self.axes2d.set_xlabel(_("Temporal resolution: %s" % x)) - else: - self.axes2d.set_xlabel(_("Time [%s]") % self.unit) + self.axes2d.set_xlabel(_("Time [%s]") % self.unit) if self.drawY != "": self.axes2d.set_ylabel(self.drawY) else: diff --git a/gui/wxpython/vdigit/mapwindow.py b/gui/wxpython/vdigit/mapwindow.py index 43841b53236..ff7e62ebaa9 100644 --- a/gui/wxpython/vdigit/mapwindow.py +++ b/gui/wxpython/vdigit/mapwindow.py @@ -534,7 +534,7 @@ def OnLeftDownDisplayCA(self, event): # highlight feature & re-draw map if not self.parent.dialogs["attributes"].IsShown(): self.parent.dialogs["attributes"].Show() - else: + else: # noqa: PLR5501 if ( self.parent.dialogs["attributes"] and self.parent.dialogs["attributes"].IsShown() @@ -561,7 +561,7 @@ def OnLeftDownDisplayCA(self, event): # highlight feature & re-draw map if not self.parent.dialogs["category"].IsShown(): self.parent.dialogs["category"].Show() - else: + else: # noqa: PLR5501 if self.parent.dialogs["category"].IsShown(): self.parent.dialogs["category"].Hide() @@ -826,29 +826,26 @@ def OnLeftUpVarious(self, event): if nselected > 0: self.digit.GetDisplay().SetSelected(selected) + # -> moveLine || deleteLine, etc. (select by point/box) + elif action == "moveLine" and len(self.digit.GetDisplay().GetSelected()) > 0: + nselected = 0 + elif action == "deleteArea": + nselected = int( + self.digit.GetDisplay().SelectAreaByPoint(pos1)["area"] != -1 + ) else: - # -> moveLine || deleteLine, etc. (select by point/box) - if action == "moveLine" and len(self.digit.GetDisplay().GetSelected()) > 0: - nselected = 0 + if action == "moveLine": + drawSeg = True else: - if action == "deleteArea": - nselected = int( - self.digit.GetDisplay().SelectAreaByPoint(pos1)["area"] != -1 - ) - else: - if action == "moveLine": - drawSeg = True - else: - drawSeg = False + drawSeg = False - nselected = self.digit.GetDisplay().SelectLinesByBox( - bbox=(pos1, pos2), drawSeg=drawSeg - ) - if nselected == 0: - nselected = int( - self.digit.GetDisplay().SelectLineByPoint(pos1)["line"] - != -1 - ) + nselected = self.digit.GetDisplay().SelectLinesByBox( + bbox=(pos1, pos2), drawSeg=drawSeg + ) + if nselected == 0: + nselected = int( + self.digit.GetDisplay().SelectLineByPoint(pos1)["line"] != -1 + ) if nselected > 0: if action in {"moveLine", "moveVertex"} and hasattr(self, "moveInfo"): @@ -891,14 +888,14 @@ def OnLeftUpVarious(self, event): # -> move line || move vertex self.UpdateMap(render=False) - else: # no vector object found - if not ( - action in {"moveLine", "moveVertex"} - and hasattr(self, "moveInfo") - and len(self.moveInfo["id"]) > 0 - ): - # avoid left-click when features are already selected - self.UpdateMap(render=False, renderVector=False) + # no vector object found + elif not ( + action in {"moveLine", "moveVertex"} + and hasattr(self, "moveInfo") + and len(self.moveInfo["id"]) > 0 + ): + # avoid left-click when features are already selected + self.UpdateMap(render=False, renderVector=False) def OnLeftUpModifyLine(self, event): """Left mouse button released - vector digitizer split line, @@ -986,10 +983,9 @@ def OnLeftUpCopyLine(self, event): ) else: self.layerTmp.SetCmd(dVectTmp) - else: - if self.layerTmp: - self.Map.DeleteLayer(self.layerTmp) - self.layerTmp = None + elif self.layerTmp: + self.Map.DeleteLayer(self.layerTmp) + self.layerTmp = None self.UpdateMap(render=True, renderVector=True) @@ -1206,14 +1202,11 @@ def _onRightUp(self, event): < 0 ): return - else: - if ( - self.digit.CopyCats( - self.copyCatsList, self.copyCatsIds, copyAttrb=True - ) - < 0 - ): - return + elif ( + self.digit.CopyCats(self.copyCatsList, self.copyCatsIds, copyAttrb=True) + < 0 + ): + return del self.copyCatsList del self.copyCatsIds diff --git a/gui/wxpython/vdigit/preferences.py b/gui/wxpython/vdigit/preferences.py index 51c14943073..b825748a025 100644 --- a/gui/wxpython/vdigit/preferences.py +++ b/gui/wxpython/vdigit/preferences.py @@ -808,11 +808,10 @@ def OnChangeSnappingValue(self, event): region = self.parent.MapWindow.Map.GetRegion() res = (region["nsres"] + region["ewres"]) / 2.0 threshold = self.digit.GetDisplay().GetThreshold(value=res) + elif self.snappingUnit.GetSelection() == 1: # map units + threshold = value else: - if self.snappingUnit.GetSelection() == 1: # map units - threshold = value - else: - threshold = self.digit.GetDisplay().GetThreshold(value=value) + threshold = self.digit.GetDisplay().GetThreshold(value=value) if value == 0: self.snappingInfo.SetLabel(_("Snapping disabled")) @@ -1011,13 +1010,12 @@ def UpdateSettings(self): "column": column, "units": unitsKey, } - else: - if ( - item - and tree.GetLayerInfo(item, key="vdigit") - and key in tree.GetLayerInfo(item, key="vdigit")["geomAttr"] - ): - del tree.GetLayerInfo(item, key="vdigit")["geomAttr"][key] + elif ( + item + and tree.GetLayerInfo(item, key="vdigit") + and key in tree.GetLayerInfo(item, key="vdigit")["geomAttr"] + ): + del tree.GetLayerInfo(item, key="vdigit")["geomAttr"][key] # query tool if self.queryLength.GetValue(): diff --git a/gui/wxpython/vdigit/toolbars.py b/gui/wxpython/vdigit/toolbars.py index 430f487245a..1e8d394bddc 100644 --- a/gui/wxpython/vdigit/toolbars.py +++ b/gui/wxpython/vdigit/toolbars.py @@ -672,16 +672,12 @@ def EnableRedo(self, enable=True): """ self._enableTool(self.redo, enable) - def _enableTool(self, tool, enable): + def _enableTool(self, tool, enable: bool): if not self.FindById(tool): return - if enable: - if self.GetToolEnabled(tool) is False: - self.EnableTool(tool, True) - else: - if self.GetToolEnabled(tool) is True: - self.EnableTool(tool, False) + if self.GetToolEnabled(tool) is not bool(enable): + self.EnableTool(tool, bool(enable)) def GetAction(self, type="desc"): """Get current action info""" diff --git a/gui/wxpython/vdigit/wxdisplay.py b/gui/wxpython/vdigit/wxdisplay.py index 8891908c2cb..a318105bd9e 100644 --- a/gui/wxpython/vdigit/wxdisplay.py +++ b/gui/wxpython/vdigit/wxdisplay.py @@ -315,57 +315,56 @@ def _drawObject(self, robj): pdc.SetId(dcId) dcId += 2 self._drawCross(pdc, p) - else: - if dcId > 0 and self._drawSegments: - self.fisrtNode = True - self.lastNodeId = robj.npoints * 2 - 1 - dcId = 2 # first segment - i = 0 - while i < robj.npoints - 1: - point_beg = wx.Point(robj.point[i].x, robj.point[i].y) - point_end = wx.Point(robj.point[i + 1].x, robj.point[i + 1].y) - # set unique id & set bbox for each segment - pdc.SetId(dcId) - pdc.SetPen(pen) - pdc.SetIdBounds(dcId - 1, Rect(point_beg.x, point_beg.y, 0, 0)) - pdc.SetIdBounds( - dcId, - Rect( - point_beg.x, - point_beg.y, - point_end.x - point_beg.x, - point_end.y - point_beg.y, - ), - ) - pdc.DrawLine(point_beg.x, point_beg.y, point_end.x, point_end.y) - i += 1 - dcId += 2 + elif dcId > 0 and self._drawSegments: + self.fisrtNode = True + self.lastNodeId = robj.npoints * 2 - 1 + dcId = 2 # first segment + i = 0 + while i < robj.npoints - 1: + point_beg = wx.Point(robj.point[i].x, robj.point[i].y) + point_end = wx.Point(robj.point[i + 1].x, robj.point[i + 1].y) + # set unique id & set bbox for each segment + pdc.SetId(dcId) + pdc.SetPen(pen) + pdc.SetIdBounds(dcId - 1, Rect(point_beg.x, point_beg.y, 0, 0)) pdc.SetIdBounds( - dcId - 1, + dcId, Rect( - robj.point[robj.npoints - 1].x, - robj.point[robj.npoints - 1].y, - 0, - 0, + point_beg.x, + point_beg.y, + point_end.x - point_beg.x, + point_end.y - point_beg.y, ), ) - else: - points = [] - for i in range(robj.npoints): - p = robj.point[i] - points.append(wx.Point(p.x, p.y)) - if len(points) <= 1: - self.log.write( - _( - "WARNING: Zero-length line or boundary drawing skipped. " - "Use v.clean to remove it." - ) + pdc.DrawLine(point_beg.x, point_beg.y, point_end.x, point_end.y) + i += 1 + dcId += 2 + pdc.SetIdBounds( + dcId - 1, + Rect( + robj.point[robj.npoints - 1].x, + robj.point[robj.npoints - 1].y, + 0, + 0, + ), + ) + else: + points = [] + for i in range(robj.npoints): + p = robj.point[i] + points.append(wx.Point(p.x, p.y)) + if len(points) <= 1: + self.log.write( + _( + "WARNING: Zero-length line or boundary drawing skipped. " + "Use v.clean to remove it." ) - return - if robj.type == TYPE_AREA: - pdc.DrawPolygon(points) - else: - pdc.DrawLines(points) + ) + return + if robj.type == TYPE_AREA: + pdc.DrawPolygon(points) + else: + pdc.DrawLines(points) def _definePen(self, rtype): """Define pen/brush based on rendered object) @@ -878,10 +877,9 @@ def GetSelectedVertex(self, pos): if idx == 0: minDist = dist Gid = idx - else: - if minDist > dist: - minDist = dist - Gid = idx + elif minDist > dist: + minDist = dist + Gid = idx vx, vy = self._cell2Pixel(points.x[idx], points.y[idx], points.z[idx]) rect = Rect(vx, vy, 0, 0) @@ -925,7 +923,7 @@ def GetRegionSelected(self): if area > 0 and area <= nareas: if not Vect_get_area_box(self.poMapInfo, area, byref(lineBox)): continue - else: + else: # noqa: PLR5501 if not Vect_get_line_box(self.poMapInfo, line, byref(lineBox)): continue @@ -997,7 +995,7 @@ def OpenMap(self, name, mapset, update=True, tmp=False): open_fn = Vect_open_tmp_update else: open_fn = Vect_open_update - else: + else: # noqa: PLR5501 if tmp: open_fn = Vect_open_tmp_old else: diff --git a/gui/wxpython/vnet/dialogs.py b/gui/wxpython/vnet/dialogs.py index d7b3bb05849..3d829ffd7be 100644 --- a/gui/wxpython/vnet/dialogs.py +++ b/gui/wxpython/vnet/dialogs.py @@ -1148,7 +1148,7 @@ def AnalysisChanged(self, analysis): if not item[1]: self.CheckItem(iItem, False) - else: + else: # noqa: PLR5501 if self.IsShown("type"): self.HideColumn("type") diff --git a/gui/wxpython/vnet/vnet_data.py b/gui/wxpython/vnet/vnet_data.py index e978a82314e..e86931ca294 100644 --- a/gui/wxpython/vnet/vnet_data.py +++ b/gui/wxpython/vnet/vnet_data.py @@ -1200,11 +1200,10 @@ def _savePreviousHist(self, newHist, oldHist): else: newHist.write("%s%s%s" % ("\n", line, "\n")) self.histStepsNum = newHistStepsNum + elif newHistStepsNum >= self.maxHistSteps: + self._parseLine(line, removedHistStep) else: - if newHistStepsNum >= self.maxHistSteps: - self._parseLine(line, removedHistStep) - else: - newHist.write("%s" % line) + newHist.write("%s" % line) return removedHistData @@ -1272,9 +1271,9 @@ def _parseValue(self, value, read=False): value = float(value) except ValueError: pass - else: # -> write data - if isinstance(value, type(())): # -> color - value = str(value[0]) + ":" + str(value[1]) + ":" + str(value[2]) + # -> write data + elif isinstance(value, type(())): # -> color + value = str(value[0]) + ":" + str(value[1]) + ":" + str(value[2]) return value diff --git a/gui/wxpython/wxplot/dialogs.py b/gui/wxpython/wxplot/dialogs.py index d2273d7083c..bae305a1817 100755 --- a/gui/wxpython/wxplot/dialogs.py +++ b/gui/wxpython/wxplot/dialogs.py @@ -464,9 +464,8 @@ def _do_layout(self): ) if self.rasterRadio.GetValue(): self.gselection.Disable() - else: - if self.group is not None: - self.gselection.SetValue(self.group) + elif self.group is not None: + self.gselection.SetValue(self.group) box.Add(self.gselection, pos=(2, 1)) # diff --git a/gui/wxpython/wxplot/histogram.py b/gui/wxpython/wxplot/histogram.py index bf86e1e8ff3..1c7d4bf21d9 100644 --- a/gui/wxpython/wxplot/histogram.py +++ b/gui/wxpython/wxplot/histogram.py @@ -145,11 +145,10 @@ def SetupHistogram(self): # if self.maptype == "group": self.ptitle = _("Histogram of image group <%s>") % self.group + elif len(self.rasterList) == 1: + self.ptitle = _("Histogram of raster map <%s>") % self.rasterList[0] else: - if len(self.rasterList) == 1: - self.ptitle = _("Histogram of raster map <%s>") % self.rasterList[0] - else: - self.ptitle = _("Histogram of selected raster maps") + self.ptitle = _("Histogram of selected raster maps") # # set xlabel based on first raster map in list to be histogrammed diff --git a/lib/init/grass.py b/lib/init/grass.py index 3548eab2b08..dac7a6d818e 100755 --- a/lib/init/grass.py +++ b/lib/init/grass.py @@ -673,20 +673,18 @@ def check_gui(expected_gui): if msg: warning(_("{}\nSwitching to text based interface mode.").format(msg)) grass_gui = "text" - - else: - # Display a message if a graphical interface was expected - if expected_gui != "text": - # Set the interface mode to text - warning( - _( - "It appears that the X Windows system is not active.\n" - "A graphical based user interface is not supported.\n" - "(DISPLAY variable is not set.)\n" - "Switching to text based interface mode." - ) + # Display a message if a graphical interface was expected + elif expected_gui != "text": + # Set the interface mode to text + warning( + _( + "It appears that the X Windows system is not active.\n" + "A graphical based user interface is not supported.\n" + "(DISPLAY variable is not set.)\n" + "Switching to text based interface mode." ) - grass_gui = "text" + ) + grass_gui = "text" return grass_gui @@ -872,103 +870,96 @@ def set_mapset( else: suggestion = _("Maybe you meant a different directory.") fatal("{reason}\n{suggestion}".format(**locals())) - else: - # 'path' is not valid and the user wants to create - # mapset on the fly - # check if 'location_name' is a valid GRASS location - if not is_location_valid(gisdbase, location_name): - if not (tmp_location or tmp_mapset): - # 'location_name' is not a valid GRASS location - # and user requested its creation, so we parsed - # the path wrong and need to move one level - # and use 'PERMANENT' mapset - # (we already got that right in case of tmploc) - gisdbase = os.path.join(gisdbase, location_name) - location_name = mapset - mapset = "PERMANENT" - if tmp_mapset: - suggestion = get_location_invalid_suggestion( - gisdbase, location_name - ) - reason = get_location_invalid_reason(gisdbase, location_name) - if suggestion: - fatal("{reason}\n{suggestion}".format(**locals())) - else: - fatal(reason) - if not can_create_location(gisdbase, location_name): - fatal(cannot_create_location_reason(gisdbase, location_name)) - # create new location based on the provided EPSG/... - if not geofile: - fatal(_("Provide CRS to create a project")) - if not tmp_location: - # Report report only when new location is not temporary. - message( - _("Creating new GRASS GIS project <{}>...").format( - location_name - ) + # 'path' is not valid and the user wants to create + # mapset on the fly + # check if 'location_name' is a valid GRASS location + elif not is_location_valid(gisdbase, location_name): + if not (tmp_location or tmp_mapset): + # 'location_name' is not a valid GRASS location + # and user requested its creation, so we parsed + # the path wrong and need to move one level + # and use 'PERMANENT' mapset + # (we already got that right in case of tmploc) + gisdbase = os.path.join(gisdbase, location_name) + location_name = mapset + mapset = "PERMANENT" + if tmp_mapset: + suggestion = get_location_invalid_suggestion( + gisdbase, location_name + ) + reason = get_location_invalid_reason(gisdbase, location_name) + if suggestion: + fatal("{reason}\n{suggestion}".format(**locals())) + else: + fatal(reason) + if not can_create_location(gisdbase, location_name): + fatal(cannot_create_location_reason(gisdbase, location_name)) + # create new location based on the provided EPSG/... + if not geofile: + fatal(_("Provide CRS to create a project")) + if not tmp_location: + # Report report only when new location is not temporary. + message( + _("Creating new GRASS GIS project <{}>...").format( + location_name ) - create_location(gisdbase, location_name, geofile) + ) + create_location(gisdbase, location_name, geofile) + else: + # 'location_name' is a valid GRASS location, + # create new mapset + if os.path.isfile(path): + # not a valid mapset, but dir exists, assuming + # broken/incomplete mapset + fatal( + _( + "Unable to create new mapset <{mapset}>" + " because <{path}> is a file." + ).format(mapset=mapset, path=path) + ) + elif os.path.isdir(path): + # not a valid mapset, but dir exists, assuming + # broken/incomplete mapset + warning( + _( + "The mapset <{}> is missing the WIND file" + " (computational region). It will be" + " fixed now. Note that this warning" + " may become an error in future versions." + ).format(mapset) + ) else: - # 'location_name' is a valid GRASS location, - # create new mapset - if os.path.isfile(path): - # not a valid mapset, but dir exists, assuming - # broken/incomplete mapset + if geofile: fatal( _( - "Unable to create new mapset <{mapset}>" - " because <{path}> is a file." - ).format(mapset=mapset, path=path) - ) - elif os.path.isdir(path): - # not a valid mapset, but dir exists, assuming - # broken/incomplete mapset - warning( - _( - "The mapset <{}> is missing the WIND file" - " (computational region). It will be" - " fixed now. Note that this warning" - " may become an error in future versions." - ).format(mapset) + "No CRS is needed for creating mapset <{mapset}>, " + "but <{geofile}> was provided as CRS." + " Did you mean to create a new project?" + ).format(mapset=mapset, geofile=geofile) ) - else: - if geofile: - fatal( - _( - "No CRS is needed for creating mapset <{mapset}>, " - "but <{geofile}> was provided as CRS." - " Did you mean to create a new project?" - ).format(mapset=mapset, geofile=geofile) - ) - if not tmp_mapset: - message( - _("Creating new GRASS GIS mapset <{}>...").format( - mapset - ) - ) - # create mapset directory - os.mkdir(path) - if tmp_mapset: - # The tmp location is handled by (re-)using the - # tmpdir, but we need to take care of the tmp - # mapset which is only a subtree in an existing - # location. We simply remove the tree at exit. - # All mapset cleaning functions should succeed - # because they are called before exit or registered - # only later (and thus called before this one). - # (Theoretically, they could be disabled if that's - # just cleaning a files in the mapset directory.) - atexit.register( - lambda: shutil.rmtree(path, ignore_errors=True) - ) - # make directory a mapset, add the region - # copy PERMANENT/DEFAULT_WIND to /WIND - s = readfile( - os.path.join( - gisdbase, location_name, "PERMANENT", "DEFAULT_WIND" + if not tmp_mapset: + message( + _("Creating new GRASS GIS mapset <{}>...").format(mapset) ) - ) - writefile(os.path.join(path, "WIND"), s) + # create mapset directory + os.mkdir(path) + if tmp_mapset: + # The tmp location is handled by (re-)using the + # tmpdir, but we need to take care of the tmp + # mapset which is only a subtree in an existing + # location. We simply remove the tree at exit. + # All mapset cleaning functions should succeed + # because they are called before exit or registered + # only later (and thus called before this one). + # (Theoretically, they could be disabled if that's + # just cleaning a files in the mapset directory.) + atexit.register(lambda: shutil.rmtree(path, ignore_errors=True)) + # make directory a mapset, add the region + # copy PERMANENT/DEFAULT_WIND to /WIND + s = readfile( + os.path.join(gisdbase, location_name, "PERMANENT", "DEFAULT_WIND") + ) + writefile(os.path.join(path, "WIND"), s) add_mapset_to_gisrc(gisrc, gisdbase, location_name, mapset) else: fatal( @@ -2438,25 +2429,24 @@ def main(): else: # Use the last used mapset. set_mapset(gisrc=gisrc, arg=last_mapset_path) + # Mapset was specified in command line parameters. + elif params.tmp_location: + # tmp loc requires other things to be set as well + set_mapset( + gisrc=gisrc, + geofile=params.geofile, + create_new=True, + tmp_location=params.tmp_location, + tmpdir=tmpdir, + ) + elif params.create_new and params.geofile: + set_mapset( + gisrc=gisrc, arg=params.mapset, geofile=params.geofile, create_new=True + ) + elif params.tmp_mapset: + set_mapset(gisrc=gisrc, arg=params.mapset, tmp_mapset=params.tmp_mapset) else: - # Mapset was specified in command line parameters. - if params.tmp_location: - # tmp loc requires other things to be set as well - set_mapset( - gisrc=gisrc, - geofile=params.geofile, - create_new=True, - tmp_location=params.tmp_location, - tmpdir=tmpdir, - ) - elif params.create_new and params.geofile: - set_mapset( - gisrc=gisrc, arg=params.mapset, geofile=params.geofile, create_new=True - ) - elif params.tmp_mapset: - set_mapset(gisrc=gisrc, arg=params.mapset, tmp_mapset=params.tmp_mapset) - else: - set_mapset(gisrc=gisrc, arg=params.mapset, create_new=params.create_new) + set_mapset(gisrc=gisrc, arg=params.mapset, create_new=params.create_new) # Set GISDBASE, LOCATION_NAME, MAPSET, LOCATION from $GISRC # e.g. wxGUI startup screen writes to the gisrc file, diff --git a/man/parser_standard_options.py b/man/parser_standard_options.py index 87ffc6c4979..b6ce30b1bb2 100644 --- a/man/parser_standard_options.py +++ b/man/parser_standard_options.py @@ -72,18 +72,17 @@ def parse_glines(glines): res[key] = [ default, ] - else: - if key is not None: - if key not in res: - res[key] = [] - start, end = 0, -1 - if line.startswith("_("): - start = 2 - if line.endswith(");"): - end = -3 - elif line.endswith(";"): - end = -2 - res[key].append(line[start:end]) + elif key is not None: + if key not in res: + res[key] = [] + start, end = 0, -1 + if line.startswith("_("): + start = 2 + if line.endswith(");"): + end = -3 + elif line.endswith(";"): + end = -2 + res[key].append(line[start:end]) # pprint(glines) # pprint(res) return res diff --git a/pyproject.toml b/pyproject.toml index b9739df49c9..21afb25d782 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -183,7 +183,6 @@ ignore = [ "PLR1704", # redefined-argument-from-local "PLR1733", # unnecessary-dict-index-lookup "PLR2004", # magic-value-comparison - "PLR5501", # collapsible-else-if "PLR6104", # non-augmented-assignment "PLR6201", # literal-membership "PLR6301", # no-self-use diff --git a/python/grass/gunittest/checkers.py b/python/grass/gunittest/checkers.py index 80ee38e5fdc..76f8100577c 100644 --- a/python/grass/gunittest/checkers.py +++ b/python/grass/gunittest/checkers.py @@ -93,14 +93,14 @@ def unify_units(dic): for n in range(len(dic["unit"])): if dic["unit"][n] in item: dic["unit"][n] = item[0] - else: + else: # noqa: PLR5501 if dic["unit"] in item: dic["unit"] = item[0] if not isinstance(dic["units"], str): for n in range(len(dic["units"])): if dic["units"][n] in item: dic["units"][n] = item[0] - else: + else: # noqa: PLR5501 if dic["units"] in item: dic["units"] = item[0] return dic @@ -222,7 +222,7 @@ def text_to_keyvalue( " Previous line's key is <%s>" ) % key raise ValueError(msg) - else: + else: # noqa: PLR5501 # line contains something but not separator if not skip_invalid: # TODO: here should go _ for translation @@ -312,9 +312,8 @@ def values_equal(value_a, value_b, precision=0.000001): # apply this function for comparison of items in the list if not values_equal(value_a[i], value_b[i], precision): return False - else: - if value_a != value_b: - return False + elif value_a != value_b: + return False return True diff --git a/python/grass/gunittest/invoker.py b/python/grass/gunittest/invoker.py index 9a19be4c85d..f07e8dd5a84 100644 --- a/python/grass/gunittest/invoker.py +++ b/python/grass/gunittest/invoker.py @@ -246,11 +246,10 @@ def try_decode(data, encodings): with open(stderr_path, "w") as stderr_file: if type(stderr) == "bytes": stderr_file.write(decode(stderr)) + elif isinstance(stderr, str): + stderr_file.write(stderr) else: - if isinstance(stderr, str): - stderr_file.write(stderr) - else: - stderr_file.write(stderr.encode("utf8")) + stderr_file.write(stderr.encode("utf8")) self._file_anonymizer.anonymize([stdout_path, stderr_path]) test_summary = update_keyval_file( diff --git a/python/grass/jupyter/baseseriesmap.py b/python/grass/jupyter/baseseriesmap.py index 50f28f58049..db915297e9c 100644 --- a/python/grass/jupyter/baseseriesmap.py +++ b/python/grass/jupyter/baseseriesmap.py @@ -74,12 +74,11 @@ def __getattr__(self, name): def wrapper(**kwargs): if not self._baseseries_added: self._base_layer_calls.append((grass_module, kwargs)) + elif self._base_calls is not None: + for row in self._base_calls: + row.append((grass_module, kwargs)) else: - if self._base_calls is not None: - for row in self._base_calls: - row.append((grass_module, kwargs)) - else: - self._base_calls.append((grass_module, kwargs)) + self._base_calls.append((grass_module, kwargs)) return wrapper diff --git a/python/grass/jupyter/region.py b/python/grass/jupyter/region.py index 67eb64104a9..fc4b406b80f 100644 --- a/python/grass/jupyter/region.py +++ b/python/grass/jupyter/region.py @@ -197,16 +197,13 @@ def set_region_from_command(self, module, **kwargs): vector=name, env=self._env ) self._extent_set = True - else: - if not self._resolution_set and not self._extent_set: - self._env["GRASS_REGION"] = gs.region_env( - raster=name, env=self._env - ) - self._extent_set = True - self._resolution_set = True - elif not self._resolution_set: - self._env["GRASS_REGION"] = gs.region_env(align=name, env=self._env) - self._resolution_set = True + elif not self._resolution_set and not self._extent_set: + self._env["GRASS_REGION"] = gs.region_env(raster=name, env=self._env) + self._extent_set = True + self._resolution_set = True + elif not self._resolution_set: + self._env["GRASS_REGION"] = gs.region_env(align=name, env=self._env) + self._resolution_set = True except CalledModuleError: return diff --git a/python/grass/pygrass/utils.py b/python/grass/pygrass/utils.py index 9727ed26791..e8f4ce68a43 100644 --- a/python/grass/pygrass/utils.py +++ b/python/grass/pygrass/utils.py @@ -332,7 +332,7 @@ def get_raster_for_points(poi_vector, raster, column=None, region=None): if column: if val is not None and not isnan(val): poi.attrs[column] = val - else: + else: # noqa: PLR5501 if val is not None and not isnan(val): result.append((poi.id, poi.x, poi.y, val)) else: diff --git a/python/grass/pygrass/vector/geometry.py b/python/grass/pygrass/vector/geometry.py index 6ddbd70db7d..591ebf38978 100644 --- a/python/grass/pygrass/vector/geometry.py +++ b/python/grass/pygrass/vector/geometry.py @@ -126,15 +126,14 @@ def get_xyz(pnt): z = 0.0 else: x, y, z = pnt.x, pnt.y, pnt.z + elif len(pnt) == 2: + x, y = pnt + z = 0.0 + elif len(pnt) == 3: + x, y, z = pnt else: - if len(pnt) == 2: - x, y = pnt - z = 0.0 - elif len(pnt) == 3: - x, y, z = pnt - else: - str_error = "The the format of the point is not supported: {0!r}" - raise ValueError(str_error.format(pnt)) + str_error = "The the format of the point is not supported: {0!r}" + raise ValueError(str_error.format(pnt)) return x, y, z diff --git a/python/grass/script/core.py b/python/grass/script/core.py index b22815bafb6..20d3f358a82 100644 --- a/python/grass/script/core.py +++ b/python/grass/script/core.py @@ -124,11 +124,11 @@ def _make_unicode(val, enc): """ if val is None or enc is None: return val + + if enc == "default": + return decode(val) else: - if enc == "default": - return decode(val) - else: - return decode(val, encoding=enc) + return decode(val, encoding=enc) def get_commands(*, env=None): @@ -1166,9 +1166,8 @@ def compare_key_value_text_files( # We compare the sum of the entries if abs(sum(dict_a[key]) - sum(dict_b[key])) > precision: return False - else: - if dict_a[key] != dict_b[key]: - return False + elif dict_a[key] != dict_b[key]: + return False return True @@ -1567,13 +1566,12 @@ def list_grouped( name, ] } + elif mapset in result: + result[mapset].append(name) else: - if mapset in result: - result[mapset].append(name) - else: - result[mapset] = [ - name, - ] + result[mapset] = [ + name, + ] return result diff --git a/python/grass/script/task.py b/python/grass/script/task.py index 9ab76eedcb8..7d635dc83bb 100644 --- a/python/grass/script/task.py +++ b/python/grass/script/task.py @@ -148,9 +148,8 @@ def get_param(self, value, element="name", raiseError=True): if isinstance(val, (list, tuple)): if value in val: return p - else: - if p[element] == value: - return p + elif p[element] == value: + return p if raiseError: raise ValueError( diff --git a/python/grass/semantic_label/reader.py b/python/grass/semantic_label/reader.py index 1da67aff7f9..591006fe352 100644 --- a/python/grass/semantic_label/reader.py +++ b/python/grass/semantic_label/reader.py @@ -138,7 +138,7 @@ def print_info(self, shortcut=None, band=None, semantic_label=None, extended=Fal else: for iband in item["bands"]: self._print_label_extended(iband, item["bands"]) - else: + else: # noqa: PLR5501 # basic information only if band: self._print_label( diff --git a/python/grass/temporal/abstract_map_dataset.py b/python/grass/temporal/abstract_map_dataset.py index 576ac347d7e..aa2472a9ee9 100644 --- a/python/grass/temporal/abstract_map_dataset.py +++ b/python/grass/temporal/abstract_map_dataset.py @@ -499,11 +499,9 @@ def set_absolute_time(self, start_time, end_time=None): % {"type": self.get_type(), "id": self.get_map_id()} ) return False - else: - # Do not create an interval in case start and end time are - # equal - if start_time == end_time: - end_time = None + # Do not create an interval in case start and end time are equal + elif start_time == end_time: + end_time = None self.base.set_ttype("absolute") self.absolute_time.set_start_time(start_time) @@ -619,11 +617,9 @@ def set_relative_time(self, start_time, end_time, unit): % {"type": self.get_type(), "id": self.get_id()} ) return False - else: - # Do not create an interval in case start and end time are - # equal - if start_time == end_time: - end_time = None + # Do not create an interval in case start and end time are equal + elif start_time == end_time: + end_time = None self.base.set_ttype("relative") diff --git a/python/grass/temporal/abstract_space_time_dataset.py b/python/grass/temporal/abstract_space_time_dataset.py index 6376261cf72..ab76ab4112c 100644 --- a/python/grass/temporal/abstract_space_time_dataset.py +++ b/python/grass/temporal/abstract_space_time_dataset.py @@ -2151,7 +2151,7 @@ def snap_map_list(maps): maps[i].set_relative_time( start, start_next, maps[i].get_relative_time_unit() ) - else: + else: # noqa: PLR5501 if maps[i].is_time_absolute(): maps[i].set_absolute_time(start, end) elif maps[i].is_time_relative(): diff --git a/python/grass/temporal/base.py b/python/grass/temporal/base.py index 887ed63fa2a..f4175c77139 100644 --- a/python/grass/temporal/base.py +++ b/python/grass/temporal/base.py @@ -112,11 +112,10 @@ def serialize(self, type, table, where=None): sql += "?" else: sql += "%s" + elif self.dbmi_paramstyle == "qmark": + sql += " ,?" else: - if self.dbmi_paramstyle == "qmark": - sql += " ,?" - else: - sql += " ,%s" + sql += " ,%s" count += 1 args.append(self.D[key]) sql += ") " @@ -138,7 +137,7 @@ def serialize(self, type, table, where=None): else: sql += " %s " % key sql += "= %s " - else: + else: # noqa: PLR5501 if self.dbmi_paramstyle == "qmark": sql += " ,%s = ? " % key else: @@ -161,7 +160,7 @@ def serialize(self, type, table, where=None): else: sql += " %s " % key sql += "= %s " - else: + else: # noqa: PLR5501 if self.dbmi_paramstyle == "qmark": sql += " ,%s = ? " % key else: diff --git a/python/grass/temporal/core.py b/python/grass/temporal/core.py index 12dafeb4fd5..0df95106e0f 100644 --- a/python/grass/temporal/core.py +++ b/python/grass/temporal/core.py @@ -1267,7 +1267,7 @@ def __init__(self, backend=None, dbstring=None): self.dbmi = sqlite3 else: self.dbmi = psycopg2 - else: + else: # noqa: PLR5501 if decode(backend) == "sqlite": self.dbmi = sqlite3 else: @@ -1402,18 +1402,17 @@ def mogrify_sql_statement(self, content): if self.dbmi.__name__ == "psycopg2": if len(args) == 0: return sql + elif self.connected: + try: + return self.cursor.mogrify(sql, args) + except Exception as exc: + print(sql, args) + raise exc else: - if self.connected: - try: - return self.cursor.mogrify(sql, args) - except Exception as exc: - print(sql, args) - raise exc - else: - self.connect() - statement = self.cursor.mogrify(sql, args) - self.close() - return statement + self.connect() + statement = self.cursor.mogrify(sql, args) + self.close() + return statement elif self.dbmi.__name__ == "sqlite3": if len(args) == 0: diff --git a/python/grass/temporal/datetime_math.py b/python/grass/temporal/datetime_math.py index b0c0d81cf9d..3d4b2869854 100644 --- a/python/grass/temporal/datetime_math.py +++ b/python/grass/temporal/datetime_math.py @@ -817,7 +817,7 @@ def check_datetime_string(time_string, use_dateutil=True): time_format = "%Y-%m-%dT%H:%M:%S.%f" else: time_format = "%Y-%m-%d %H:%M:%S.%f" - else: + else: # noqa: PLR5501 if "T" in time_string: time_format = "%Y-%m-%dT%H:%M:%S" else: diff --git a/python/grass/temporal/list_stds.py b/python/grass/temporal/list_stds.py index bb0823995da..9f6b6867766 100644 --- a/python/grass/temporal/list_stds.py +++ b/python/grass/temporal/list_stds.py @@ -438,12 +438,11 @@ def check_columns(column_names, output_format, element_type): output_format=output_format, element_type=element_type, ) + elif output_format == "line": + # For list of values, only one column is needed. + columns = ["id"] else: - if output_format == "line": - # For list of values, only one column is needed. - columns = ["id"] - else: - columns = ["name", "mapset", "start_time", "end_time"] + columns = ["name", "mapset", "start_time", "end_time"] if not order: order = "start_time" diff --git a/python/grass/temporal/temporal_algebra.py b/python/grass/temporal/temporal_algebra.py index fefb4e00f60..c2546ec0d9e 100644 --- a/python/grass/temporal/temporal_algebra.py +++ b/python/grass/temporal/temporal_algebra.py @@ -2536,10 +2536,9 @@ def p_statement_assign(self, t): "Error map %s exist in temporal database. " "Use overwrite flag." % map_i.get_map_id() ) - else: + elif self.dry_run is False: # Insert map into temporal database. - if self.dry_run is False: - map_i.insert(dbif) + map_i.insert(dbif) # Register map in result space time dataset. if self.dry_run is False: @@ -3241,11 +3240,10 @@ def p_expr_condition_elif_relation(self, t): resultlist = self.check_stds(resultlist, clear=True) # Return resulting map list. t[0] = resultlist + elif t[5]: + t[0] = str(t[7]) else: - if t[5]: - t[0] = str(t[7]) - else: - t[0] = str(t[9]) + t[0] = str(t[9]) if self.debug: if t[5]: diff --git a/python/grass/temporal/temporal_granularity.py b/python/grass/temporal/temporal_granularity.py index 94aa3b124aa..d66f6c57ffb 100644 --- a/python/grass/temporal/temporal_granularity.py +++ b/python/grass/temporal/temporal_granularity.py @@ -1237,11 +1237,11 @@ def _return(output, tounit, shell): """Function to return the output""" if shell: return output + + if output == 1: + return f"{output} {tounit}" else: - if output == 1: - return f"{output} {tounit}" - else: - return f"{output} {tounit}s" + return f"{output} {tounit}s" # TODO check the leap second if check_granularity_string(from_gran, "absolute"): diff --git a/python/grass/temporal/temporal_raster_base_algebra.py b/python/grass/temporal/temporal_raster_base_algebra.py index 4ca921adaaf..02e28e0e435 100644 --- a/python/grass/temporal/temporal_raster_base_algebra.py +++ b/python/grass/temporal/temporal_raster_base_algebra.py @@ -928,10 +928,9 @@ def p_statement_assign(self, t): "Error raster map %s exist in temporal database. " "Use overwrite flag." % map_i.get_map_id() ) - else: + elif self.dry_run is False: # Insert map into temporal database. - if self.dry_run is False: - map_i.insert(dbif) + map_i.insert(dbif) # Register map in result space time dataset. if self.dry_run is False: success = resultstds.register_map(map_i, dbif) diff --git a/scripts/d.frame/d.frame.py b/scripts/d.frame/d.frame.py index a1befa3a1dd..e9b44b21650 100755 --- a/scripts/d.frame/d.frame.py +++ b/scripts/d.frame/d.frame.py @@ -311,22 +311,19 @@ def main(): fatal(_("Required parameter <%s> not set") % "at") # create new frame if not exists create_frame(monitor, options["frame"], options["at"]) - else: - if os.getenv("GRASS_OVERWRITE", "0") == "1": - warning( - _("Frame <%s> already exists and will be overwritten") - % options["frame"] + elif os.getenv("GRASS_OVERWRITE", "0") == "1": + warning( + _("Frame <%s> already exists and will be overwritten") % options["frame"] + ) + create_frame(monitor, options["frame"], options["at"], overwrite=True) + elif options["at"]: + warning( + _( + "Frame <%s> already found. An existing frame can be " + "overwritten by '%s' flag." ) - create_frame(monitor, options["frame"], options["at"], overwrite=True) - else: - if options["at"]: - warning( - _( - "Frame <%s> already found. An existing frame can be " - "overwritten by '%s' flag." - ) - % (options["frame"], "--overwrite") - ) + % (options["frame"], "--overwrite") + ) # select givenframe select_frame(monitor, options["frame"]) diff --git a/scripts/g.extension/g.extension.py b/scripts/g.extension/g.extension.py index c02b8b8e3a4..fb399db9d06 100644 --- a/scripts/g.extension/g.extension.py +++ b/scripts/g.extension/g.extension.py @@ -665,11 +665,10 @@ def list_installed_extensions(toolboxes=False): gs.message(_("List of installed extensions (modules):")) sys.stdout.write("\n".join(elist)) sys.stdout.write("\n") + elif toolboxes: + gs.info(_("No extension (toolbox) installed")) else: - if toolboxes: - gs.info(_("No extension (toolbox) installed")) - else: - gs.info(_("No extension (module) installed")) + gs.info(_("No extension (module) installed")) def get_installed_toolboxes(force=False): @@ -756,9 +755,8 @@ def list_available_extensions(url): print("%s (%s)" % (toolbox_data["name"], toolbox_code)) if flags["c"] or flags["g"]: list_available_modules(url, toolbox_data["modules"]) - else: - if toolbox_data["modules"]: - print(os.linesep.join(["* " + x for x in toolbox_data["modules"]])) + elif toolbox_data["modules"]: + print(os.linesep.join(["* " + x for x in toolbox_data["modules"]])) else: gs.message(_("List of available extensions (modules):")) # TODO: extensions with several modules + lib @@ -2192,23 +2190,22 @@ def remove_extension(force=False): for ename in edict: if ename in eremoved: gs.message(_("Extension <%s> successfully uninstalled.") % ename) - else: - if flags["t"]: - gs.warning( - _( - "Toolbox <%s> not removed. " - "Re-run '%s' with '-f' flag to force removal" - ) - % (options["extension"], "g.extension") + elif flags["t"]: + gs.warning( + _( + "Toolbox <%s> not removed. " + "Re-run '%s' with '-f' flag to force removal" ) - else: - gs.warning( - _( - "Extension <%s> not removed. " - "Re-run '%s' with '-f' flag to force removal" - ) - % (options["extension"], "g.extension") + % (options["extension"], "g.extension") + ) + else: + gs.warning( + _( + "Extension <%s> not removed. " + "Re-run '%s' with '-f' flag to force removal" ) + % (options["extension"], "g.extension") + ) # remove existing extension(s) (reading XML file) diff --git a/scripts/m.proj/m.proj.py b/scripts/m.proj/m.proj.py index 6c5c2c0eed7..4540b3dcd0a 100755 --- a/scripts/m.proj/m.proj.py +++ b/scripts/m.proj/m.proj.py @@ -216,16 +216,15 @@ def main(): fd.write("%s%s%s\n" % (x, ifs, y)) fd.close() inf = open(tmpfile) + elif input == "-": + infile = None + inf = sys.stdin else: - if input == "-": - infile = None - inf = sys.stdin - else: - infile = input - if not os.path.exists(infile): - gcore.fatal(_("Unable to read input data")) - inf = open(infile) - gcore.debug("input file=[%s]" % infile) + infile = input + if not os.path.exists(infile): + gcore.fatal(_("Unable to read input data")) + inf = open(infile) + gcore.debug("input file=[%s]" % infile) # set up output file if not output: diff --git a/scripts/r.semantic.label/r.semantic.label.py b/scripts/r.semantic.label/r.semantic.label.py index e26ee779900..53239772117 100644 --- a/scripts/r.semantic.label/r.semantic.label.py +++ b/scripts/r.semantic.label/r.semantic.label.py @@ -128,9 +128,8 @@ def main(): semantic_label = semantic_labels[i] if multi_labels else semantic_labels[0] if options["operation"] == "print": print_map_semantic_label(maps[i], label_reader) - else: - if manage_map_semantic_label(maps[i], semantic_label) != 0: - ret = 1 + elif manage_map_semantic_label(maps[i], semantic_label) != 0: + ret = 1 return ret diff --git a/scripts/v.db.reconnect.all/v.db.reconnect.all.py b/scripts/v.db.reconnect.all/v.db.reconnect.all.py index eb10d2a1675..03a98f7bf0e 100755 --- a/scripts/v.db.reconnect.all/v.db.reconnect.all.py +++ b/scripts/v.db.reconnect.all/v.db.reconnect.all.py @@ -316,16 +316,15 @@ def main(): % (table, vect, str(layer)) ) - else: - if database != new_database_subst: - gs.warning( - _( - "Layer <%d> will not be reconnected " - "because database or schema do not " - "match." - ) - % layer + elif database != new_database_subst: + gs.warning( + _( + "Layer <%d> will not be reconnected " + "because database or schema do not " + "match." ) + % layer + ) return 0 diff --git a/scripts/v.in.lines/v.in.lines.py b/scripts/v.in.lines/v.in.lines.py index 72c10511b94..44112b297a3 100755 --- a/scripts/v.in.lines/v.in.lines.py +++ b/scripts/v.in.lines/v.in.lines.py @@ -78,7 +78,7 @@ def main(): outf.close() runfile = tmp - else: + else: # noqa: PLR5501 # read from a real file if fs == " ": runfile = infile diff --git a/scripts/v.report/v.report.py b/scripts/v.report/v.report.py index b31562e05db..8da23bbaf91 100755 --- a/scripts/v.report/v.report.py +++ b/scripts/v.report/v.report.py @@ -235,19 +235,13 @@ def main(): # sort results if sort: - if sort == "asc": - if option == "coor": - records3.sort(key=lambda r: (float(r[-3]), float(r[-2]), float(r[-1]))) - else: - records3.sort(key=lambda r: float(r[-1])) + if option == "coor": + records3.sort( + key=lambda r: (float(r[-3]), float(r[-2]), float(r[-1])), + reverse=(sort != "asc"), + ) else: - if option == "coor": - records3.sort( - key=lambda r: (float(r[-3]), float(r[-2]), float(r[-1])), - reverse=True, - ) - else: - records3.sort(key=lambda r: float(r[-1]), reverse=True) + records3.sort(key=lambda r: float(r[-1]), reverse=(sort != "asc")) for r in records3: sys.stdout.write(fs.join(map(str, r)) + "\n") diff --git a/temporal/t.rast.accdetect/t.rast.accdetect.py b/temporal/t.rast.accdetect/t.rast.accdetect.py index 629dfb44a47..49953246037 100644 --- a/temporal/t.rast.accdetect/t.rast.accdetect.py +++ b/temporal/t.rast.accdetect/t.rast.accdetect.py @@ -458,7 +458,7 @@ def main(): curr_map, indicator_mid, ) - else: + else: # noqa: PLR5501 if i == 0: prev_map = curr_map subexpr1 = "null()" diff --git a/temporal/t.rast.aggregate/t.rast.aggregate.py b/temporal/t.rast.aggregate/t.rast.aggregate.py index dfc5e59aa05..609d6d19d38 100755 --- a/temporal/t.rast.aggregate/t.rast.aggregate.py +++ b/temporal/t.rast.aggregate/t.rast.aggregate.py @@ -175,7 +175,7 @@ def main(): if has_end_time is True: if start_time >= end_time: break - else: + else: # noqa: PLR5501 if start_time > end_time: break diff --git a/temporal/t.rast.gapfill/t.rast.gapfill.py b/temporal/t.rast.gapfill/t.rast.gapfill.py index ffcfe30da50..e2ffb149513 100755 --- a/temporal/t.rast.gapfill/t.rast.gapfill.py +++ b/temporal/t.rast.gapfill/t.rast.gapfill.py @@ -211,9 +211,8 @@ def main(): "Please use another base name." % (_id) ) ) - else: - if new_map.is_in_db(dbif): - overwrite_flags[new_id] = True + elif new_map.is_in_db(dbif): + overwrite_flags[new_id] = True map_names.append(new_map.get_name()) map_positions.append(position) diff --git a/temporal/t.vect.db.select/t.vect.db.select.py b/temporal/t.vect.db.select/t.vect.db.select.py index d9cf9c12858..a8f2490749e 100755 --- a/temporal/t.vect.db.select/t.vect.db.select.py +++ b/temporal/t.vect.db.select/t.vect.db.select.py @@ -120,23 +120,22 @@ def main(): if col_names != col_names_new: col_names = col_names_new print(col_names) - else: - if row["end_time"]: - print( - "%s%s%s%s%s" - % ( - row["start_time"], - separator, - row["end_time"], - separator, - entry, - ) - ) - else: - print( - "%s%s%s%s" - % (row["start_time"], separator, separator, entry) + elif row["end_time"]: + print( + "%s%s%s%s%s" + % ( + row["start_time"], + separator, + row["end_time"], + separator, + entry, ) + ) + else: + print( + "%s%s%s%s" + % (row["start_time"], separator, separator, entry) + ) count += 1 diff --git a/utils/gitlog2changelog.py b/utils/gitlog2changelog.py index 5438a0277cb..e9627567e05 100755 --- a/utils/gitlog2changelog.py +++ b/utils/gitlog2changelog.py @@ -97,11 +97,10 @@ messageNL = True elif len(line) == 4: messageFound = True + elif len(message) == 0: + message = message + line.strip() else: - if len(message) == 0: - message = message + line.strip() - else: - message = message + " " + line.strip() + message = message + " " + line.strip() # If this line is hit all of the files have been stored for this commit elif re.search("files? changed", line): filesFound = True diff --git a/utils/mkhtml.py b/utils/mkhtml.py index 1e9a335bcc2..210e83b3fd6 100644 --- a/utils/mkhtml.py +++ b/utils/mkhtml.py @@ -407,16 +407,15 @@ def get_last_git_commit(src_dir, addon_path, is_addon): commit=process_result.stdout.decode(), src_dir=src_dir, ) + elif gs: + # Addons installation + return get_git_commit_from_rest_api_for_addon_repo( + addon_path=addon_path, + src_dir=src_dir, + ) + # During GRASS GIS compilation from source code without Git else: - if gs: - # Addons installation - return get_git_commit_from_rest_api_for_addon_repo( - addon_path=addon_path, - src_dir=src_dir, - ) - # During GRASS GIS compilation from source code without Git - else: - return get_git_commit_from_file(src_dir=src_dir) + return get_git_commit_from_file(src_dir=src_dir) html_page_footer_pages_path = os.getenv("HTML_PAGE_FOOTER_PAGES_PATH") or "" @@ -772,11 +771,10 @@ def get_addon_path(): if desc: pgm = desc.group(2).strip() header_tmpl = string.Template(header_base + header_nopgm) +elif not pgm_desc: + header_tmpl = string.Template(header_base + header_pgm) else: - if not pgm_desc: - header_tmpl = string.Template(header_base + header_pgm) - else: - header_tmpl = string.Template(header_base + header_pgm_desc) + header_tmpl = string.Template(header_base + header_pgm_desc) if not re.search("", src_data, re.IGNORECASE): tmp_data = read_file(tmp_file) From 972bd32a23156f3255025a1ed6d42c9e563960b8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 17 Jul 2024 16:45:46 -0400 Subject: [PATCH 036/471] CI(deps): Update softprops/action-gh-release action to v2.0.7 (#4066) --- .github/workflows/create_release_draft.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/create_release_draft.yml b/.github/workflows/create_release_draft.yml index dcaa6d337b1..a802b0b7059 100644 --- a/.github/workflows/create_release_draft.yml +++ b/.github/workflows/create_release_draft.yml @@ -73,7 +73,7 @@ jobs: sha256sum ${{ env.GRASS }}.tar.xz > ${{ env.GRASS }}.tar.xz.sha256 - name: Publish draft distribution to GitHub (for tags only) if: startsWith(github.ref, 'refs/tags/') - uses: softprops/action-gh-release@a74c6b72af54cfa997e81df42d94703d6313a2d0 # v2.0.6 + uses: softprops/action-gh-release@fb2d03176f42a1f0dd433ca263f314051d3edd44 # v2.0.7 with: name: GRASS GIS ${{ github.ref_name }} body: | From 0c3fdede72664037fcfc8d3f21daa90d36b1417c Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Wed, 17 Jul 2024 21:31:33 -0400 Subject: [PATCH 037/471] raster: Fix uninitialized variable issue for C raster modules (#4065) raster: Fix uninitialized variable issue for c files in raster --- raster/r.coin/print_coin.c | 2 +- raster/r.mapcalc/map3.c | 2 +- raster/r.object.geometry/main.c | 2 +- raster/r.out.mpeg/main.c | 2 +- raster/r.sim/simlib/random.c | 4 ++-- raster/r.spreadpath/path_finder.c | 2 +- raster/r.statistics/o_kurt.c | 2 +- raster/r.statistics/o_sdev.c | 2 +- raster/r.statistics/o_skew.c | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/raster/r.coin/print_coin.c b/raster/r.coin/print_coin.c index f2a291fa326..a74ab75044b 100644 --- a/raster/r.coin/print_coin.c +++ b/raster/r.coin/print_coin.c @@ -32,7 +32,7 @@ int print_coin(int Conformat, int out_cols, int tofile) double colarea_no_0, rowarea_no_0; double area; - int addflag; + int addflag = 0; char topformat[133], midformat[133], namformat[133]; char fillformat[133]; const char *mapone; diff --git a/raster/r.mapcalc/map3.c b/raster/r.mapcalc/map3.c index 374f6a76109..c9683148275 100644 --- a/raster/r.mapcalc/map3.c +++ b/raster/r.mapcalc/map3.c @@ -265,7 +265,7 @@ static void translate_from_cats(map *m, CELL *cell, DCELL *xcell, int ncols) { struct Categories *pcats; BTREE *btree; - int i, idx; + int i = 0, idx = 0; CELL cat, key; double vbuf[1 << SHIFT]; double *values; diff --git a/raster/r.object.geometry/main.c b/raster/r.object.geometry/main.c index 062a7aa1a83..f808c4b88e1 100644 --- a/raster/r.object.geometry/main.c +++ b/raster/r.object.geometry/main.c @@ -56,7 +56,7 @@ int main(int argc, char *argv[]) } *obj_geos; double unit_area; int n_objects; - int planimetric, compute_areas; + int planimetric = 0, compute_areas = 0; struct Cell_head cellhd; G_gisinit(argv[0]); diff --git a/raster/r.out.mpeg/main.c b/raster/r.out.mpeg/main.c index a83004544b1..0ac3b631b80 100644 --- a/raster/r.out.mpeg/main.c +++ b/raster/r.out.mpeg/main.c @@ -235,7 +235,7 @@ static int load_files(void) register int i, rowoff, row, col, vxoff, vyoff, offset; int cnt, fd, size, tsiz, coff; int vnum; - int y_rows, y_cols; + int y_rows = 0, y_cols = 0; char *pr, *pg, *pb; unsigned char *tr, *tg, *tb, *tset; char *mpfilename, *name; diff --git a/raster/r.sim/simlib/random.c b/raster/r.sim/simlib/random.c index 4fff384f795..8646a93bf9f 100644 --- a/raster/r.sim/simlib/random.c +++ b/raster/r.sim/simlib/random.c @@ -25,7 +25,7 @@ double gasdev(void) double ret_val; /* Local variables */ - double r = 0., vv1, vv2, fac; + double r = 0.0, vv1 = 0.0, vv2 = 0.0, fac = 0.0; if (iset == 0) { while (r >= 1. || r == 0.) { @@ -47,7 +47,7 @@ double gasdev(void) void gasdev_for_paralel(double *x, double *y) { - double r = 0., vv1, vv2, fac; + double r = 0.0, vv1 = 0.0, vv2 = 0.0, fac = 0.0; while (r >= 1. || r == 0.) { vv1 = simwe_rand() * 2. - 1.; diff --git a/raster/r.spreadpath/path_finder.c b/raster/r.spreadpath/path_finder.c index 8b5cfe87a39..9cf98048d47 100644 --- a/raster/r.spreadpath/path_finder.c +++ b/raster/r.spreadpath/path_finder.c @@ -10,7 +10,7 @@ void path_finder(int row, int col, int backrow, int backcol) { - int data, new_backrow, new_backcol; + int data = 0, new_backrow = 0, new_backcol = 0; extern char *value; extern int nrows, ncols; extern SEGMENT in_row_seg, in_col_seg, out_seg; diff --git a/raster/r.statistics/o_kurt.c b/raster/r.statistics/o_kurt.c index b6383811dd4..0155ffa0893 100644 --- a/raster/r.statistics/o_kurt.c +++ b/raster/r.statistics/o_kurt.c @@ -17,7 +17,7 @@ int o_kurt(const char *basemap, const char *covermap, const char *outputmap, { struct Popen stats_child, reclass_child; FILE *stats, *reclass; - int first, i, count; + int first, i, count = 0; size_t mem; long basecat, covercat, catb, catc; double value, var, x; diff --git a/raster/r.statistics/o_sdev.c b/raster/r.statistics/o_sdev.c index 4a39cd76559..b49c134508f 100644 --- a/raster/r.statistics/o_sdev.c +++ b/raster/r.statistics/o_sdev.c @@ -17,7 +17,7 @@ int o_sdev(const char *basemap, const char *covermap, const char *outputmap, { struct Popen stats_child, reclass_child; FILE *stats, *reclass; - int first, i, count; + int first, i, count = 0; size_t mem; long basecat, covercat, catb, catc; double value, sdev, x; diff --git a/raster/r.statistics/o_skew.c b/raster/r.statistics/o_skew.c index e08c97e5951..09cca655d46 100644 --- a/raster/r.statistics/o_skew.c +++ b/raster/r.statistics/o_skew.c @@ -17,7 +17,7 @@ int o_skew(const char *basemap, const char *covermap, const char *outputmap, { struct Popen stats_child, reclass_child; FILE *stats, *reclass; - int first, i, count; + int first, i, count = 0; size_t mem; long basecat, covercat, catb, catc; double value, var, x; From 6fb030f8d8aa15642344a6acfded9fe903454e33 Mon Sep 17 00:00:00 2001 From: Paulo van Breugel Date: Thu, 18 Jul 2024 03:41:39 +0200 Subject: [PATCH 038/471] r.li manual: Update reference to fragstat paper (#4001) --- raster/r.li/r.li.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/raster/r.li/r.li.html b/raster/r.li/r.li.html index 94c6a47abb4..0ad8e5b6d77 100644 --- a/raster/r.li/r.li.html +++ b/raster/r.li/r.li.html @@ -211,7 +211,7 @@

          REFERENCES

          McGarigal, K., and B. J. Marks. 1995. FRAGSTATS: spatial pattern analysis program for quantifying landscape structure. USDA For. Serv. Gen. Tech. Rep. PNW-351 - (PDF). + (PDF).
        • Baker, W.L. and Y. Cai. 1992. The r.le programs for multiscale analysis of From daa3b61895acdd552bddfc773e8dd2160c16d12f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 18 Jul 2024 22:46:13 +0000 Subject: [PATCH 039/471] CI(deps): Update ruff to v0.5.3 (#4073) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * CI(deps): Update ruff to v0.5.3 * Apply ruff fixes * Apply black --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Edouard Choinière <27212526+echoix@users.noreply.github.com> --- .github/workflows/python-code-quality.yml | 2 +- .pre-commit-config.yaml | 2 +- gui/wxpython/mapwin/buffered.py | 2 +- gui/wxpython/wxplot/histogram.py | 2 +- gui/wxpython/wxplot/profile.py | 6 ++---- 5 files changed, 6 insertions(+), 8 deletions(-) diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index 81f74056f9d..b1c7dd8391a 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -36,7 +36,7 @@ jobs: # renovate: datasource=pypi depName=bandit BANDIT_VERSION: "1.7.9" # renovate: datasource=pypi depName=ruff - RUFF_VERSION: "0.5.2" + RUFF_VERSION: "0.5.3" runs-on: ${{ matrix.os }} permissions: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9843a6f6616..143ab87d44e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -37,7 +37,7 @@ repos: ) - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.5.2 + rev: v0.5.3 hooks: # Run the linter. - id: ruff diff --git a/gui/wxpython/mapwin/buffered.py b/gui/wxpython/mapwin/buffered.py index 6ce7f98731e..e0442ed94ae 100644 --- a/gui/wxpython/mapwin/buffered.py +++ b/gui/wxpython/mapwin/buffered.py @@ -1127,7 +1127,7 @@ def DragMap(self, moveto): def DragItem(self, id, coords): """Drag an overlay decoration item""" - if id == 99 or id == "" or id is None: + if id in (99, "") or id is None: return Debug.msg(5, "BufferedWindow.DragItem(): id=%d" % id) x, y = self.lastpos diff --git a/gui/wxpython/wxplot/histogram.py b/gui/wxpython/wxplot/histogram.py index 1c7d4bf21d9..f8319747dab 100644 --- a/gui/wxpython/wxplot/histogram.py +++ b/gui/wxpython/wxplot/histogram.py @@ -154,7 +154,7 @@ def SetupHistogram(self): # set xlabel based on first raster map in list to be histogrammed # units = self.raster[self.rasterList[0]]["units"] - if units != "" and units != "(none)" and units is not None: + if units not in ("", "(none)") and units is not None: self.xlabel = _("Raster cell values %s") % units else: self.xlabel = _("Raster cell values") diff --git a/gui/wxpython/wxplot/profile.py b/gui/wxpython/wxplot/profile.py index 1e79379b08b..1d3de6681af 100644 --- a/gui/wxpython/wxplot/profile.py +++ b/gui/wxpython/wxplot/profile.py @@ -313,11 +313,9 @@ def CreateDatalist(self, raster, coords): dist, elev = line.strip().split(" ") if ( dist is None - or dist == "" - or dist == "nan" + or dist in ("", "nan") or elev is None - or elev == "" - or elev == "nan" + or elev in ("", "nan") ): continue dist = float(dist) From a35956fbc44248bb8be873940ec6e6fbb5c41929 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 18 Jul 2024 22:52:57 -0400 Subject: [PATCH 040/471] CI(deps): Update softprops/action-gh-release action to v2.0.8 (#4074) --- .github/workflows/create_release_draft.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/create_release_draft.yml b/.github/workflows/create_release_draft.yml index a802b0b7059..d43e1248555 100644 --- a/.github/workflows/create_release_draft.yml +++ b/.github/workflows/create_release_draft.yml @@ -73,7 +73,7 @@ jobs: sha256sum ${{ env.GRASS }}.tar.xz > ${{ env.GRASS }}.tar.xz.sha256 - name: Publish draft distribution to GitHub (for tags only) if: startsWith(github.ref, 'refs/tags/') - uses: softprops/action-gh-release@fb2d03176f42a1f0dd433ca263f314051d3edd44 # v2.0.7 + uses: softprops/action-gh-release@c062e08bd532815e2082a85e87e3ef29c3e6d191 # v2.0.8 with: name: GRASS GIS ${{ github.ref_name }} body: | From 7840f76ae21f339ca227f2202a3a261da6f39cc9 Mon Sep 17 00:00:00 2001 From: Stefan Blumentrath Date: Fri, 19 Jul 2024 08:33:49 +0200 Subject: [PATCH 041/471] t.rast.neighbors: support all r.neighbors features and allow to append to existing STRDS as well as filtering by region (#3798) * propagate r.neighbors options * reactivate and extend tests * allow to extend existing STRDS * allow spatial selection by computational region --------- Co-authored-by: Veronica Andreo --- .../temporal/abstract_space_time_dataset.py | 11 +- .../t.rast.neighbors/t.rast.neighbors.html | 25 ++- temporal/t.rast.neighbors/t.rast.neighbors.py | 178 ++++++++++++++---- .../testsuite/test_neighbors.py | 82 +++++++- 4 files changed, 243 insertions(+), 53 deletions(-) diff --git a/python/grass/temporal/abstract_space_time_dataset.py b/python/grass/temporal/abstract_space_time_dataset.py index ab76ab4112c..890061ee64d 100644 --- a/python/grass/temporal/abstract_space_time_dataset.py +++ b/python/grass/temporal/abstract_space_time_dataset.py @@ -1551,7 +1551,12 @@ def get_registered_maps_as_objects( # use all columns rows = self.get_registered_maps( - None, where, order, dbif, spatial_extent, spatial_relation + columns=None, + where=where, + order=order, + dbif=dbif, + spatial_extent=spatial_extent, + spatial_relation=spatial_relation, ) if rows: @@ -2403,7 +2408,9 @@ def delete(self, dbif=None, execute=True): self.msgr.debug( 1, _("Drop map register table: %s") % (self.get_map_register()) ) - rows = self.get_registered_maps("id", None, None, dbif) + rows = self.get_registered_maps( + columns="id", where=None, order=None, dbif=dbif + ) # Unregister each registered map in the table if rows is not None: for row in rows: diff --git a/temporal/t.rast.neighbors/t.rast.neighbors.html b/temporal/t.rast.neighbors/t.rast.neighbors.html index 78f6da54f9a..054b5b57754 100644 --- a/temporal/t.rast.neighbors/t.rast.neighbors.html +++ b/temporal/t.rast.neighbors/t.rast.neighbors.html @@ -2,19 +2,28 @@

          DESCRIPTION

          t.rast.neighbors performs r.neighbors computations on the maps of a space time raster dataset (STRDS). This -module supports a subset of options that are available in -r.neighbors. The size of the neighborhood -and the aggregation method can be chosen. +module supports the options that are available in +r.neighbors. +

          The user must provide an input and an output space time raster dataset and the basename of the resulting raster maps. The resulting STRDS will have -the same temporal resolution as the input dataset. -All maps will be processed using the current region settings. +the same temporal resolution as the input dataset. With the -e flag, +resulting maps can be registered in an existing STRDS, that e.g. may have +been created with a previous run of t.rast.neighbors. +All maps will be processed using the current region settings unless the +-r flag is selected. In the latter case, the computaional region +is set to each raster map selected from the input STRDS. +

          The user can select a subset of the input space time raster dataset for -processing using a SQL WHERE statement. The number of CPU's to be used -for parallel processing can be specified with the nprocs -option to speedup the computation on multi-core system. +processing using a SQL WHERE statement or using the region_relation +for spatial selection of raster maps. For the spatial map selection the +current computational region is used, even when the -r flag is +given. The number of CPU's to be used for parallel processing can be +specified with the nprocs option to speedup the computation on +multi-core system. +

          Semantic labels are needed to relate output raster maps to input raster maps. E.g. with method=stddev, the user needs to know the diff --git a/temporal/t.rast.neighbors/t.rast.neighbors.py b/temporal/t.rast.neighbors/t.rast.neighbors.py index 8e94e88ed6e..e7014e9ca2e 100755 --- a/temporal/t.rast.neighbors/t.rast.neighbors.py +++ b/temporal/t.rast.neighbors/t.rast.neighbors.py @@ -38,6 +38,21 @@ # %option G_OPT_T_WHERE # %end +# %option +# % key: region_relation +# % description: Process only maps with this spatial relation to the current computational region +# % guisection: Selection +# % options: overlaps, contains, is_contained +# % required: no +# % multiple: no +# %end + +# %option G_OPT_R_INPUT +# % key: selection +# % required: no +# % description: Name of an input raster map to select the cells which should be processed +# %end + # %option # % key: size # % type: integer @@ -57,6 +72,43 @@ # % answer: average # %end +# %option +# % key: weighting_function +# % type: string +# % required: no +# % multiple: no +# % options: none,gaussian,exponential,file +# % description: Weighting function +# % descriptions: none;No weighting; gaussian;Gaussian weighting function; exponential;Exponential weighting function; file;File with a custom weighting matrix +# % answer: none +# %end + +# %option +# % key: weighting_factor +# % type: double +# % required: no +# % multiple: no +# % description: Factor used in the selected weighting function (ignored for weighting_function=none and file) +# %end + +# %option G_OPT_F_INPUT +# % key: weight +# % type: string +# % required: no +# % multiple: no +# % description: Text file containing weights +# %end + +# %option +# % key: quantile +# % type: double +# % required: no +# % multiple: yes +# % options: 0.0-1.0 +# % description: Quantile to calculate for method=quantile +# % guisection: Neighborhood +# %end + # %option # % key: basename # % type: string @@ -96,6 +148,16 @@ # % answer: 1 # %end +# %flag +# % key: c +# % description: Use circular neighborhood +# %end + +# %flag +# % key: e +# % description: Extend existing space time raster dataset +# %end + # %flag # % key: n # % description: Register Null maps @@ -110,8 +172,6 @@ import grass.script as gs -############################################################################ - def main(): # lazy imports @@ -122,6 +182,7 @@ def main(): input = options["input"] output = options["output"] where = options["where"] + region_relation = options["region_relation"] size = options["size"] base = options["basename"] register_null = flags["n"] @@ -130,6 +191,14 @@ def main(): nprocs = options["nprocs"] time_suffix = options["suffix"] new_labels = options["semantic_labels"] + quantiles = ( + [float(quant) for quant in options["quantile"].split(",")] + if options["quantile"] + else None + ) + + if method == "quantile" and not options["quantile"]: + gs.fatal(_("The method requires input in the 'quantile' option.")) # Make sure the temporal database exists tgis.init() @@ -140,23 +209,42 @@ def main(): overwrite = gs.overwrite() sp = tgis.open_old_stds(input, "strds", dbif) - maps = sp.get_registered_maps_as_objects(where=where, dbif=dbif) + + spatial_extent = None + if region_relation: + spatial_extent = gs.parse_command("g.region", flags="3gu") + + maps = sp.get_registered_maps_as_objects( + where=where, + spatial_extent=spatial_extent, + spatial_relation=region_relation, + dbif=dbif, + ) if not maps: dbif.close() - gs.warning(_("Space time raster dataset <%s> is empty") % sp.get_id()) + gs.warning(_("Space time raster dataset <{}> is empty").format(sp.get_id())) return - new_sp = tgis.check_new_stds(output, "strds", dbif=dbif, overwrite=overwrite) + output_strds = tgis.check_new_stds(output, "strds", dbif=dbif, overwrite=overwrite) + output_exists = output_strds.is_in_db(dbif) # Configure the r.neighbor module neighbor_module = pymod.Module( "r.neighbors", + flags="c" if flags["c"] else "", input="dummy", output="dummy", run_=False, finish_=False, + selection=options["selection"], size=int(size), method=method, + quantile=quantiles, + weighting_function=options["weighting_function"], + weighting_factor=( + float(options["weighting_factor"]) if options["weighting_factor"] else None + ), + weight=options["weight"], overwrite=overwrite, quiet=True, ) @@ -182,10 +270,10 @@ def main(): suffix = tgis.create_suffix_from_datetime( map.temporal_extent.get_start_time(), sp.get_granularity() ) - map_name = "{ba}_{su}".format(ba=base, su=suffix) + map_name = f"{base}_{suffix}" elif sp.get_temporal_type() == "absolute" and time_suffix == "time": suffix = tgis.create_time_suffix(map) - map_name = "{ba}_{su}".format(ba=base, su=suffix) + map_name = f"{base}_{suffix}" else: map_name = tgis.create_numeric_suffix(base, count, time_suffix) @@ -215,13 +303,12 @@ def main(): if use_raster_region is True: reg = copy.deepcopy(gregion_module) reg(raster=map.get_id()) - print(reg.get_bash()) - print(mod.get_bash()) + gs.verbose(reg.get_bash()) mm = pymod.MultiModule([reg, mod], sync=False, set_temp_region=True) process_queue.put(mm) else: - print(mod.get_bash()) process_queue.put(mod) + gs.verbose(mod.get_bash()) # Wait for unfinished processes process_queue.wait() @@ -232,58 +319,75 @@ def main(): for proc in proc_list: if proc.returncode != 0: gs.error( - _("Error running module: %\n stderr: %s") - % (proc.get_bash(), proc.outputs.stderr) + _("Error running module: {mod}\n stderr: {error}").format( + mod=proc.get_bash(), error=proc.outputs.stderr + ) ) error += 1 if error > 0: gs.fatal(_("Error running modules.")) - # Open the new space time raster dataset - ttype, stype, title, descr = sp.get_initial_values() - new_sp = tgis.open_new_stds( - output, "strds", ttype, title, descr, stype, dbif, overwrite - ) + # Open a new space time raster dataset + if not output_exists or (overwrite and not flags["e"]): + # Get basic metadata + temporal_type, semantic_type, title, description = sp.get_initial_values() + + # Create new STRDS + output_strds = tgis.open_new_stds( + output, + "strds", + temporal_type, + title, + description, + semantic_type, + dbif, + overwrite, + ) + + # Append to existing + elif output_exists and flags["e"]: + output_strds = tgis.open_old_stds(output, "strds", dbif) + num_maps = len(new_maps) # collect empty maps to remove them empty_maps = [] # Register the maps in the database - count = 0 - for map in new_maps: - count += 1 - + for count, raster_map in enumerate(new_maps, 1): if count % 10 == 0: gs.percent(count, num_maps, 1) # Do not register empty maps - map.load() - if map.metadata.get_min() is None and map.metadata.get_max() is None: + raster_map.load() + if ( + raster_map.metadata.get_min() is None + and raster_map.metadata.get_max() is None + ): if not register_null: - empty_maps.append(map) + empty_maps.append(raster_map) continue # Insert map in temporal database - map.insert(dbif) - new_sp.register_map(map, dbif) + raster_map.insert(dbif) + output_strds.register_map(raster_map, dbif) # Update the spatio-temporal extent and the metadata table entries - new_sp.update_from_registered_maps(dbif) + output_strds.update_from_registered_maps(dbif) gs.percent(1, 1, 1) + if output_exists: + output_strds.update_command_string(dbif=dbif) + # Remove empty maps if len(empty_maps) > 0: - names = "" - count = 0 - for map in empty_maps: - if count == 0: - count += 1 - names += "%s" % (map.get_name()) - else: - names += ",%s" % (map.get_name()) - - gs.run_command("g.remove", flags="f", type="raster", name=names, quiet=True) + gs.run_command( + "g.remove", + flags="f", + type="raster", + name=",".join([raster_map.get_name() for raster_map in empty_maps]), + quiet=True, + ) dbif.close() diff --git a/temporal/t.rast.neighbors/testsuite/test_neighbors.py b/temporal/t.rast.neighbors/testsuite/test_neighbors.py index 6216c3f9923..414b009fb1d 100644 --- a/temporal/t.rast.neighbors/testsuite/test_neighbors.py +++ b/temporal/t.rast.neighbors/testsuite/test_neighbors.py @@ -5,15 +5,11 @@ """ import os -import sys -import unittest - import grass.temporal as tgis from grass.gunittest.case import TestCase from grass.gunittest.gmodules import SimpleModule -@unittest.skipIf(sys.version_info[0] > 2, "temporary disabled") class TestAggregationAbsolute(TestCase): @classmethod def setUpClass(cls): @@ -21,6 +17,10 @@ def setUpClass(cls): os.putenv("GRASS_OVERWRITE", "1") tgis.init() cls.use_temp_region() + cls.runModule("g.region", s=0, n=90, w=140, e=160, b=0, t=50, res=10, res3=10) + cls.runModule( + "r.mapcalc", expression="a4 = rand(1,10)", flags=["s"], overwrite=True + ) cls.runModule("g.region", s=0, n=80, w=0, e=120, b=0, t=50, res=10, res3=10) cls.runModule( "r.mapcalc", expression="a1 = rand(1,10)", flags=["s"], overwrite=True @@ -47,7 +47,7 @@ def setUpClass(cls): flags="i", type="raster", input="A", - maps="a1,a2,a3", + maps="a1,a2,a3,a4", start="2001-01-01 00:00:00", increment="1 month", overwrite=True, @@ -85,6 +85,7 @@ def test_simple(self): trast_list = SimpleModule( "t.rast.neighbors", quiet=True, + flags="n", input="A", output="B", size="5", @@ -94,6 +95,42 @@ def test_simple(self): self.assertModule(trast_list) self.assertRasterMinMax("b_2001_01", 1, 10) self.assertRasterMinMax("b_2001_02", 1, 10) + self.assertRasterMinMax("b_2001_03", 1, 10) + minmax = "min=NULL\nmax=NULL" + self.assertRasterFitsInfo(raster="b_2001_04", reference=minmax) + + def test_weights(self): + """Test weights in t.rast.neighbors""" + trast_list = SimpleModule( + "t.rast.neighbors", + quiet=True, + input="A", + output="B", + basename="b", + weighting_function="gauss", + weighting_factor=1.5, + overwrite=True, + size="3", + where="start_time <= '2001-01-01 00:00:00'", + ) + self.assertModule(trast_list) + self.assertRasterExists("b_2001_01") + + def test_circular(self): + """Test circular neighborhood in t.rast.neighbors""" + trast_list = SimpleModule( + "t.rast.neighbors", + quiet=True, + flags="c", + input="A", + output="B", + basename="b", + overwrite=True, + size="3", + where="start_time <= '2001-01-01 00:00:00'", + ) + self.assertModule(trast_list) + self.assertRasterExists("b_2001_01") def test_time_suffix(self): """Test simple t.rast.neighbors""" @@ -105,6 +142,7 @@ def test_time_suffix(self): size="5", basename="b", suffix="time", + region_relation="overlaps", overwrite=True, ) self.assertModule(trast_list) @@ -117,9 +155,10 @@ def test_num_suffix(self): quiet=True, input="A", output="B", - size="5", + size="3", basename="b", suffix="num%03", + region_relation="overlaps", overwrite=True, ) self.assertModule(trast_list) @@ -134,6 +173,7 @@ def test_num_region(self): output="B", size="5", basename="b", + region_relation="overlaps", nprocs=2, suffix="num%03", flags="r", @@ -143,6 +183,36 @@ def test_num_region(self): self.assertRasterExists("b_001") self.assertRasterExists("b_002") self.assertRasterExists("b_003") + self.assertRasterDoesNotExist("b_004") + + def test_extend(self): + """Test extension of existing STRDS with t.rast.neighbors""" + trast_list1 = SimpleModule( + "t.rast.neighbors", + quiet=True, + input="A", + output="B", + basename="b", + overwrite=True, + size="5", + where="start_time <= '2001-02-01 00:00:00'", + ) + trast_list2 = SimpleModule( + "t.rast.neighbors", + quiet=True, + flags="e", + input="A", + output="B", + basename="b", + overwrite=True, + size="5", + where="start_time > '2001-02-01 00:00:00'", + ) + self.assertModule(trast_list1) + self.assertModule(trast_list2) + self.assertRasterExists("b_2001_01") + self.assertRasterMinMax("b_2001_02", 1, 10) + self.assertRasterExists("b_2001_03") if __name__ == "__main__": From 456546a4806e9e2b26af91d97b82ca3299d23498 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 19 Jul 2024 18:51:10 +0000 Subject: [PATCH 042/471] CI(deps): Update github/codeql-action action to v3.25.13 (#4075) --- .github/workflows/codeql-analysis.yml | 4 ++-- .github/workflows/python-code-quality.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 5317047188a..25bfafdf865 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -56,7 +56,7 @@ jobs: if: ${{ matrix.language == 'c-cpp' }} - name: Initialize CodeQL - uses: github/codeql-action/init@4fa2a7953630fd2f3fb380f21be14ede0169dd4f # v3.25.12 + uses: github/codeql-action/init@2d790406f505036ef40ecba973cc774a50395aac # v3.25.13 with: languages: ${{ matrix.language }} config-file: ./.github/codeql/codeql-config.yml @@ -81,6 +81,6 @@ jobs: run: .github/workflows/build_ubuntu-22.04.sh "${HOME}/install" - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@4fa2a7953630fd2f3fb380f21be14ede0169dd4f # v3.25.12 + uses: github/codeql-action/analyze@2d790406f505036ef40ecba973cc774a50395aac # v3.25.13 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index b1c7dd8391a..bcfe97ec418 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -132,7 +132,7 @@ jobs: path: bandit.sarif - name: Upload SARIF File into Security Tab - uses: github/codeql-action/upload-sarif@4fa2a7953630fd2f3fb380f21be14ede0169dd4f # v3.25.12 + uses: github/codeql-action/upload-sarif@2d790406f505036ef40ecba973cc774a50395aac # v3.25.13 with: sarif_file: bandit.sarif From c1f55e303d175a8ad02d39cf24076d7a96026ce2 Mon Sep 17 00:00:00 2001 From: Kriti Birda <164247895+kritibirda26@users.noreply.github.com> Date: Sat, 20 Jul 2024 03:23:19 +0530 Subject: [PATCH 043/471] r.category: add JSON support (#4018) * r.category: add JSON support * add test and docs * address PR feedback --- raster/r.category/Makefile | 2 +- raster/r.category/local_proto.h | 9 +- raster/r.category/main.c | 92 +++++++++++++++++--- raster/r.category/r.category.html | 20 +++++ raster/r.category/test_rcategory_doctest.txt | 16 ++++ 5 files changed, 126 insertions(+), 13 deletions(-) diff --git a/raster/r.category/Makefile b/raster/r.category/Makefile index 74909c5f608..5fd448ab5c5 100644 --- a/raster/r.category/Makefile +++ b/raster/r.category/Makefile @@ -2,7 +2,7 @@ MODULE_TOPDIR = ../.. PGM = r.category -LIBES = $(RASTERLIB) $(GISLIB) +LIBES = $(RASTERLIB) $(GISLIB) $(PARSONLIB) DEPENDENCIES = $(RASTERDEP) $(GISDEP) include $(MODULE_TOPDIR)/include/Make/Module.make diff --git a/raster/r.category/local_proto.h b/raster/r.category/local_proto.h index 05514fbb9ee..370f98cd406 100644 --- a/raster/r.category/local_proto.h +++ b/raster/r.category/local_proto.h @@ -18,13 +18,18 @@ #ifndef __LOCAL_PROTO_H__ #define __LOCAL_PROTO_H__ +#include + +enum OutputFormat { PLAIN, JSON }; + /* cats.c */ int get_cats(const char *, const char *); int next_cat(long *); /* main.c */ -int print_label(long); -int print_d_label(double); +void print_json(JSON_Value *); +int print_label(long, enum OutputFormat, JSON_Array *); +int print_d_label(double, enum OutputFormat, JSON_Array *); int scan_cats(const char *, long *, long *); int scan_vals(const char *, double *); diff --git a/raster/r.category/main.c b/raster/r.category/main.c index b526ba657e2..d0b945a9064 100644 --- a/raster/r.category/main.c +++ b/raster/r.category/main.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "local_proto.h" static struct Categories cats; @@ -39,9 +40,13 @@ int main(int argc, char *argv[]) int from_stdin = FALSE; struct GModule *module; + enum OutputFormat format; + JSON_Value *root_value; + JSON_Array *root_array; + struct { struct Option *map, *fs, *cats, *vals, *raster, *file, *fmt_str, - *fmt_coeff; + *fmt_coeff, *format; } parm; G_gisinit(argv[0]); @@ -103,9 +108,25 @@ int main(int argc, char *argv[]) parm.fmt_coeff->description = _("Two pairs of category multiplier and offsets, for $1 and $2"); + parm.format = G_define_standard_option(G_OPT_F_FORMAT); + parm.format->key = "output_format"; + parm.format->guisection = _("Print"); + if (G_parser(argc, argv)) exit(EXIT_FAILURE); + if (strcmp(parm.format->answer, "json") == 0) { + format = JSON; + root_value = json_value_init_array(); + if (root_value == NULL) { + G_fatal_error(_("Failed to initialize JSON array. Out of memory?")); + } + root_array = json_array(root_value); + } + else { + format = PLAIN; + } + name = parm.map->answer; fs = G_option_to_separator(parm.fs); @@ -282,7 +303,10 @@ int main(int argc, char *argv[]) if (map_type == CELL_TYPE) { get_cats(name, mapset); while (next_cat(&x)) - print_label(x); + print_label(x, format, root_array); + if (format == JSON) { + print_json(root_value); + } exit(EXIT_SUCCESS); } } @@ -300,7 +324,10 @@ int main(int argc, char *argv[]) for (i = 0; parm.cats->answers[i]; i++) { scan_cats(parm.cats->answers[i], &x, &y); while (x <= y) - print_label(x++); + print_label(x++, format, root_array); + } + if (format == JSON) { + print_json(root_value); } exit(EXIT_SUCCESS); } @@ -315,31 +342,76 @@ int main(int argc, char *argv[]) } for (i = 0; parm.vals->answers[i]; i++) { scan_vals(parm.vals->answers[i], &dx); - print_d_label(dx); + print_d_label(dx, format, root_array); + } + + if (format == JSON) { + print_json(root_value); } + exit(EXIT_SUCCESS); } -int print_label(long x) +void print_json(JSON_Value *root_value) +{ + char *serialized_string = NULL; + serialized_string = json_serialize_to_string_pretty(root_value); + if (serialized_string == NULL) { + G_fatal_error(_("Failed to initialize pretty JSON string.")); + } + puts(serialized_string); + json_free_serialized_string(serialized_string); + json_value_free(root_value); +} + +int print_label(long x, enum OutputFormat format, JSON_Array *root_array) { char *label; + JSON_Value *category_value; + JSON_Object *category; G_squeeze(label = Rast_get_c_cat((CELL *)&x, &cats)); - fprintf(stdout, "%ld%s%s\n", x, fs, label); + + switch (format) { + case PLAIN: + fprintf(stdout, "%ld%s%s\n", x, fs, label); + break; + case JSON: + category_value = json_value_init_object(); + category = json_object(category_value); + json_object_set_number(category, "category", x); + json_object_set_string(category, "description", label); + json_array_append_value(root_array, category_value); + break; + } return 0; } -int print_d_label(double x) +int print_d_label(double x, enum OutputFormat format, JSON_Array *root_array) { char *label, tmp[40]; DCELL dtmp; + JSON_Value *category_value; + JSON_Object *category; dtmp = x; G_squeeze(label = Rast_get_d_cat(&dtmp, &cats)); - sprintf(tmp, "%.10f", x); - G_trim_decimal(tmp); - fprintf(stdout, "%s%s%s\n", tmp, fs, label); + + switch (format) { + case PLAIN: + sprintf(tmp, "%.10f", x); + G_trim_decimal(tmp); + fprintf(stdout, "%s%s%s\n", tmp, fs, label); + break; + case JSON: + category_value = json_value_init_object(); + category = json_object(category_value); + json_object_set_number(category, "category", x); + json_object_set_string(category, "description", label); + json_array_append_value(root_array, category_value); + break; + } return 0; } diff --git a/raster/r.category/r.category.html b/raster/r.category/r.category.html index 9a10e63779c..4dba832a98c 100644 --- a/raster/r.category/r.category.html +++ b/raster/r.category/r.category.html @@ -147,6 +147,26 @@

          Printing categories

          as the character separating the category values from the category values in the output. +

          +

          +r.category map=landclass96 cats=3,4 output_format=json
          +
          + +generates the following JSON output: + +
          +[
          +    {
          +        "category": 3,
          +        "description": "herbaceous"
          +    },
          +    {
          +        "category": 4,
          +        "description": "shrubland"
          +    }
          +]
          +
          +

          Adding categories

          Example for defining new category labels, using a colon as separator: diff --git a/raster/r.category/test_rcategory_doctest.txt b/raster/r.category/test_rcategory_doctest.txt index 9276c3e9e62..b8424bbb253 100644 --- a/raster/r.category/test_rcategory_doctest.txt +++ b/raster/r.category/test_rcategory_doctest.txt @@ -224,6 +224,22 @@ Some of these commands should not work and return 1. +JSON Output +=========== +>>> print(read_command('r.category', map='test', output_format='json')) +[ + { + "category": 1, + "description": "trees, very green" + }, + { + "category": 2, + "description": "water, very deep" + } +] + + + Clean the results ================= From c223cd02be7f348374122f616a1d2c96cb51083d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 20 Jul 2024 18:49:32 -0400 Subject: [PATCH 044/471] CI(deps): Update ruff to v0.5.4 (#4078) --- .github/workflows/python-code-quality.yml | 2 +- .pre-commit-config.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index bcfe97ec418..4c953ef15ac 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -36,7 +36,7 @@ jobs: # renovate: datasource=pypi depName=bandit BANDIT_VERSION: "1.7.9" # renovate: datasource=pypi depName=ruff - RUFF_VERSION: "0.5.3" + RUFF_VERSION: "0.5.4" runs-on: ${{ matrix.os }} permissions: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 143ab87d44e..84b3749ed1d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -37,7 +37,7 @@ repos: ) - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.5.3 + rev: v0.5.4 hooks: # Run the linter. - id: ruff From fdf087c9fecf7c5ba30a268457587281acf3954b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Sun, 21 Jul 2024 13:05:14 -0400 Subject: [PATCH 045/471] pytest: Collect all uncovered Python files in code coverage (#4077) * Create .coveragerc config file to keep options when using subprocess * Create coverage_mapper.py to fix non-standard installation paths of scripts * pytest: Upload artifact of HTML code coverage report containing test context * pytest: Fix non-standard installed scripts paths in coverage data * Update .gitignore --- .coveragerc | 64 ++++++++++++++++++++++++++++++++++++ .github/workflows/pytest.yml | 20 ++++++++++- .gitignore | 2 ++ utils/coverage_mapper.py | 41 +++++++++++++++++++++++ 4 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 .coveragerc create mode 100644 utils/coverage_mapper.py diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 00000000000..2dfe1352b28 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,64 @@ +[run] +; branch = True +; dynamic_context = test_function +concurrency = multiprocessing,thread +parallel = True +data_file = ${INITIAL_PWD-.}/.coverage +omit = + ${INITIAL_PWD-.}/testreport + ${INITIAL_PWD-.}/.github/* + ${INITIAL_PWD-.}/bin.*/* + ${INITIAL_PWD-.}/dist.*/* + **/OBJ.*/* +source = + . + ${INITIAL_PWD-.}/ + ${INITIAL_GISBASE-/usr/local/grass??}/ + +[paths] +root = + ./ + ${INITIAL_GISBASE-/usr/local/grass??}/ + /home/*/install/grass??/ +python = + ./python/ + ${INITIAL_GISBASE-/usr/local/grass??}/etc/python/ + /home/*/install/grass??/etc/python/ +special_d_mon = + ./display/d.mon/ + ${INITIAL_GISBASE-/usr/local/grass??}/etc/d.mon/ + /home/*/install/grass??/etc/d.mon/ +special_r_in_wms = + ./scripts/r.in.wms/ + ${INITIAL_GISBASE-/usr/local/grass??}/etc/r.in.wms/ + /home/*/install/grass??/etc/r.in.wms/ + + +[report] +; Since our file structure isn't an importable package, not all files are found +; This allows to find python files even if there is missing __init__.py files, but is slow +include_namespace_packages = True +skip_covered = False +; Regexes for lines to exclude from consideration +exclude_also = + ; Don't complain about missing debug-only code: + def __repr__ + if self\.debug + + ; Don't complain if tests don't hit defensive assertion code: + raise AssertionError + raise NotImplementedError + + ; Don't complain if non-runnable code isn't run: + ; if 0: + ; if __name__ == .__main__.: + + ; Don't complain about abstract methods, they aren't run: + @(abc\.)?abstractmethod + +ignore_errors = True +precision = 2 + +[html] +directory = coverage_html_report +show_contexts = true diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 1600be8fda6..70ad658265f 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -89,10 +89,21 @@ jobs: run: | export PYTHONPATH=`grass --config python_path`:$PYTHONPATH export LD_LIBRARY_PATH=$(grass --config path)/lib:$LD_LIBRARY_PATH - pytest --verbose --color=yes --durations=0 --durations-min=0.5 \ + export INITIAL_GISBASE="$(grass --config path)" + INITIAL_PWD="${PWD}" pytest --verbose --color=yes --durations=0 --durations-min=0.5 \ --cov \ + --cov-context=test \ -ra . \ -m 'needs_solo_run' + - name: Fix non-standard installed script paths in coverage data + run: | + export PYTHONPATH=`grass --config python_path`:$PYTHONPATH + export LD_LIBRARY_PATH=$(grass --config path)/lib:$LD_LIBRARY_PATH + export INITIAL_GISBASE="$(grass --config path)" + export INITIAL_PWD="${PWD}" + python utils/coverage_mapper.py + coverage combine + coverage html - name: Upload coverage reports to Codecov uses: codecov/codecov-action@e28ff129e5465c2c0dcc6f003fc735cb6ae0c673 # v4.5.0 @@ -101,6 +112,13 @@ jobs: flags: pytest-python-${{ matrix.python-version }} name: pytest-python-${{ matrix.python-version }} token: ${{ secrets.CODECOV_TOKEN }} + - name: Make python-only code coverage test report available + if: ${{ !cancelled() }} + uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4 + with: + name: python-codecoverage-report-${{ matrix.os }}-${{ matrix.python-version }} + path: coverage_html_report + retention-days: 1 pytest-success: name: pytest Result diff --git a/.gitignore b/.gitignore index 42fd7650544..49317acf642 100644 --- a/.gitignore +++ b/.gitignore @@ -65,3 +65,5 @@ lib/*/latex/ *.gcno *.gcda .coverage +.coverage.* +coverage.xml diff --git a/utils/coverage_mapper.py b/utils/coverage_mapper.py new file mode 100644 index 00000000000..9a2f1389781 --- /dev/null +++ b/utils/coverage_mapper.py @@ -0,0 +1,41 @@ +import os +import subprocess +from pathlib import Path + + +def get_grass_config_path(): + grass_config_path = None + try: + grass_config_path = subprocess.run( + ["grass", "--config", "path"], capture_output=True, text=True, check=True + ).stdout.rstrip() + except OSError: + grass_config_path = None + return grass_config_path + + +INITIAL_GISBASE = os.getenv("INITIAL_GISBASE", get_grass_config_path()) +INITIAL_PWD = os.getenv("INITIAL_PWD", str(Path.cwd().absolute())) + + +def map_scripts_paths(old_path): + if INITIAL_GISBASE is None or INITIAL_PWD is None: + return old_path + p = Path(old_path) + temporal_base = Path(INITIAL_GISBASE) / "scripts" / "t.*" + base = Path(INITIAL_GISBASE) / "scripts" / "*" + if p.match(str(temporal_base)): + return str(Path(INITIAL_PWD) / "temporal" / (p.name) / (p.name)) + ".py" + if p.match(str(base)): + return str(Path(INITIAL_PWD) / "scripts" / (p.name) / (p.name)) + ".py" + + return old_path + + +if __name__ == "__main__": + from coverage import CoverageData + + a = CoverageData(".coverage") + b = CoverageData(".coverage.fixed_scripts") + b.update(a, map_path=map_scripts_paths) + b.write() From 1f6fa6c62e7d53f6daa980098735f73f9e3e16b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Sun, 21 Jul 2024 13:07:13 -0400 Subject: [PATCH 046/471] style: Fix needless-bool (SIM103) (#4055) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * style: Fix needless-bool (SIM103) Return the condition directly Ruff rule: https://docs.astral.sh/ruff/rules/needless-bool/ Added bool return type annotations when possible to confirm no other type was possible on changed functions * Update frame.py * Apply suggestions from code review Co-authored-by: Edouard Choinière <27212526+echoix@users.noreply.github.com> * style: Merge more if conditions per review --------- Co-authored-by: Stefan Blumentrath --- gui/wxpython/animation/nviztask.py | 7 +- gui/wxpython/core/globalvar.py | 15 +- gui/wxpython/core/render.py | 6 +- gui/wxpython/core/treemodel.py | 14 +- gui/wxpython/core/utils.py | 15 +- gui/wxpython/dbmgr/base.py | 7 +- gui/wxpython/gmodeler/model.py | 21 +- gui/wxpython/gui_core/vselect.py | 7 +- gui/wxpython/iclass/frame.py | 18 +- gui/wxpython/iscatt/plots.py | 7 +- gui/wxpython/location_wizard/wizard.py | 6 +- gui/wxpython/mapdisp/gprint.py | 7 +- gui/wxpython/mapdisp/statusbar.py | 6 +- gui/wxpython/mapswipe/dialogs.py | 6 +- gui/wxpython/mapwin/decorations.py | 22 +- gui/wxpython/modules/colorrules.py | 16 +- gui/wxpython/psmap/dialogs.py | 12 +- gui/wxpython/rlisetup/functions.py | 7 +- gui/wxpython/timeline/frame.py | 7 +- gui/wxpython/tplot/frame.py | 7 +- gui/wxpython/vdigit/wxdisplay.py | 14 +- gui/wxpython/vnet/vnet_data.py | 6 +- gui/wxpython/vnet/widgets.py | 7 +- gui/wxpython/web_services/cap_interface.py | 19 +- gui/wxpython/web_services/widgets.py | 19 +- lib/init/grass.py | 6 +- man/build_class_graphical.py | 14 +- man/build_manual_gallery.py | 6 +- pyproject.toml | 1 - python/grass/app/data.py | 6 +- python/grass/grassdb/checks.py | 28 +-- python/grass/grassdb/data.py | 6 +- python/grass/gunittest/checkers.py | 7 +- python/grass/gunittest/gutils.py | 7 +- python/grass/pygrass/utils.py | 6 +- python/grass/script/core.py | 7 +- python/grass/temporal/abstract_dataset.py | 6 +- python/grass/temporal/base.py | 7 +- python/grass/temporal/spatial_extent.py | 217 +++++------------- python/grass/temporal/temporal_extent.py | 176 +++++--------- python/grass/temporal/temporal_granularity.py | 12 +- scripts/g.search.modules/g.search.modules.py | 17 +- scripts/r.in.wms/wms_cap_parsers.py | 27 +-- scripts/r.in.wms/wms_drv.py | 8 +- scripts/r.tileset/r.tileset.py | 7 +- 45 files changed, 275 insertions(+), 574 deletions(-) diff --git a/gui/wxpython/animation/nviztask.py b/gui/wxpython/animation/nviztask.py index 92a6e767ce0..437bba7946f 100644 --- a/gui/wxpython/animation/nviztask.py +++ b/gui/wxpython/animation/nviztask.py @@ -285,11 +285,8 @@ def _join(self, toJoin, delim=","): toJoin = filter(self._ignore, toJoin) return delim.join(map(str, toJoin)) - def _ignore(self, value): - if value == "" or value is None: - return False - else: - return True + def _ignore(self, value) -> bool: + return not (value == "" or value is None) def ListMapParameters(self): # params = self.task.get_list_params() diff --git a/gui/wxpython/core/globalvar.py b/gui/wxpython/core/globalvar.py index 60fdf7077ba..7624183796c 100644 --- a/gui/wxpython/core/globalvar.py +++ b/gui/wxpython/core/globalvar.py @@ -71,23 +71,18 @@ def version_as_string(version): return ".".join(texts) -def CheckWxPhoenix(): - if "phoenix" in wx.version(): - return True - return False +def CheckWxPhoenix() -> bool: + return "phoenix" in wx.version() -def CheckWxVersion(version): +def CheckWxVersion(version) -> bool: """Check wx version. :return: True if current wx version is greater or equal than - specifed version otherwise False + specified version otherwise False """ parsed_version = parse_version_string(wx.__version__) - if parsed_version < version: - return False - - return True + return not parsed_version < version def CheckForWx(): diff --git a/gui/wxpython/core/render.py b/gui/wxpython/core/render.py index 6e74ade9dc1..021f3aa6500 100644 --- a/gui/wxpython/core/render.py +++ b/gui/wxpython/core/render.py @@ -284,11 +284,9 @@ def IsHidden(self): """Check if layer is hidden""" return self.hidden - def IsRendered(self): + def IsRendered(self) -> bool: """!Check if layer was rendered (if the image file exists)""" - if os.path.exists(self.mapfile): - return True - return False + return bool(os.path.exists(self.mapfile)) def SetType(self, ltype): """Set layer type""" diff --git a/gui/wxpython/core/treemodel.py b/gui/wxpython/core/treemodel.py index 3bcbd6c6ae2..dd84fed7bf9 100644 --- a/gui/wxpython/core/treemodel.py +++ b/gui/wxpython/core/treemodel.py @@ -229,15 +229,13 @@ def nprint(self, text, indent=0): for child in self.children: child.nprint(text, indent + 2) - def match(self, key, value): + def match(self, key, value) -> bool: """Method used for searching according to given parameters. :param value: dictionary value to be matched :param key: data dictionary key """ - if key in self.data and self.data[key] == value: - return True - return False + return bool(key in self.data and self.data[key] == value) class DictFilterNode(DictNode): @@ -269,7 +267,7 @@ def _match_exact(self, **kwargs): return False return True - def _match_filtering(self, **kwargs): + def _match_filtering(self, **kwargs) -> bool: """Match method for filtering.""" if ( "type" in kwargs @@ -277,13 +275,11 @@ def _match_filtering(self, **kwargs): and kwargs["type"] != self.data["type"] ): return False - if ( + return not ( "name" in kwargs and "name" in self.data and not kwargs["name"].search(self.data["name"]) - ): - return False - return True + ) class ModuleNode(DictNode): diff --git a/gui/wxpython/core/utils.py b/gui/wxpython/core/utils.py index 5e52fc0532d..68242f04984 100644 --- a/gui/wxpython/core/utils.py +++ b/gui/wxpython/core/utils.py @@ -1077,7 +1077,7 @@ def autoCropImageFromFile(filename): return wx.Image(filename) -def isInRegion(regionA, regionB): +def isInRegion(regionA, regionB) -> bool: """Tests if 'regionA' is inside of 'regionB'. For example, region A is a display region and region B is some reference @@ -1097,15 +1097,12 @@ def isInRegion(regionA, regionB): :return: True if region A is inside of region B :return: False otherwise """ - if ( + return bool( regionA["s"] >= regionB["s"] and regionA["n"] <= regionB["n"] and regionA["w"] >= regionB["w"] and regionA["e"] <= regionB["e"] - ): - return True - - return False + ) def do_doctest_gettext_workaround(): @@ -1187,11 +1184,9 @@ def get_shell_pid(env=None): return None -def is_shell_running(): +def is_shell_running() -> bool: """Return True if a separate shell is registered in the GIS environment""" - if get_shell_pid() is None: - return False - return True + return get_shell_pid() is not None def parse_mapcalc_cmd(command): diff --git a/gui/wxpython/dbmgr/base.py b/gui/wxpython/dbmgr/base.py index 5aa5011ce9a..2a58ce13e88 100644 --- a/gui/wxpython/dbmgr/base.py +++ b/gui/wxpython/dbmgr/base.py @@ -716,12 +716,9 @@ def GetSortImages(self): def OnGetItemImage(self, item): return -1 - def IsEmpty(self): + def IsEmpty(self) -> bool: """Check if list if empty""" - if self.columns: - return False - - return True + return not self.columns def _updateColSortFlag(self): """ diff --git a/gui/wxpython/gmodeler/model.py b/gui/wxpython/gmodeler/model.py index 72e1e0419d3..4596e0b04eb 100644 --- a/gui/wxpython/gmodeler/model.py +++ b/gui/wxpython/gmodeler/model.py @@ -475,12 +475,9 @@ def AddItem(self, newItem, pos=-1): # item.SetId(i) # i += 1 - def IsValid(self): + def IsValid(self) -> bool: """Return True if model is valid""" - if self.Validate(): - return False - - return True + return not self.Validate() def Validate(self): """Validate model, return None if model is valid otherwise @@ -820,12 +817,9 @@ def Update(self): for item in self.items: item.Update() - def IsParameterized(self): + def IsParameterized(self) -> bool: """Return True if model is parameterized""" - if self.Parameterize(): - return True - - return False + return bool(self.Parameterize()) def Parameterize(self): """Return parameterized options""" @@ -3785,9 +3779,6 @@ def GetErrors(self): return errList - def DeleteIntermediateData(self): + def DeleteIntermediateData(self) -> bool: """Check if to detele intermediate data""" - if self.interData.IsShown() and self.interData.IsChecked(): - return True - - return False + return bool(self.interData.IsShown() and self.interData.IsChecked()) diff --git a/gui/wxpython/gui_core/vselect.py b/gui/wxpython/gui_core/vselect.py index cefcc596628..0ff30afaafd 100644 --- a/gui/wxpython/gui_core/vselect.py +++ b/gui/wxpython/gui_core/vselect.py @@ -208,7 +208,7 @@ def _onMapClickHandler(self, event): if self._dialog: self._dialog.Raise() - def AddVecInfo(self, vInfoDictTMP): + def AddVecInfo(self, vInfoDictTMP) -> bool: """Update vector in list Note: click on features add category @@ -232,10 +232,7 @@ def AddVecInfo(self, vInfoDictTMP): if self._dialog: self.slist.AddItem(vInfoDictTMP) - if len(self.selectedFeatures) == 0: - return False - - return True + return not len(self.selectedFeatures) == 0 def _draw(self): """Call class 'VectorSelectHighlighter' to draw selected features""" diff --git a/gui/wxpython/iclass/frame.py b/gui/wxpython/iclass/frame.py index 3aede6480c8..c6a401ad4d6 100644 --- a/gui/wxpython/iclass/frame.py +++ b/gui/wxpython/iclass/frame.py @@ -263,7 +263,7 @@ def CreateTempVector(self): return vectorName - def RemoveTempVector(self): + def RemoveTempVector(self) -> bool: """Removes temporary vector map with training areas""" ret = RunCommand( prog="g.remove", @@ -272,20 +272,16 @@ def RemoveTempVector(self): type="vector", name=self.trainingAreaVector, ) - if ret != 0: - return False - return True + return bool(ret == 0) - def RemoveTempRaster(self, raster): + def RemoveTempRaster(self, raster) -> bool: """Removes temporary raster maps""" self.GetFirstMap().Clean() self.GetSecondMap().Clean() ret = RunCommand( prog="g.remove", parent=self, flags="f", type="raster", name=raster ) - if ret != 0: - return False - return True + return bool(ret == 0) def AddToolbar(self, name): """Add defined toolbar to the window @@ -817,7 +813,7 @@ def OnExportAreas(self, event): parent=self, ) - def ExportAreas(self, vectorName, withTable): + def ExportAreas(self, vectorName, withTable) -> bool: """Export training areas to new vector map (with attribute table). :param str vectorName: name of exported vector map @@ -930,9 +926,7 @@ def ExportAreas(self, vectorName, withTable): ) wx.EndBusyCursor() os.remove(dbFile.name) - if ret != 0: - return False - return True + return bool(ret == 0) def _runDBUpdate(self, tmpFile, table, column, value, cat): """Helper function for UPDATE statement diff --git a/gui/wxpython/iscatt/plots.py b/gui/wxpython/iscatt/plots.py index f9cad136314..08afe5906ed 100644 --- a/gui/wxpython/iscatt/plots.py +++ b/gui/wxpython/iscatt/plots.py @@ -565,7 +565,7 @@ def _rendDtFilesToMemmaps(rend_dt): del rend_dt[k]["sh"] -def _renderCat(cat_id, rend_dt, scatt, styles): +def _renderCat(cat_id, rend_dt, scatt, styles) -> bool: return True if cat_id not in rend_dt: @@ -574,10 +574,7 @@ def _renderCat(cat_id, rend_dt, scatt, styles): return False if scatt["render"]: return True - if cat_id != 0 and rend_dt[cat_id]["color"] != styles[cat_id]["color"]: - return True - - return False + return bool(cat_id != 0 and rend_dt[cat_id]["color"] != styles[cat_id]["color"]) def _getColorMap(cat_id, styles): diff --git a/gui/wxpython/location_wizard/wizard.py b/gui/wxpython/location_wizard/wizard.py index bdd955a7d30..5a206fafa52 100644 --- a/gui/wxpython/location_wizard/wizard.py +++ b/gui/wxpython/location_wizard/wizard.py @@ -293,11 +293,9 @@ def _nameValidationFailed(self, ctrl): ).format(ctrl.GetValue(), "/\"'@,=*~") GError(parent=self, message=message, caption=_("Invalid name")) - def _checkLocationNotExists(self, text): + def _checkLocationNotExists(self, text) -> bool: """Check whether user's input location exists or not.""" - if location_exists(self.tgisdbase.GetLabel(), text): - return False - return True + return not location_exists(self.tgisdbase.GetLabel(), text) def _locationAlreadyExists(self, ctrl): message = _( diff --git a/gui/wxpython/mapdisp/gprint.py b/gui/wxpython/mapdisp/gprint.py index c951475c132..b72a265f035 100644 --- a/gui/wxpython/mapdisp/gprint.py +++ b/gui/wxpython/mapdisp/gprint.py @@ -41,11 +41,8 @@ def OnEndPrinting(self): def OnPreparePrinting(self): super().OnPreparePrinting() - def HasPage(self, page): - if page <= 2: - return True - else: - return False + def HasPage(self, page) -> bool: + return page <= 2 def GetPageInfo(self): return (1, 2, 1, 2) diff --git a/gui/wxpython/mapdisp/statusbar.py b/gui/wxpython/mapdisp/statusbar.py index 0c5bac10124..72e9d0afdbc 100644 --- a/gui/wxpython/mapdisp/statusbar.py +++ b/gui/wxpython/mapdisp/statusbar.py @@ -107,16 +107,14 @@ def GetProperty(self, name): """ return self.statusbarItems[name].GetValue() - def HasProperty(self, name): + def HasProperty(self, name) -> bool: """Checks whether property is represented by one of contained SbItems :param name: name of SbItem (from name attribute) :return: True if particular SbItem is contained, False otherwise """ - if name in self.statusbarItems: - return True - return False + return name in self.statusbarItems def AddStatusbarItem(self, item): """Adds item to statusbar""" diff --git a/gui/wxpython/mapswipe/dialogs.py b/gui/wxpython/mapswipe/dialogs.py index ad846fd3220..945aee590ea 100644 --- a/gui/wxpython/mapswipe/dialogs.py +++ b/gui/wxpython/mapswipe/dialogs.py @@ -244,10 +244,8 @@ def GetValues(self): else: return (self._firstLayerList, self._secondLayerList) - def IsSimpleMode(self): - if self._switchSizer.IsShown(self._firstPanel): - return True - return False + def IsSimpleMode(self) -> bool: + return bool(self._switchSizer.IsShown(self._firstPanel)) def GetFirstSimpleLmgr(self): return self._firstLmgr diff --git a/gui/wxpython/mapwin/decorations.py b/gui/wxpython/mapwin/decorations.py index fef02581715..7ab010a1d3f 100644 --- a/gui/wxpython/mapwin/decorations.py +++ b/gui/wxpython/mapwin/decorations.py @@ -126,10 +126,10 @@ def SetDialog(self, win): dialog = property(fget=GetDialog, fset=SetDialog) - def IsShown(self): - if self._overlay and self._overlay.IsActive() and self._overlay.IsRendered(): - return True - return False + def IsShown(self) -> bool: + return bool( + self._overlay and self._overlay.IsActive() and self._overlay.IsRendered() + ) def Show(self, show=True): """Activate or deactivate overlay.""" @@ -168,7 +168,7 @@ def _add(self): def _update(self): self._renderer.ChangeOverlay(id=self._id, command=self._cmd) - def CmdIsValid(self): + def CmdIsValid(self) -> bool: """If command is valid""" return True @@ -205,7 +205,7 @@ def __init__(self, renderer, giface): self._defaultAt = "at=50,50" self._cmd = ["d.text", self._defaultAt] - def CmdIsValid(self): + def CmdIsValid(self) -> bool: inputs = 0 for param in self._cmd[1:]: param = param.split("=") @@ -213,9 +213,7 @@ def CmdIsValid(self): inputs += 1 elif param[0] == "text" and len(param) == 2: inputs += 1 - if inputs >= 1: - return True - return False + return inputs >= 1 class BarscaleController(OverlayController): @@ -311,7 +309,7 @@ def GetPlacement(self, screensize): return x, y - def CmdIsValid(self): + def CmdIsValid(self) -> bool: inputs = 0 for param in self._cmd[1:]: param = param.split("=") @@ -321,9 +319,7 @@ def CmdIsValid(self): inputs += 1 elif param[0] == "raster_3d" and len(param) == 2: inputs += 1 - if inputs == 1: - return True - return False + return inputs == 1 def ResizeLegend(self, begin, end, screenSize): """Resize legend according to given bbox coordinates.""" diff --git a/gui/wxpython/modules/colorrules.py b/gui/wxpython/modules/colorrules.py index 569bb4e5d7d..0772dc7f9d5 100644 --- a/gui/wxpython/modules/colorrules.py +++ b/gui/wxpython/modules/colorrules.py @@ -772,7 +772,7 @@ def LoadTable(self, mapType="raster"): self.ReadColorTable(ctable=ctable) - def CreateColorTable(self, tmp=False): + def CreateColorTable(self, tmp=False) -> bool: """Creates color table :return: True on success @@ -822,10 +822,7 @@ def CreateColorTable(self, tmp=False): cmd = cmdlist_to_tuple(cmd) ret = RunCommand(cmd[0], **cmd[1]) - if ret != 0: - return False - - return True + return bool(ret == 0) def DoPreview(self, ltype, cmdlist): """Update preview (based on computational region)""" @@ -1241,15 +1238,12 @@ def OnPaneChanged(self, event=None): else: self.cp.SetLabel(_("Import or export color table")) - def CheckMapset(self): + def CheckMapset(self) -> bool: """Check if current vector is in current mapset""" - if ( + return bool( gs.find_file(name=self.inmap, element="vector")["mapset"] == gs.gisenv()["MAPSET"] - ): - return True - else: - return False + ) def NoConnection(self, vectorName): dlg = wx.MessageDialog( diff --git a/gui/wxpython/psmap/dialogs.py b/gui/wxpython/psmap/dialogs.py index 42a3ef862e3..740678de3cb 100644 --- a/gui/wxpython/psmap/dialogs.py +++ b/gui/wxpython/psmap/dialogs.py @@ -2085,11 +2085,9 @@ def __init__(self, parent, id, settings, env): self.id = self.rPanel.getId() self._layout(self.rPanel) - def update(self): + def update(self) -> bool: ok = self.rPanel.update() - if ok: - return True - return False + return bool(ok) def OnApply(self, event): ok = self.update() @@ -4410,12 +4408,10 @@ def updateVectorLegend(self): self.parent.objectId.append(self.id[1]) return True - def update(self): + def update(self) -> bool: okR = self.updateRasterLegend() okV = self.updateVectorLegend() - if okR and okV: - return True - return False + return bool(okR and okV) def updateDialog(self): """Update legend coordinates after moving""" diff --git a/gui/wxpython/rlisetup/functions.py b/gui/wxpython/rlisetup/functions.py index 2b9433ea4d6..28f186fa396 100644 --- a/gui/wxpython/rlisetup/functions.py +++ b/gui/wxpython/rlisetup/functions.py @@ -65,15 +65,12 @@ def retRLiPath(): return rlipath -def checkMapExists(name, typ="raster"): +def checkMapExists(name, typ="raster") -> bool: """Check if a map already exist in the working mapset""" env = grass.gisenv() mapset = env["MAPSET"] mapp = grass.find_file(name, typ, mapset) - if mapp.name != "": - return True - else: - return False + return bool(mapp.name != "") def convertFeature(vect, outrast, cat, origrast, layer="1", overwrite=False): diff --git a/gui/wxpython/timeline/frame.py b/gui/wxpython/timeline/frame.py index 0330adbde3d..bba9b7a3717 100644 --- a/gui/wxpython/timeline/frame.py +++ b/gui/wxpython/timeline/frame.py @@ -57,7 +57,7 @@ COLORS = ["b", "g", "r", "c", "m", "y", "k"] -def check_version(*version): +def check_version(*version) -> bool: """Checks if given version or newer is installed""" versionInstalled = [] for i in mpl.__version__.split("."): @@ -66,10 +66,7 @@ def check_version(*version): versionInstalled.append(v) except ValueError: versionInstalled.append(0) - if versionInstalled < list(version): - return False - else: - return True + return versionInstalled >= list(version) class TimelineFrame(wx.Frame): diff --git a/gui/wxpython/tplot/frame.py b/gui/wxpython/tplot/frame.py index 2f738c93644..85028081158 100755 --- a/gui/wxpython/tplot/frame.py +++ b/gui/wxpython/tplot/frame.py @@ -74,7 +74,7 @@ LINEAR_REG_LINE_COLOR = (0.56, 0.00, 1.00) -def check_version(*version): +def check_version(*version) -> bool: """Checks if given version or newer is installed""" versionInstalled = [] for i in mpl.__version__.split("."): @@ -83,10 +83,7 @@ def check_version(*version): versionInstalled.append(v) except ValueError: versionInstalled.append(0) - if versionInstalled < list(version): - return False - else: - return True + return not versionInstalled < list(version) def findBetween(s, first, last): diff --git a/gui/wxpython/vdigit/wxdisplay.py b/gui/wxpython/vdigit/wxdisplay.py index a318105bd9e..b747f3fa44f 100644 --- a/gui/wxpython/vdigit/wxdisplay.py +++ b/gui/wxpython/vdigit/wxdisplay.py @@ -456,7 +456,7 @@ def _getDrawFlag(self): return ret - def _isSelected(self, line, force=False): + def _isSelected(self, line, force=False) -> bool: """Check if vector object selected? :param line: feature id @@ -464,10 +464,7 @@ def _isSelected(self, line, force=False): :return: True if vector object is selected :return: False if vector object is not selected """ - if line in self.selected["ids"]: - return True - - return False + return line in self.selected["ids"] def _isDuplicated(self, line): """Check for already marked duplicates @@ -556,7 +553,7 @@ def _getSelectType(self): return ftype - def _validLine(self, line): + def _validLine(self, line) -> bool: """Check if feature id is valid :param line: feature id @@ -564,10 +561,7 @@ def _validLine(self, line): :return: True valid feature id :return: False invalid """ - if line > 0 and line <= Vect_get_num_lines(self.poMapInfo): - return True - - return False + return bool(line > 0 and line <= Vect_get_num_lines(self.poMapInfo)) def SelectLinesByBox(self, bbox, ltype=None, drawSeg=False, poMapInfo=None): """Select vector objects by given bounding box diff --git a/gui/wxpython/vnet/vnet_data.py b/gui/wxpython/vnet/vnet_data.py index e86931ca294..a1332398a1e 100644 --- a/gui/wxpython/vnet/vnet_data.py +++ b/gui/wxpython/vnet/vnet_data.py @@ -1464,13 +1464,11 @@ def RemoveDataValidator(self, row): remove_to_angle = self.turn_data[row][2] self.turn_data[prev_row][2] = remove_to_angle - def IsInInterval(self, from_angle, to_angle, angle): + def IsInInterval(self, from_angle, to_angle, angle) -> bool: """Test if a direction includes or not includes a value""" if to_angle < from_angle: to_angle = math.pi * 2 + to_angle if angle < from_angle: angle = math.pi * 2 + angle - if angle > from_angle and angle < to_angle: - return True - return False + return bool(angle > from_angle and angle < to_angle) diff --git a/gui/wxpython/vnet/widgets.py b/gui/wxpython/vnet/widgets.py index 44e88cf3df0..2690ba57ab4 100644 --- a/gui/wxpython/vnet/widgets.py +++ b/gui/wxpython/vnet/widgets.py @@ -532,7 +532,7 @@ def ShowColumn(self, colName, pos): return False - def IsShown(self, colName): + def IsShown(self, colName) -> bool: """Is column shown :param colName: name of column @@ -542,10 +542,7 @@ def IsShown(self, colName): :return: False - if is not shown """ - if self._getColumnNum(colName) == -1: - return False - else: - return True + return not self._getColumnNum(colName) == -1 class EditItem(wx.Dialog): diff --git a/gui/wxpython/web_services/cap_interface.py b/gui/wxpython/web_services/cap_interface.py index 51ef21c4392..4287375dd47 100644 --- a/gui/wxpython/web_services/cap_interface.py +++ b/gui/wxpython/web_services/cap_interface.py @@ -182,10 +182,7 @@ def IsRequestable(self): name = self.xml_ns.Ns("Name") name_node = self.layer_node.find(name) - if name_node is not None: - return True - else: - return False + return name_node is not None class WMTSCapabilities(CapabilitiesBase, WMTSCapabilitiesTree): @@ -307,12 +304,9 @@ def _getProjs(self): layer_projs.append(mat_set_srs) return layer_projs - def IsRequestable(self): + def IsRequestable(self) -> bool: """Is it possible to use the layer for WMTS request?""" - if self.layer_node is None: - return False - else: - return True + return self.layer_node is not None class OnEarthCapabilities(CapabilitiesBase, OnEarthCapabilitiesTree): @@ -364,12 +358,9 @@ def __init__(self, layer_node, parent_layer, id, cap): self.child_layers = [] self.parent_layer = parent_layer - def IsRequestable(self): + def IsRequestable(self) -> bool: """Is it possible to use the layer for NASA OnEarth GetMap request?""" - if self.layer_node is None or self.layer_node.tag == "TiledGroups": - return False - else: - return True + return not (self.layer_node is None or self.layer_node.tag == "TiledGroups") def GetLayerData(self, param): """Get layer data""" diff --git a/gui/wxpython/web_services/widgets.py b/gui/wxpython/web_services/widgets.py index 200d5521b70..67d000d75df 100644 --- a/gui/wxpython/web_services/widgets.py +++ b/gui/wxpython/web_services/widgets.py @@ -1088,18 +1088,21 @@ def SelectLayers(self, l_st_list): """ def checknext(root_item, l_st_list, items_to_sel): - def compare(item, l_name, st_name): + def compare(item, l_name, st_name) -> bool: it_l_name = self.GetItemData(item)["layer"].GetLayerData("name") it_st = self.GetItemData(item)["style"] it_type = self.GetItemData(item)["type"] - if it_l_name == l_name and ( - (not it_st and not st_name) - or (it_st and it_st["name"] == st_name and it_type == "style") - ): - return True - - return False + return bool( + it_l_name == l_name + and ( + not it_st + and not st_name + or it_st + and it_st["name"] == st_name + and it_type == "style" + ) + ) (child, cookie) = self.GetFirstChild(root_item) while child.IsOk(): diff --git a/lib/init/grass.py b/lib/init/grass.py index dac7a6d818e..1231e299049 100755 --- a/lib/init/grass.py +++ b/lib/init/grass.py @@ -730,12 +730,10 @@ def create_location(gisdbase, location, geostring): fatal(err.value.strip('"').strip("'").replace("\\n", os.linesep)) -def can_create_location(gisdbase, location): +def can_create_location(gisdbase, location) -> bool: """Checks if location can be created""" path = os.path.join(gisdbase, location) - if os.path.exists(path): - return False - return True + return not os.path.exists(path) def cannot_create_location_reason(gisdbase, location): diff --git a/man/build_class_graphical.py b/man/build_class_graphical.py index 4e295bb18f5..955f5c2a63e 100644 --- a/man/build_class_graphical.py +++ b/man/build_class_graphical.py @@ -97,17 +97,15 @@ def file_matches(filename, patterns): return False -def starts_with_module(string, module): +def starts_with_module(string, module) -> bool: # not solving: # module = module.replace('wxGUI.', 'g.gui.') # TODO: matches g.mapsets images for g.mapset and d.rast.num for d.rast - if string.startswith(module.replace(".", "_")): - return True - if string.startswith(module.replace(".", "")): - return True - if string.startswith(module): - return True - return False + return bool( + string.startswith(module.replace(".", "_")) + or string.startswith(module.replace(".", "")) + or string.startswith(module) + ) def get_module_image(module, images): diff --git a/man/build_manual_gallery.py b/man/build_manual_gallery.py index ea1ffb449c2..3d381e27877 100755 --- a/man/build_manual_gallery.py +++ b/man/build_manual_gallery.py @@ -94,13 +94,11 @@ """ -def img_in_html(filename, imagename): +def img_in_html(filename, imagename) -> bool: # for some reason, calling search just once is much faster # than calling it on every line (time is spent in _compile) pattern = re.compile("".format(imagename)) - if re.search(pattern, Path(filename).read_text()): - return True - return False + return bool(re.search(pattern, Path(filename).read_text())) def file_matches(filename, patterns): diff --git a/pyproject.toml b/pyproject.toml index 21afb25d782..574b99df4cb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -268,7 +268,6 @@ ignore = [ "S608", # hardcoded-sql-expression "SIM101", # duplicate-isinstance-call "SIM102", # collapsible-if - "SIM103", # needless-bool "SIM105", # suppressible-exception "SIM108", # if-else-block-instead-of-if-exp "SIM109", # compare-with-tuple diff --git a/python/grass/app/data.py b/python/grass/app/data.py index ed7573c82de..439a6c3c4d1 100644 --- a/python/grass/app/data.py +++ b/python/grass/app/data.py @@ -122,7 +122,7 @@ def _copy_startup_location(startup_location, location_in_grassdb): return False -def create_startup_location_in_grassdb(grassdatabase, startup_location_name): +def create_startup_location_in_grassdb(grassdatabase, startup_location_name) -> bool: """Create a new startup location in the given GRASS database. Returns True if a new startup location successfully created @@ -137,9 +137,7 @@ def create_startup_location_in_grassdb(grassdatabase, startup_location_name): # Copy the simple startup_location with some data to GRASS database location_in_grassdb = os.path.join(grassdatabase, startup_location_name) - if _copy_startup_location(startup_location, location_in_grassdb): - return True - return False + return bool(_copy_startup_location(startup_location, location_in_grassdb)) def ensure_default_data_hierarchy(): diff --git a/python/grass/grassdb/checks.py b/python/grass/grassdb/checks.py index b8748de9876..4de65a4cba3 100644 --- a/python/grass/grassdb/checks.py +++ b/python/grass/grassdb/checks.py @@ -85,24 +85,20 @@ def is_location_valid(path, location=None): return os.access(os.path.join(path, "PERMANENT", "DEFAULT_WIND"), os.F_OK) -def is_mapset_current(database, location, mapset): +def is_mapset_current(database, location, mapset) -> bool: """Return True if the given GRASS Mapset is the current mapset""" genv = gisenv() - if ( + return bool( database == genv["GISDBASE"] and location == genv["LOCATION_NAME"] and mapset == genv["MAPSET"] - ): - return True - return False + ) -def is_location_current(database, location): +def is_location_current(database, location) -> bool: """Return True if the given GRASS Location is the current location""" genv = gisenv() - if database == genv["GISDBASE"] and location == genv["LOCATION_NAME"]: - return True - return False + return bool(database == genv["GISDBASE"] and location == genv["LOCATION_NAME"]) def is_current_user_mapset_owner(mapset_path): @@ -207,15 +203,13 @@ def get_mapset_lock_info(mapset_path): return info -def can_start_in_mapset(mapset_path, ignore_lock=False): +def can_start_in_mapset(mapset_path, ignore_lock: bool = False) -> bool: """Check if a mapset from a gisrc file is usable for new session""" - if not is_mapset_valid(mapset_path): - return False - if not is_current_user_mapset_owner(mapset_path): - return False - if not ignore_lock and is_mapset_locked(mapset_path): - return False - return True + return not ( + (not is_mapset_valid(mapset_path)) + or (not is_current_user_mapset_owner(mapset_path)) + or (not ignore_lock and is_mapset_locked(mapset_path)) + ) def get_reason_id_mapset_not_usable(mapset_path): diff --git a/python/grass/grassdb/data.py b/python/grass/grassdb/data.py index 49696b155e1..980b0f02ff3 100644 --- a/python/grass/grassdb/data.py +++ b/python/grass/grassdb/data.py @@ -12,7 +12,7 @@ import grass.script as gs -def map_exists(name, element, mapset=None, env=None): +def map_exists(name, element, mapset=None, env=None) -> bool: """Check is map is present in the mapset given in the environment :param name: Name of the map @@ -42,6 +42,4 @@ def map_exists(name, element, mapset=None, env=None): info = gs.parse_key_val(output, sep="=") # file is the key questioned in grass.script.core find_file() # return code should be equivalent to checking the output - if info["file"]: - return True - return False + return bool(info["file"]) diff --git a/python/grass/gunittest/checkers.py b/python/grass/gunittest/checkers.py index 76f8100577c..fb46a1deb66 100644 --- a/python/grass/gunittest/checkers.py +++ b/python/grass/gunittest/checkers.py @@ -495,7 +495,7 @@ def lowercase_equals(string_a, string_b, precision=None): # TODO: change checking over lines? # TODO: change parameter order? # TODO: the behavior with last \n is strange but now using DOTALL and $ -def check_text_ellipsis(reference, actual): +def check_text_ellipsis(reference, actual) -> bool: r""" >>> check_text_ellipsis( ... "Vector map <...> contains ... points.", @@ -537,10 +537,7 @@ def check_text_ellipsis(reference, actual): ref_escaped = re.escape(reference) exp = re.compile(r"\\\.\\\.\\\.") # matching escaped ... ref_regexp = exp.sub(".+", ref_escaped) + "$" - if re.match(ref_regexp, actual, re.DOTALL): - return True - else: - return False + return bool(re.match(ref_regexp, actual, re.DOTALL)) def check_text_ellipsis_doctest(reference, actual): diff --git a/python/grass/gunittest/gutils.py b/python/grass/gunittest/gutils.py index 16618e729f5..ca5b8f7b436 100644 --- a/python/grass/gunittest/gutils.py +++ b/python/grass/gunittest/gutils.py @@ -21,7 +21,7 @@ def get_current_mapset(): return call_module("g.mapset", flags="p").strip() -def is_map_in_mapset(name, type, mapset=None): +def is_map_in_mapset(name, type, mapset=None) -> bool: """Check is map is present in the mapset (current mapset by default) This function is different from what we would expect in GRASS @@ -60,7 +60,4 @@ def is_map_in_mapset(name, type, mapset=None): info = text_to_keyvalue(decode(output), sep="=") # file is the key questioned in grass.script.core find_file() # return code should be equivalent to checking the output - if info["file"]: - return True - else: - return False + return bool(info["file"]) diff --git a/python/grass/pygrass/utils.py b/python/grass/pygrass/utils.py index e8f4ce68a43..d9d17ade0d9 100644 --- a/python/grass/pygrass/utils.py +++ b/python/grass/pygrass/utils.py @@ -174,7 +174,7 @@ def get_mapset_vector(mapname, mapset=""): return decode(libgis.G_find_vector2(mapname, mapset)) -def is_clean_name(name): +def is_clean_name(name) -> bool: """Return if the name is valid >>> is_clean_name("census") @@ -187,9 +187,7 @@ def is_clean_name(name): False """ - if libgis.G_legal_filename(name) < 0: - return False - return True + return not libgis.G_legal_filename(name) < 0 def coor2pixel(coord, region): diff --git a/python/grass/script/core.py b/python/grass/script/core.py index 20d3f358a82..d46c35d3352 100644 --- a/python/grass/script/core.py +++ b/python/grass/script/core.py @@ -1192,7 +1192,7 @@ def gisenv(env=None): # interface to g.region -def locn_is_latlong(env=None): +def locn_is_latlong(env=None) -> bool: """Tests if location is lat/long. Value is obtained by checking the "g.region -pu" projection code. @@ -1200,10 +1200,7 @@ def locn_is_latlong(env=None): """ s = read_command("g.region", flags="pu", env=env) kv = parse_key_val(s, ":") - if kv["projection"].split(" ")[0] == "3": - return True - else: - return False + return kv["projection"].split(" ")[0] == "3" def region(region3d=False, complete=False, env=None): diff --git a/python/grass/temporal/abstract_dataset.py b/python/grass/temporal/abstract_dataset.py index f8b9a5e1cce..cfe3c83a841 100644 --- a/python/grass/temporal/abstract_dataset.py +++ b/python/grass/temporal/abstract_dataset.py @@ -301,7 +301,7 @@ def get_relative_time_unit(self): """ return self.relative_time.get_unit() - def check_relative_time_unit(self, unit): + def check_relative_time_unit(self, unit) -> bool: """Check if unit is of type year(s), month(s), day(s), hour(s), minute(s) or second(s) @@ -323,9 +323,7 @@ def check_relative_time_unit(self, unit): "second", "seconds", ] - if unit not in units: - return False - return True + return not unit not in units def get_temporal_type(self): """Return the temporal type of this dataset diff --git a/python/grass/temporal/base.py b/python/grass/temporal/base.py index f4175c77139..9296a22dfa1 100644 --- a/python/grass/temporal/base.py +++ b/python/grass/temporal/base.py @@ -313,7 +313,7 @@ def get_is_in_db_statement(self): + "';\n" ) - def is_in_db(self, dbif=None, mapset=None): + def is_in_db(self, dbif=None, mapset=None) -> bool: """Check if this object is present in the temporal database :param dbif: The database interface to be used, @@ -342,10 +342,7 @@ def is_in_db(self, dbif=None, mapset=None): dbif.close() # Nothing found - if row is None: - return False - - return True + return row is not None def get_select_statement(self): """Return the sql statement and the argument list in diff --git a/python/grass/temporal/spatial_extent.py b/python/grass/temporal/spatial_extent.py index 6c025fb7a40..329a0a4a684 100644 --- a/python/grass/temporal/spatial_extent.py +++ b/python/grass/temporal/spatial_extent.py @@ -143,7 +143,7 @@ def __init__( self.set_spatial_extent_from_values(north, south, east, west, top, bottom) self.set_projection(proj) - def overlapping_2d(self, extent): + def overlapping_2d(self, extent) -> bool: """Return True if this (A) and the provided spatial extent (B) overlaps in two dimensional space. Code is lend from wind_overlap.c in lib/gis @@ -191,21 +191,14 @@ def overlapping_2d(self, extent): E -= 360.0 W -= 360.0 - if self.get_north() <= S: - return False - - if self.get_south() >= N: - return False - - if self.get_east() <= W: - return False - - if self.get_west() >= E: - return False - - return True + return not ( + self.get_north() <= S + or self.get_south() >= N + or self.get_east() <= W + or self.get_west() >= E + ) - def overlapping(self, extent): + def overlapping(self, extent) -> bool: """Return True if this (A) and the provided spatial extent (B) overlaps in three dimensional space. @@ -240,13 +233,7 @@ def overlapping(self, extent): T = extent.get_top() B = extent.get_bottom() - if self.get_top() <= B: - return False - - if self.get_bottom() >= T: - return False - - return True + return not (self.get_top() <= B or self.get_bottom() >= T) def intersect_2d(self, extent): """Return the two dimensional intersection as spatial_extent @@ -592,7 +579,7 @@ def disjoint_union(self, extent): return new - def is_in_2d(self, extent): + def is_in_2d(self, extent) -> bool: """Return True if this extent (A) is located in the provided spatial extent (B) in two dimensions. @@ -636,18 +623,9 @@ def is_in_2d(self, extent): eE -= 360.0 eW -= 360.0 - if eW >= W: - return False - if eE <= E: - return False - if eN <= N: - return False - if eS >= S: - return False + return not (eW >= W or eE <= E or eN <= N or eS >= S) - return True - - def is_in(self, extent): + def is_in(self, extent) -> bool: """Return True if this extent (A) is located in the provided spatial extent (B) in three dimensions. @@ -678,12 +656,7 @@ def is_in(self, extent): T = self.get_top() B = self.get_bottom() - if eB >= B: - return False - if eT <= T: - return False - - return True + return not (eB >= B or eT <= T) def contain_2d(self, extent): """Return True if this extent (A) contains the provided spatial @@ -729,7 +702,7 @@ def contain(self, extent): """ return extent.is_in(self) - def equivalent_2d(self, extent): + def equivalent_2d(self, extent) -> bool: """Return True if this extent (A) is equal to the provided spatial extent (B) in two dimensions. @@ -776,18 +749,9 @@ def equivalent_2d(self, extent): eE -= 360.0 eW -= 360.0 - if eW != W: - return False - if eE != E: - return False - if eN != N: - return False - if eS != S: - return False + return not (eW != W or eE != E or eN != N or eS != S) - return True - - def equivalent(self, extent): + def equivalent(self, extent) -> bool: """Return True if this extent (A) is equal to the provided spatial extent (B) in three dimensions. @@ -819,14 +783,9 @@ def equivalent(self, extent): T = self.get_top() B = self.get_bottom() - if eB != B: - return False - if eT != T: - return False - - return True + return not (eB != B or eT != T) - def cover_2d(self, extent): + def cover_2d(self, extent) -> bool: """Return True if this extent (A) covers the provided spatial extent (B) in two dimensions. @@ -916,12 +875,9 @@ def cover_2d(self, extent): if eS > S and eS < N: edge_count += 1 - if edge_count == 0: - return False - - return True + return not edge_count == 0 - def cover(self, extent): + def cover(self, extent) -> bool: """Return True if this extent covers the provided spatial extent in three dimensions. @@ -974,17 +930,7 @@ def cover(self, extent): eW -= 360.0 # Edges of extent located outside of self are not allowed - if eW >= E: - return False - if eE <= W: - return False - if eS >= N: - return False - if eN <= S: - return False - if eB >= T: - return False - if eT <= B: + if (eW >= E) or (eE <= W) or (eS >= N) or (eN <= S) or (eB >= T) or (eT <= B): return False # First we check that at least one edge of extent meets an edge of self @@ -1010,10 +956,7 @@ def cover(self, extent): if eB > B and eB < T: edge_count += 1 - if edge_count == 0: - return False - - return True + return not edge_count == 0 def covered_2d(self, extent): """Return True if this extent is covered by the provided spatial @@ -1047,7 +990,7 @@ def covered(self, extent): return extent.cover(self) - def overlap_2d(self, extent): + def overlap_2d(self, extent) -> bool: """Return True if this extent (A) overlaps with the provided spatial extent (B) in two dimensions. Code is lend from wind_overlap.c in lib/gis @@ -1103,21 +1046,14 @@ def overlap_2d(self, extent): E -= 360.0 W -= 360.0 - if self.get_north() <= S: - return False - - if self.get_south() >= N: - return False - - if self.get_east() <= W: - return False - - if self.get_west() >= E: - return False - - return True + return not ( + self.get_north() <= S + or self.get_south() >= N + or self.get_east() <= W + or self.get_west() >= E + ) - def overlap(self, extent): + def overlap(self, extent) -> bool: """Return True if this extent overlaps with the provided spatial extent in three dimensions. @@ -1165,25 +1101,14 @@ def overlap(self, extent): E -= 360.0 W -= 360.0 - if self.get_north() <= S: - return False - - if self.get_south() >= N: - return False - - if self.get_east() <= W: - return False - - if self.get_west() >= E: - return False - - if self.get_top() <= B: - return False - - if self.get_bottom() >= T: - return False - - return True + return not ( + self.get_north() <= S + or self.get_south() >= N + or self.get_east() <= W + or self.get_west() >= E + or self.get_top() <= B + or self.get_bottom() >= T + ) def meet_2d(self, extent): """Return True if this extent (A) meets with the provided spatial @@ -1270,7 +1195,7 @@ def meet_2d(self, extent): return True - def meet(self, extent): + def meet(self, extent) -> bool: """Return True if this extent meets with the provided spatial extent in three dimensions. @@ -1350,7 +1275,7 @@ def meet(self, extent): return True - def disjoint_2d(self, extent): + def disjoint_2d(self, extent) -> bool: """Return True if this extent (A) is disjoint with the provided spatial extent (B) in three dimensions. @@ -1368,28 +1293,15 @@ def disjoint_2d(self, extent): :return: True or False """ - if self.is_in_2d(extent): - return False - - if self.contain_2d(extent): - return False - - if self.cover_2d(extent): - return False - - if self.covered_2d(extent): - return False - - if self.equivalent_2d(extent): - return False - - if self.overlapping_2d(extent): - return False - - if self.meet_2d(extent): - return False - - return True + return not ( + self.is_in_2d(extent) + or self.contain_2d(extent) + or self.cover_2d(extent) + or self.covered_2d(extent) + or self.equivalent_2d(extent) + or self.overlapping_2d(extent) + or self.meet_2d(extent) + ) def disjoint(self, extent): """Return True if this extent is disjoint with the provided spatial @@ -1399,28 +1311,15 @@ def disjoint(self, extent): :return: True or False """ - if self.is_in(extent): - return False - - if self.contain(extent): - return False - - if self.cover(extent): - return False - - if self.covered(extent): - return False - - if self.equivalent(extent): - return False - - if self.overlapping(extent): - return False - - if self.meet(extent): - return False - - return True + return not ( + self.is_in(extent) + or self.contain(extent) + or self.cover(extent) + or self.covered(extent) + or self.equivalent(extent) + or self.overlapping(extent) + or self.meet(extent) + ) def spatial_relation_2d(self, extent): """Returns the two dimensional spatial relation between this diff --git a/python/grass/temporal/temporal_extent.py b/python/grass/temporal/temporal_extent.py index fd9c63db2fa..42cce4599b1 100644 --- a/python/grass/temporal/temporal_extent.py +++ b/python/grass/temporal/temporal_extent.py @@ -428,7 +428,7 @@ def union(self, extent): return self.disjoint_union(extent) - def starts(self, extent): + def starts(self, extent) -> bool: """Return True if this temporal extent (A) starts at the start of the provided temporal extent (B) and finishes within it :: @@ -455,15 +455,12 @@ def starts(self, extent): if self.D["end_time"] is None or extent.D["end_time"] is None: return False - if ( + return bool( self.D["start_time"] == extent.D["start_time"] and self.D["end_time"] < extent.D["end_time"] - ): - return True - else: - return False + ) - def started(self, extent): + def started(self, extent) -> bool: """Return True if this temporal extent (A) started at the start of the provided temporal extent (B) and finishes after it :: @@ -489,15 +486,12 @@ def started(self, extent): if self.D["end_time"] is None or extent.D["end_time"] is None: return False - if ( + return bool( self.D["start_time"] == extent.D["start_time"] and self.D["end_time"] > extent.D["end_time"] - ): - return True - else: - return False + ) - def finishes(self, extent): + def finishes(self, extent) -> bool: """Return True if this temporal extent (A) starts after the start of the provided temporal extent (B) and finishes with it :: @@ -523,15 +517,12 @@ def finishes(self, extent): if self.D["end_time"] is None or extent.D["end_time"] is None: return False - if ( + return bool( self.D["end_time"] == extent.D["end_time"] and self.D["start_time"] > extent.D["start_time"] - ): - return True - else: - return False + ) - def finished(self, extent): + def finished(self, extent) -> bool: """Return True if this temporal extent (A) starts before the start of the provided temporal extent (B) and finishes with it :: @@ -557,15 +548,12 @@ def finished(self, extent): if self.D["end_time"] is None or extent.D["end_time"] is None: return False - if ( + return bool( self.D["end_time"] == extent.D["end_time"] and self.D["start_time"] < extent.D["start_time"] - ): - return True - else: - return False + ) - def after(self, extent): + def after(self, extent) -> bool: """Return True if this temporal extent (A) is located after the provided temporal extent (B) :: @@ -589,15 +577,9 @@ def after(self, extent): """ if extent.D["end_time"] is None: - if self.D["start_time"] > extent.D["start_time"]: - return True - else: - return False + return self.D["start_time"] > extent.D["start_time"] - if self.D["start_time"] > extent.D["end_time"]: - return True - else: - return False + return self.D["start_time"] > extent.D["end_time"] def before(self, extent): """Return True if this temporal extent (A) is located before the @@ -623,17 +605,11 @@ def before(self, extent): """ if self.D["end_time"] is None: - if self.D["start_time"] < extent.D["start_time"]: - return True - else: - return False + return self.D["start_time"] < extent.D["start_time"] - if self.D["end_time"] < extent.D["start_time"]: - return True - else: - return False + return self.D["end_time"] < extent.D["start_time"] - def adjacent(self, extent): + def adjacent(self, extent) -> bool: """Return True if this temporal extent (A) is a meeting neighbor the provided temporal extent (B) :: @@ -667,14 +643,16 @@ def adjacent(self, extent): if self.D["end_time"] is None and extent.D["end_time"] is None: return False - if (self.D["start_time"] == extent.D["end_time"]) or ( - self.D["end_time"] == extent.D["start_time"] - ): - return True - else: - return False + return bool( + self.D["start_time"] is not None + and extent.D["end_time"] is not None + and ( + self.D["start_time"] == extent.D["end_time"] + or self.D["end_time"] == extent.D["start_time"] + ) + ) - def follows(self, extent): + def follows(self, extent) -> bool: """Return True if this temporal extent (A) follows the provided temporal extent (B) :: @@ -697,15 +675,12 @@ def follows(self, extent): False """ - if extent.D["end_time"] is None: - return False - - if self.D["start_time"] == extent.D["end_time"]: - return True - else: - return False + return ( + extent.D["end_time"] is not None + and self.D["start_time"] == extent.D["end_time"] + ) - def precedes(self, extent): + def precedes(self, extent) -> bool: """Return True if this temporal extent (A) precedes the provided temporal extent (B) :: @@ -730,15 +705,12 @@ def precedes(self, extent): """ - if self.D["end_time"] is None: - return False - - if self.D["end_time"] == extent.D["start_time"]: - return True - else: - return False + return ( + self.D["end_time"] is not None + and self.D["end_time"] == extent.D["start_time"] + ) - def during(self, extent): + def during(self, extent) -> bool: """Return True if this temporal extent (A) is located during the provided temporal extent (B) :: @@ -766,23 +738,17 @@ def during(self, extent): # Check single point of time in interval if self.D["end_time"] is None: - if ( + return bool( self.D["start_time"] >= extent.D["start_time"] and self.D["start_time"] < extent.D["end_time"] - ): - return True - else: - return False + ) - if ( + return bool( self.D["start_time"] > extent.D["start_time"] and self.D["end_time"] < extent.D["end_time"] - ): - return True - else: - return False + ) - def contains(self, extent): + def contains(self, extent) -> bool: """Return True if this temporal extent (A) contains the provided temporal extent (B) :: @@ -811,23 +777,17 @@ def contains(self, extent): # Check single point of time in interval if extent.D["end_time"] is None: - if ( + return bool( self.D["start_time"] <= extent.D["start_time"] and self.D["end_time"] > extent.D["start_time"] - ): - return True - else: - return False + ) - if ( + return bool( self.D["start_time"] < extent.D["start_time"] and self.D["end_time"] > extent.D["end_time"] - ): - return True - else: - return False + ) - def equal(self, extent): + def equal(self, extent) -> bool: """Return True if this temporal extent (A) is equal to the provided temporal extent (B) :: @@ -851,23 +811,17 @@ def equal(self, extent): """ if self.D["end_time"] is None and extent.D["end_time"] is None: - if self.D["start_time"] == extent.D["start_time"]: - return True - else: - return False + return self.D["start_time"] == extent.D["start_time"] if self.D["end_time"] is None or extent.D["end_time"] is None: return False - if ( + return bool( self.D["start_time"] == extent.D["start_time"] and self.D["end_time"] == extent.D["end_time"] - ): - return True - else: - return False + ) - def overlaps(self, extent): + def overlaps(self, extent) -> bool: """Return True if this temporal extent (A) overlapped the provided temporal extent (B) :: @@ -897,19 +851,16 @@ def overlaps(self, extent): False """ - if self.D["end_time"] is None or extent.D["end_time"] is None: - return False - if ( - self.D["start_time"] < extent.D["start_time"] + return bool( + self.D["end_time"] is not None + and extent.D["end_time"] is not None + and self.D["start_time"] < extent.D["start_time"] and self.D["end_time"] < extent.D["end_time"] and self.D["end_time"] > extent.D["start_time"] - ): - return True - else: - return False + ) - def overlapped(self, extent): + def overlapped(self, extent) -> bool: """Return True if this temporal extent (A) overlapps the provided temporal extent (B) :: @@ -940,17 +891,14 @@ def overlapped(self, extent): False """ - if self.D["end_time"] is None or extent.D["end_time"] is None: - return False - if ( - self.D["start_time"] > extent.D["start_time"] + return bool( + self.D["end_time"] is not None + and extent.D["end_time"] is not None + and self.D["start_time"] > extent.D["start_time"] and self.D["end_time"] > extent.D["end_time"] and self.D["start_time"] < extent.D["end_time"] - ): - return True - else: - return False + ) def temporal_relation(self, extent): """Returns the temporal relation between temporal objects diff --git a/python/grass/temporal/temporal_granularity.py b/python/grass/temporal/temporal_granularity.py index d66f6c57ffb..29836959cb8 100644 --- a/python/grass/temporal/temporal_granularity.py +++ b/python/grass/temporal/temporal_granularity.py @@ -150,7 +150,7 @@ def _get_row_time_tuple(db_table_row): return _get_row_time_tuple -def _is_after(start, start1, end1): +def _is_after(start, start1, end1) -> bool: """Helper function that checks if start timestamp is temporally after the start1 and end1, where start1 and end1 represent a temporal extent. @@ -177,15 +177,9 @@ def _is_after(start, start1, end1): """ if end1 is None: - if start > start1: - return True - else: - return False + return start > start1 - if start > end1: - return True - else: - return False + return start > end1 def compute_relative_time_granularity(maps): diff --git a/scripts/g.search.modules/g.search.modules.py b/scripts/g.search.modules/g.search.modules.py index e96600e1495..89ea482bc5b 100755 --- a/scripts/g.search.modules/g.search.modules.py +++ b/scripts/g.search.modules/g.search.modules.py @@ -286,23 +286,22 @@ def _search_module( return sorted(found_modules, key=lambda k: k["name"]) -def _basic_search(pattern, name, description, module_keywords): +def _basic_search(pattern, name, description, module_keywords) -> bool: """Search for a string in all the provided strings. This lowercases the strings before searching in them, so the pattern string should be lowercased too. """ - if name and description and module_keywords: - if ( + return bool( + name + and description + and module_keywords + and ( name.lower().find(pattern) > -1 or description.lower().find(pattern) > -1 or module_keywords.lower().find(pattern) > -1 - ): - return True - else: - return False - else: - return False + ) + ) def _exact_search(keyword, module_keywords): diff --git a/scripts/r.in.wms/wms_cap_parsers.py b/scripts/r.in.wms/wms_cap_parsers.py index 5435fe29591..56d6e788e3a 100644 --- a/scripts/r.in.wms/wms_cap_parsers.py +++ b/scripts/r.in.wms/wms_cap_parsers.py @@ -350,7 +350,7 @@ def __init__(self, cap_file): gs.debug("Check of WMTS capabilities tree was finished.", 4) - def _checkMatSet(self, mat_set): + def _checkMatSet(self, mat_set) -> bool: """!Check .""" mat_set_id = mat_set.find(self.xml_ns.NsOws("Identifier")) if mat_set_id is None or not mat_set_id.text: @@ -370,15 +370,12 @@ def _checkMatSet(self, mat_set): mat_set.remove(t_mat) tile_mats = mat_set.findall(self.xml_ns.NsWmts("TileMatrix")) - if not tile_mats: - return False - - return True + return bool(tile_mats) def _checkMat(self, t_mat): """!Check .""" - def _checkElement(t_mat, e, func): + def _checkElement(t_mat, e, func) -> bool: element = t_mat.find(self.xml_ns.NsWmts(e)) if element is None or not element.text: return False @@ -388,9 +385,7 @@ def _checkElement(t_mat, e, func): except ValueError: return False - if e < 0: - return False - return True + return not e < 0 for e, func in [ ["ScaleDenominator", float], @@ -450,7 +445,7 @@ def _checkLayer(self, layer): return True - def _checkMatSetLink(self, link, mat_sets): + def _checkMatSetLink(self, link, mat_sets) -> bool: """!Check element.""" mat_set_link_id = link.find(self.xml_ns.NsWmts("TileMatrixSet")).text found = False @@ -484,10 +479,7 @@ def _checkMatSetLink(self, link, mat_sets): gs.debug("Removed invalid element.", 4) link.remove(tile_mat_set_limits) - if not found: - return False - - return True + return found def _checkMatSetLimit(self, limit): """!Check element.""" @@ -604,7 +596,7 @@ def _find(self, etreeElement, tag): return res - def _checkLayer(self, layer): + def _checkLayer(self, layer) -> bool: """!Check / elements.""" if layer.tag == "TiledGroups": return True @@ -628,10 +620,7 @@ def _checkLayer(self, layer): patt.text = "\n".join(urls) t_patts = layer.findall("TilePattern") - if not t_patts: - return False - - return True + return bool(t_patts) def _getUrls(self, tile_pattern): """!Get all urls from tile pattern.""" diff --git a/scripts/r.in.wms/wms_drv.py b/scripts/r.in.wms/wms_drv.py index 2f92ec45380..4628c60b8ef 100644 --- a/scripts/r.in.wms/wms_drv.py +++ b/scripts/r.in.wms/wms_drv.py @@ -439,11 +439,11 @@ def _computeRequestData(self, bbox, tl_corner, tile_span, tile_size, mat_num_bbo self.tile_ref = {"sizeX": tile_size["x"], "sizeY": tile_size["y"]} - def _isGeoProj(self, proj): + def _isGeoProj(self, proj) -> bool: """!Is it geographic projection?""" - if proj.find("+proj=latlong") != -1 or proj.find("+proj=longlat") != -1: - return True - return False + return bool( + proj.find("+proj=latlong") != -1 or proj.find("+proj=longlat") != -1 + ) class WMSRequestMgr(BaseRequestMgr): diff --git a/scripts/r.tileset/r.tileset.py b/scripts/r.tileset/r.tileset.py index f2049385a94..f5e7ab17b13 100644 --- a/scripts/r.tileset/r.tileset.py +++ b/scripts/r.tileset/r.tileset.py @@ -217,7 +217,7 @@ def sideLengths(points, xmetric, ymetric): return {"x": (ret[1], ret[3]), "y": (ret[0], ret[2])} -def bboxesIntersect(bbox_1, bbox_2): +def bboxesIntersect(bbox_1, bbox_2) -> bool: """Determine if two bounding boxes intersect""" bi_a1 = (bbox_1["w"], bbox_1["s"]) bi_a2 = (bbox_1["e"], bbox_1["n"]) @@ -233,10 +233,7 @@ def bboxesIntersect(bbox_1, bbox_2): ): cin[i] = True - if cin[0] and cin[1]: - return True - - return False + return bool(cin[0] and cin[1]) def main(): From 7b564ce3a412799ce194d887c4c2c534b3f85072 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2024 02:11:52 +0000 Subject: [PATCH 047/471] CI(deps): Lock file maintenance (#4080) From 4c672cb1bfdb2aeecc04830e7e39b2e19a838572 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2024 07:07:52 -0400 Subject: [PATCH 048/471] CI(deps): Lock file maintenance (#4081) From 1dee2d65c3a5984f72538ebf5afde8a079b736d1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2024 12:21:38 +0000 Subject: [PATCH 049/471] CI(deps): Update docker/build-push-action action to v6.5.0 (#4082) --- .github/workflows/docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 89c3baec8c6..37f6ae747af 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -76,7 +76,7 @@ jobs: password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build and push id: docker_build - uses: docker/build-push-action@1ca370b3a9802c92e886402e0dd88098a2533b12 # v6.4.1 + uses: docker/build-push-action@5176d81f87c23d6fc96624dfdbcd9f3830bbe445 # v6.5.0 with: push: true pull: true From 660bdc68355cd9dcb0372dbabba337ada502a60b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2024 12:22:16 +0000 Subject: [PATCH 050/471] CI(deps): Update docker/login-action action to v3.3.0 (#4083) --- .github/workflows/docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 37f6ae747af..2f17c439bff 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -70,7 +70,7 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@4fd812986e6c8c2a69e18311145f9371337f27d4 # v3.4.0 - name: Login to DockerHub - uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3.2.0 + uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} From eaae0d329b0a8a38db16b19003b5afda5c47ffe4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2024 21:15:32 -0400 Subject: [PATCH 051/471] CI(deps): Update DeterminateSystems/nix-installer-action action to v13 (#4086) --- .github/workflows/test-nix.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-nix.yml b/.github/workflows/test-nix.yml index f9d5297bef6..a73ac835414 100644 --- a/.github/workflows/test-nix.yml +++ b/.github/workflows/test-nix.yml @@ -31,7 +31,7 @@ jobs: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Install nix - uses: DeterminateSystems/nix-installer-action@7993355175c2765e5733dae74f3e0786fe0e5c4f # v12 + uses: DeterminateSystems/nix-installer-action@ab6bcb2d5af0e904d04aea750e2089e9dc4cbfdd # v13 - name: Setup cachix uses: cachix/cachix-action@ad2ddac53f961de1989924296a1f236fcfbaa4fc # v15 From 36cc5b219e31cf1333b2e3878bdfcaf62b8fda79 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 23 Jul 2024 01:28:14 +0000 Subject: [PATCH 052/471] CI(deps): Update alpine:3.20 Docker digest to 0a4eaa0 (#4089) --- docker/alpine/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/alpine/Dockerfile b/docker/alpine/Dockerfile index ad2a878bfae..ea463f5906a 100644 --- a/docker/alpine/Dockerfile +++ b/docker/alpine/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.20@sha256:b89d9c93e9ed3597455c90a0b88a8bbb5cb7188438f70953fede212a0c4394e0 as common +FROM alpine:3.20@sha256:0a4eaa0eecf5f8c050e5bba433f58c052be7587ee8af3e8b3910ef9ab5fbe9f5 as common # Based on: # https://github.com/mundialis/docker-grass-gis/blob/master/Dockerfile From 7f2b7bd9fbcd496579a97cfe41f5728ba2c1d20c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 23 Jul 2024 07:55:39 -0400 Subject: [PATCH 053/471] CI(deps): Update docker/setup-buildx-action action to v3.5.0 (#4084) --- .github/workflows/docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 2f17c439bff..56442875aa3 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -68,7 +68,7 @@ jobs: - name: Set up QEMU uses: docker/setup-qemu-action@5927c834f5b4fdf503fca6f4c7eccda82949e1ee # v3.1.0 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@4fd812986e6c8c2a69e18311145f9371337f27d4 # v3.4.0 + uses: docker/setup-buildx-action@aa33708b10e362ff993539393ff100fa93ed6a27 # v3.5.0 - name: Login to DockerHub uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 with: From d7b13e0a63612cb98d3205eacde3acc922954533 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 23 Jul 2024 07:56:03 -0400 Subject: [PATCH 054/471] CI(deps): Update docker/setup-qemu-action action to v3.2.0 (#4085) --- .github/workflows/docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 56442875aa3..041c4dffa2b 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -66,7 +66,7 @@ jobs: latest=false suffix=-${{ matrix.os }} - name: Set up QEMU - uses: docker/setup-qemu-action@5927c834f5b4fdf503fca6f4c7eccda82949e1ee # v3.1.0 + uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # v3.2.0 - name: Set up Docker Buildx uses: docker/setup-buildx-action@aa33708b10e362ff993539393ff100fa93ed6a27 # v3.5.0 - name: Login to DockerHub From 0306f9333d0ce45e32ee970af569e876c99918aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Wed, 24 Jul 2024 07:11:17 -0400 Subject: [PATCH 055/471] tests: Add .gitignore to gunittest testreport folder if not present (#4092) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gunittest: Add .gitignore to testreport folder if not present Co-authored-by: Edouard Choinière --- python/grass/gunittest/reporters.py | 5 +++-- python/grass/gunittest/utils.py | 7 +++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/python/grass/gunittest/reporters.py b/python/grass/gunittest/reporters.py index 21afce314e1..45b1c386b27 100644 --- a/python/grass/gunittest/reporters.py +++ b/python/grass/gunittest/reporters.py @@ -19,7 +19,7 @@ import re from collections.abc import Iterable -from .utils import ensure_dir +from .utils import add_gitignore_to_dir, ensure_dir from .checkers import text_to_keyvalue @@ -330,9 +330,10 @@ def __init__(self, reporters, forgiving=False): def start(self, results_dir): # TODO: no directory cleaning (self.clean_before)? now cleaned by caller - # TODO: perhaps only those whoe need it should do it (even multiple times) + # TODO: perhaps only those who need it should do it (even multiple times) # and there is also the delete problem ensure_dir(os.path.abspath(results_dir)) + add_gitignore_to_dir(os.path.abspath(results_dir)) for reporter in self.reporters: try: reporter.start(results_dir) diff --git a/python/grass/gunittest/utils.py b/python/grass/gunittest/utils.py index d20f575325b..082366cacaf 100644 --- a/python/grass/gunittest/utils.py +++ b/python/grass/gunittest/utils.py @@ -11,6 +11,7 @@ import errno import os +from pathlib import Path import shutil import sys @@ -21,6 +22,12 @@ def ensure_dir(directory): os.makedirs(directory) +def add_gitignore_to_dir(directory): + gitignore_path = Path(directory) / ".gitignore" + if not Path(gitignore_path).exists(): + Path(gitignore_path).write_text("*") + + def silent_rmtree(filename): """Remove the file but do nothing if file does not exist.""" try: From 06455101f616c037a1e2e913e548eb56bd2d31ef Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 25 Jul 2024 12:02:15 +0000 Subject: [PATCH 056/471] CI(deps): Update github/codeql-action action to v3.25.14 (#4102) --- .github/workflows/codeql-analysis.yml | 4 ++-- .github/workflows/python-code-quality.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 25bfafdf865..5c57c79a28b 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -56,7 +56,7 @@ jobs: if: ${{ matrix.language == 'c-cpp' }} - name: Initialize CodeQL - uses: github/codeql-action/init@2d790406f505036ef40ecba973cc774a50395aac # v3.25.13 + uses: github/codeql-action/init@5cf07d8b700b67e235fbb65cbc84f69c0cf10464 # v3.25.14 with: languages: ${{ matrix.language }} config-file: ./.github/codeql/codeql-config.yml @@ -81,6 +81,6 @@ jobs: run: .github/workflows/build_ubuntu-22.04.sh "${HOME}/install" - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@2d790406f505036ef40ecba973cc774a50395aac # v3.25.13 + uses: github/codeql-action/analyze@5cf07d8b700b67e235fbb65cbc84f69c0cf10464 # v3.25.14 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index 4c953ef15ac..7abf5e5542f 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -132,7 +132,7 @@ jobs: path: bandit.sarif - name: Upload SARIF File into Security Tab - uses: github/codeql-action/upload-sarif@2d790406f505036ef40ecba973cc774a50395aac # v3.25.13 + uses: github/codeql-action/upload-sarif@5cf07d8b700b67e235fbb65cbc84f69c0cf10464 # v3.25.14 with: sarif_file: bandit.sarif From b082d422ef215c21a0fcdaf3a0a5b38cc7c51ea1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 25 Jul 2024 17:57:27 +0000 Subject: [PATCH 057/471] CI(deps): Update ruff to v0.5.5 (#4103) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * CI(deps): Update ruff to v0.5.5 * Remove trailing whitespace in .coveragerc --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Edouard Choinière <27212526+echoix@users.noreply.github.com> --- .coveragerc | 2 +- .github/workflows/python-code-quality.yml | 2 +- .pre-commit-config.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.coveragerc b/.coveragerc index 2dfe1352b28..65e13c2234b 100644 --- a/.coveragerc +++ b/.coveragerc @@ -10,7 +10,7 @@ omit = ${INITIAL_PWD-.}/bin.*/* ${INITIAL_PWD-.}/dist.*/* **/OBJ.*/* -source = +source = . ${INITIAL_PWD-.}/ ${INITIAL_GISBASE-/usr/local/grass??}/ diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index 7abf5e5542f..3eadddcec85 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -36,7 +36,7 @@ jobs: # renovate: datasource=pypi depName=bandit BANDIT_VERSION: "1.7.9" # renovate: datasource=pypi depName=ruff - RUFF_VERSION: "0.5.4" + RUFF_VERSION: "0.5.5" runs-on: ${{ matrix.os }} permissions: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 84b3749ed1d..47d0cd36bb9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -37,7 +37,7 @@ repos: ) - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.5.4 + rev: v0.5.5 hooks: # Run the linter. - id: ruff From cd0687fb7904e507ecb28d9d250e50afe3dc89f1 Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Fri, 26 Jul 2024 04:37:22 -0400 Subject: [PATCH 058/471] d.labels: Fix buffer overflow issues (#4041) Addresses multiple warnings identified by cppcheck related to potential buffer overflow issues. Added field width specifiers to the sscanf calls to prevent buffer overflows. --- display/d.labels/do_labels.c | 56 ++++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 21 deletions(-) diff --git a/display/d.labels/do_labels.c b/display/d.labels/do_labels.c index a63fcc2e7b6..aa3c7d148ac 100644 --- a/display/d.labels/do_labels.c +++ b/display/d.labels/do_labels.c @@ -7,18 +7,22 @@ #include #include "local_proto.h" -#define NL 012 -#define TAB 011 -#define BACK 0134 -#define MTEXT 1024 - -#define TOP 0 -#define CENT 1 -#define BOT 2 -#define LEFT 0 -#define RITE 2 -#define YES 1 -#define NO 0 +#define NL 012 +#define TAB 011 +#define BACK 0134 +#define MTEXT 1024 + +#define TOP 0 +#define CENT 1 +#define BOT 2 +#define LEFT 0 +#define RITE 2 +#define YES 1 +#define NO 0 + +#define BUFFSIZE 128 +#define FONTSIZE 256 +#define WORDSIZE 50 static double east; static double north; @@ -33,9 +37,13 @@ static int highlight_width; static int opaque; static double width, rotation; static char text[MTEXT]; -static char font[256]; +static char font[FONTSIZE]; static const char *std_font; +static char buff_fmt[WORDSIZE]; +static char font_fmt[WORDSIZE]; +static char word_fmt[WORDSIZE]; + static int ymatch(char *); static int xmatch(char *); @@ -67,7 +75,12 @@ int initialize_options(void) int do_labels(FILE *infile, int do_rotation) { - char buff[128]; + char buff[BUFFSIZE]; + + snprintf(buff_fmt, sizeof(buff_fmt), "%%*s %%%ds", BUFFSIZE - 1); + snprintf(font_fmt, sizeof(font_fmt), "%%*s %%%ds", FONTSIZE - 1); + snprintf(word_fmt, sizeof(word_fmt), "%%%ds %%%ds", WORDSIZE - 1, + WORDSIZE - 1); initialize_options(); @@ -84,7 +97,7 @@ int do_labels(FILE *infile, int do_rotation) else if (!strncmp(text, "yof", 3)) sscanf(text, "%*s %d", &yoffset); else if (!strncmp(text, "col", 3)) { - sscanf(text, "%*s %s", buff); + sscanf(text, buff_fmt, buff); set_RGBA_from_str(&color, buff); } else if (!strncmp(text, "siz", 3)) @@ -94,15 +107,15 @@ int do_labels(FILE *infile, int do_rotation) else if (!strncmp(text, "wid", 3)) sscanf(text, "%*s %lf", &width); else if (!strncmp(text, "bac", 3)) { - sscanf(text, "%*s %s", buff); + sscanf(text, buff_fmt, buff); set_RGBA_from_str(&background, buff); } else if (!strncmp(text, "bor", 3)) { - sscanf(text, "%*s %s", buff); + sscanf(text, buff_fmt, buff); set_RGBA_from_str(&border, buff); } else if (!strncmp(text, "opa", 3)) { - sscanf(text, "%*s %s", buff); + sscanf(text, buff_fmt, buff); if (!strncmp(buff, "YES", 3)) opaque = YES; else @@ -115,7 +128,7 @@ int do_labels(FILE *infile, int do_rotation) } } else if (!strncmp(text, "fon", 3)) { - if (sscanf(text, "%*s %s", font) != 1 || !strcmp(font, "standard")) + if (sscanf(text, font_fmt, font) != 1 || !strcmp(font, "standard")) strcpy(font, std_font); } else if (!strncmp(text, "rot", 3)) { @@ -123,7 +136,7 @@ int do_labels(FILE *infile, int do_rotation) sscanf(text, "%*s %lf", &rotation); } else if (!strncmp(text, "hco", 3)) { - sscanf(text, "%*s %s", buff); + sscanf(text, buff_fmt, buff); set_RGBA_from_str(&highlight_color, buff); } else if (!strncmp(text, "hwi", 3)) @@ -452,7 +465,7 @@ int scan_ref(char *buf) if (buf[i] >= 'A' && buf[i] <= 'Z') buf[i] += 'a' - 'A'; xref = yref = CENT; - switch (sscanf(buf, "%s%s", word1, word2)) { + switch (sscanf(buf, word_fmt, word1, word2)) { case 2: if (!(xmatch(word2) || ymatch(word2))) return 0; @@ -461,6 +474,7 @@ int scan_ref(char *buf) if (xmatch(word1) || ymatch(word1)) return 1; FALLTHROUGH; + case EOF: default: return 0; } From e386819091cd710821acc53c106d7bce8e821e5a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 26 Jul 2024 16:43:09 +0000 Subject: [PATCH 059/471] CI(deps): Update github/codeql-action action to v3.25.15 (#4107) --- .github/workflows/codeql-analysis.yml | 4 ++-- .github/workflows/python-code-quality.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 5c57c79a28b..daf1e8762b0 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -56,7 +56,7 @@ jobs: if: ${{ matrix.language == 'c-cpp' }} - name: Initialize CodeQL - uses: github/codeql-action/init@5cf07d8b700b67e235fbb65cbc84f69c0cf10464 # v3.25.14 + uses: github/codeql-action/init@afb54ba388a7dca6ecae48f608c4ff05ff4cc77a # v3.25.15 with: languages: ${{ matrix.language }} config-file: ./.github/codeql/codeql-config.yml @@ -81,6 +81,6 @@ jobs: run: .github/workflows/build_ubuntu-22.04.sh "${HOME}/install" - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@5cf07d8b700b67e235fbb65cbc84f69c0cf10464 # v3.25.14 + uses: github/codeql-action/analyze@afb54ba388a7dca6ecae48f608c4ff05ff4cc77a # v3.25.15 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index 3eadddcec85..2d2b167ae75 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -132,7 +132,7 @@ jobs: path: bandit.sarif - name: Upload SARIF File into Security Tab - uses: github/codeql-action/upload-sarif@5cf07d8b700b67e235fbb65cbc84f69c0cf10464 # v3.25.14 + uses: github/codeql-action/upload-sarif@afb54ba388a7dca6ecae48f608c4ff05ff4cc77a # v3.25.15 with: sarif_file: bandit.sarif From 3aa7d1e1f93c8b710ed87f0f1e4e82d49c78403d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 29 Jul 2024 05:43:58 -0400 Subject: [PATCH 060/471] CI(deps): Lock file maintenance (#4109) From 84db88a9d2b7e05fdb5472b118986ece263b6615 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Bartoletti?= Date: Mon, 29 Jul 2024 19:02:33 +0200 Subject: [PATCH 061/471] lib/gis: Add portable G_strlcpy function (#4101) This commit introduces a new G_strlcpy function in lib/gis, inspired by G_asprintf. G_strlcpy provides a safer alternative to strcpy and strncpy, with consistent behavior across different systems. Key points: - Implements strlcpy functionality, available natively on BSD systems - Portable implementation for non-BSD systems (excluding Linux with libbsd) - Based on FreeBSD's implementation: https://github.com/freebsd/freebsd-src/blob/98dd639c94f716858ae29958f484729b1d2fd387/sys/libkern/strlcpy.c#L28 - Designed to replace unsafe uses of strcpy and strncpy throughout the project The function is implemented to use the native strlcpy where available, falling back to our portable version on systems without it. This ensures optimal performance on BSD systems while maintaining compatibility across different platforms. By providing G_strlcpy, we aim to improve the overall safety and consistency of string operations in our codebase. --- configure | 7 ++++ configure.ac | 5 +++ include/grass/config.h.in | 3 ++ include/grass/defs/gis.h | 3 ++ lib/gis/strlcpy.c | 76 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 94 insertions(+) create mode 100644 lib/gis/strlcpy.c diff --git a/configure b/configure index fc8d39a7a05..7d8b9b1e5e7 100755 --- a/configure +++ b/configure @@ -766,6 +766,7 @@ ICONVLIB DLLIB MATHLIB HAVE_ASPRINTF +HAVE_STRLCPY DBMIEXTRALIB USE_X11 XTLIB @@ -8311,6 +8312,12 @@ then : fi +ac_fn_c_check_func "$LINENO" "strlcpy" "ac_cv_func_strlcpy" +if test "x$ac_cv_func_strlcpy" = xyes +then : + printf "%s\n" "#define HAVE_STRLCPY 1" >>confdefs.h + +fi # Test if mathlib needs -lm flag or is included with libc diff --git a/configure.ac b/configure.ac index d7f7b578282..8ef6e1b9f7c 100644 --- a/configure.ac +++ b/configure.ac @@ -595,6 +595,11 @@ AC_SUBST(DBMIEXTRALIB) AC_CHECK_FUNCS(asprintf) AC_SUBST(HAVE_ASPRINTF) +# Test if strlcpy exists +# This is a function part of *BSD libc (optionally available on Linux via libbsd) +AC_CHECK_FUNCS(strlcpy) +AC_SUBST(HAVE_STRLCPY) + # Test if mathlib needs -lm flag or is included with libc AC_CHECK_FUNC(atan, MATHLIB=, [ AC_CHECK_LIB(m, atan, MATHLIB=-lm, [ diff --git a/include/grass/config.h.in b/include/grass/config.h.in index ca90ef601c5..f070b4ccc0d 100644 --- a/include/grass/config.h.in +++ b/include/grass/config.h.in @@ -14,6 +14,9 @@ /* Define to 1 if you have the `asprintf' function. */ #undef HAVE_ASPRINTF +/* Define to 1 if you have the `strlcpy' function. */ +#undef HAVE_STRLCPY + /* Define to 1 if you have the header file. */ #undef HAVE_BZLIB_H diff --git a/include/grass/defs/gis.h b/include/grass/defs/gis.h index 4339fad72d1..1eacd9ed933 100644 --- a/include/grass/defs/gis.h +++ b/include/grass/defs/gis.h @@ -152,6 +152,9 @@ int G_vfaprintf(FILE *, const char *, va_list); int G_vsaprintf(char *, const char *, va_list); int G_vsnaprintf(char *, size_t, const char *, va_list); +/* strlcpy.c */ +size_t G_strlcpy(char *, const char *, size_t); + /* basename.c */ char *G_basename(char *, const char *); size_t G_get_num_decimals(const char *); diff --git a/lib/gis/strlcpy.c b/lib/gis/strlcpy.c new file mode 100644 index 00000000000..5c0601391f5 --- /dev/null +++ b/lib/gis/strlcpy.c @@ -0,0 +1,76 @@ +/*! + * \file lib/gis/strlcpy.c + * + * \brief GIS Library - GRASS implementation of strlcpy(). + * + * Loïc Bartoletti - 2024-07-25 + * + * Copyright (c) 1998, 2015 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +/** + * \brief Safe string copy function. + * + * Copy string src to buffer dst of size dsize. At most dsize-1 + * characters will be copied. Always NUL terminates (unless dsize == 0). + * This function is a safer alternative to strncpy. + * + * \param[out] dst Pointer to the destination buffer. + * \param[in] src Pointer to the source string. Must be a NUL-terminated C + * string. + * \param[in] dsize The size of the destination buffer. + * + * \return The total length of the string src (not including the terminating + * NUL character). If the return value is >= dsize, truncation occurred. + * + * \note If truncation occurred, the return value is the length of the string + * that would have been created if enough space had been available. + * + * \warning This function does not pad the destination buffer with NUL bytes + * if the source string is shorter than dsize-1 bytes, unlike strncpy. + * + * \warning The src string must be a valid NUL-terminated C string. Passing an + * unterminated string may result in buffer overrun. + */ + +size_t G_strlcpy(char *restrict dst, const char *restrict src, size_t dsize) +{ +#ifdef HAVE_STRLCPY + return strlcpy(dst, src, dsize); +#else + const char *osrc = src; + size_t nleft = dsize; + + /* Copy as many bytes as will fit. */ + if (nleft != 0) { + while (--nleft != 0) { + if ((*dst++ = *src++) == '\0') + break; + } + } + + /* Not enough room in dst, add NUL and traverse rest of src. */ + if (nleft == 0) { + if (dsize != 0) + *dst = '\0'; /* NUL-terminate dst */ + while (*src++) + ; + } + + return (src - osrc - 1); /* count does not include NUL */ +#endif +} From 127ec9a1b03793777026332188782816a8d0c6c8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 29 Jul 2024 22:00:50 -0400 Subject: [PATCH 062/471] CI(deps): Update docker/setup-buildx-action action to v3.6.1 (#4110) --- .github/workflows/docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 041c4dffa2b..d3104a6ed4b 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -68,7 +68,7 @@ jobs: - name: Set up QEMU uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # v3.2.0 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@aa33708b10e362ff993539393ff100fa93ed6a27 # v3.5.0 + uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # v3.6.1 - name: Login to DockerHub uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 with: From 0983373d840b2c0fdbc3b0c70cee8d9e1caba2b1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 29 Jul 2024 22:01:01 -0400 Subject: [PATCH 063/471] CI(deps): Update msys2/setup-msys2 action to v2.24.0 (#4108) --- .github/workflows/osgeo4w.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/osgeo4w.yml b/.github/workflows/osgeo4w.yml index d96cd0c3ff8..2fd1412d76b 100644 --- a/.github/workflows/osgeo4w.yml +++ b/.github/workflows/osgeo4w.yml @@ -30,7 +30,7 @@ jobs: git config --global core.autocrlf false git config --global core.eol lf - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - uses: msys2/setup-msys2@d0e80f58dffbc64f6a3a1f43527d469b4fc7b6c8 # v2.23.0 + - uses: msys2/setup-msys2@5df0ca6cbf14efcd08f8d5bd5e049a3cc8e07fd2 # v2.24.0 with: path-type: inherit location: D:\ From e56c7cddd7a23b612cd491592c4ac355ca1bac61 Mon Sep 17 00:00:00 2001 From: Nicklas Larsson Date: Tue, 30 Jul 2024 20:26:40 +0200 Subject: [PATCH 064/471] gui: fix broken if-else statement in EnableLongHelp() (#4106) Fixes a regression introduced by https://github.com/OSGeo/grass/commit/f99780c11f8d4d1633e469fb32da2b6c75852b4f Closes #4091 --- gui/wxpython/gui_core/toolbars.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/gui/wxpython/gui_core/toolbars.py b/gui/wxpython/gui_core/toolbars.py index 47c00fd36fc..d47a850a6a5 100644 --- a/gui/wxpython/gui_core/toolbars.py +++ b/gui/wxpython/gui_core/toolbars.py @@ -174,9 +174,10 @@ def EnableLongHelp(self, enable): if tool[0][0] == "": # separator continue internal_label = tool[0][0] - elif tool[0] == "": # separator - continue - internal_label = tool[0] + else: + if tool[0] == "": # separator + continue + internal_label = tool[0] label = vars(self.widget)[internal_label] if enable: From 75a4c53242f5cfbc138acde0cece37f638b2b0d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Tue, 30 Jul 2024 17:15:04 -0400 Subject: [PATCH 065/471] CI(ruff): Show ruff annotations even for fixes (#4111) * CI(ruff): Show ruff annotations even for fixes * CI(ruff): Use default output format when fixing --- .github/workflows/python-code-quality.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index 2d2b167ae75..ea8eb9eeac8 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -66,8 +66,11 @@ jobs: - name: Install Ruff run: pip install ruff==${{ env.RUFF_VERSION }} - - name: Run Ruff - run: ruff check --output-format=github . --preview --fix --unsafe-fixes + - name: Run Ruff (output annotations on fixable errors) + run: ruff check --output-format=github . --preview --unsafe-fixes + continue-on-error: true + - name: Run Ruff (apply fixes for suggestions) + run: ruff check . --preview --fix --unsafe-fixes - name: Create and uploads code suggestions to apply for Ruff # Will fail fast here if there are changes required id: diff-ruff From 50c8ea41c9ede0c3e825047a5e8869f14b2317a7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 31 Jul 2024 13:44:31 +0000 Subject: [PATCH 066/471] CI(deps): Update super-linter/super-linter action to v6.8.0 (#4119) --- .github/workflows/super-linter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/super-linter.yml b/.github/workflows/super-linter.yml index 95a9d8329c0..3ed9efc5125 100644 --- a/.github/workflows/super-linter.yml +++ b/.github/workflows/super-linter.yml @@ -31,7 +31,7 @@ jobs: # list of files that changed across commits fetch-depth: 0 - name: Lint code base - uses: super-linter/super-linter/slim@3fe03abab2eafb293ace16d4a3b07aeabcb3f1a0 # v6.7.0 + uses: super-linter/super-linter/slim@b4515bd4ad9d0aa4681960e053916ab991bdbe96 # v6.8.0 env: DEFAULT_BRANCH: main # To report GitHub Actions status checks From 78490a200aa77d6a109242f6b3e1a9c8bb5f56f6 Mon Sep 17 00:00:00 2001 From: Nicklas Larsson Date: Wed, 31 Jul 2024 15:53:57 +0200 Subject: [PATCH 067/471] lib/vector: fix always true if-condition in Vect_point_buffer2() (#4115) The true if-statement only handles buffers with rounded corners. E.g. using `v.buffer` without `-s` on points. GEOS handles buffers with corners. --- lib/vector/Vlib/buffer2.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/vector/Vlib/buffer2.c b/lib/vector/Vlib/buffer2.c index f380f81af78..102b1ca350b 100644 --- a/lib/vector/Vlib/buffer2.c +++ b/lib/vector/Vlib/buffer2.c @@ -1135,6 +1135,8 @@ void Vect_area_buffer2(struct Map_info *Map, int area, double da, double db, \param round make corners round \param tol maximum distance between theoretical arc and output segments \param[out] oPoints output polygon outer border (ccw order) + + \note Currently only handles buffers with rounded corners (round = 1) */ void Vect_point_buffer2(double px, double py, double da, double db, double dalpha, int round, double tol, @@ -1144,13 +1146,13 @@ void Vect_point_buffer2(double px, double py, double da, double db, double angular_tol, angular_step, phi1; int j, nsegments; - G_debug(2, "Vect_point_buffer()"); + G_debug(2, "%s()", __func__); *oPoints = Vect_new_line_struct(); dalpha *= PI / 180; /* convert dalpha from degrees to radians */ - if (round || (!round)) { + if (round) { angular_tol = angular_tolerance(tol, da, db); nsegments = (int)(2 * PI / angular_tol) + 1; From afc607996350d6a74b84104d8f51aa98690bc028 Mon Sep 17 00:00:00 2001 From: Riya Saxena <77328768+29riyasaxena@users.noreply.github.com> Date: Wed, 31 Jul 2024 22:20:48 +0530 Subject: [PATCH 068/471] grass.jupyter: Allow Users to view/update computational region (#3838) This pull request introduces a new feature to grass.jupyter.interactivemap.py: a "View/Update Computational Region" button that allows users to update the computational region by adjusting its boundaries interactively. Users can move the rectangle representing the current computation region and adjust its size by changing its vertex. --------- Co-authored-by: Anna Petrasova --- python/grass/jupyter/interactivemap.py | 116 +++++++++++++++++- .../jupyter/testsuite/interactivemap_test.py | 18 +++ python/grass/jupyter/utils.py | 27 ++++ 3 files changed, 160 insertions(+), 1 deletion(-) diff --git a/python/grass/jupyter/interactivemap.py b/python/grass/jupyter/interactivemap.py index 082122d24d0..561b2cdfbb2 100644 --- a/python/grass/jupyter/interactivemap.py +++ b/python/grass/jupyter/interactivemap.py @@ -17,6 +17,12 @@ import json from pathlib import Path from .reprojection_renderer import ReprojectionRenderer +from .utils import ( + get_region_bounds_latlon, + reproject_region, + update_region, + get_location_proj_string, +) def get_backend(interactive_map): @@ -300,6 +306,7 @@ def _import_ipyleaflet(error): ) # Set LayerControl default self.layer_control_object = None + self.region_rectangle = None self._renderer = ReprojectionRenderer( use_region=use_region, saved_region=saved_region @@ -346,13 +353,120 @@ def add_layer_control(self, **kwargs): else: self.layer_control_object = self._ipyleaflet.LayersControl(**kwargs) + def draw_computational_region(self): + """ + Allow users to draw the computational region and modify it. + """ + import ipywidgets as widgets # pylint: disable=import-outside-toplevel + + region_mode_button = widgets.ToggleButton( + icon="square-o", + description="", + value=False, + tooltip="Click to show and edit computational region", + layout=widgets.Layout(width="40px", margin="0px 0px 0px 0px"), + ) + + save_button = widgets.Button( + description="Update region", + tooltip="Click to update region", + disabled=True, + ) + bottom_output_widget = widgets.Output( + layout={ + "width": "100%", + "max_height": "300px", + "overflow": "auto", + "display": "none", + } + ) + + changed_region = {} + save_button_control = None + + def update_output(region): + with bottom_output_widget: + bottom_output_widget.clear_output() + print( + _( + "Region changed to: n={n}, s={s}, e={e}, w={w} " + "nsres={nsres} ewres={ewres}" + ).format(**region) + ) + + def on_rectangle_change(value): + save_button.disabled = False + bottom_output_widget.layout.display = "none" + latlon_bounds = value["new"][0] + changed_region["north"] = latlon_bounds[2]["lat"] + changed_region["south"] = latlon_bounds[0]["lat"] + changed_region["east"] = latlon_bounds[2]["lng"] + changed_region["west"] = latlon_bounds[0]["lng"] + + def toggle_region_mode(change): + nonlocal save_button_control + + if change["new"]: + region_bounds = get_region_bounds_latlon() + self.region_rectangle = self._ipyleaflet.Rectangle( + bounds=region_bounds, + color="red", + fill_color="red", + fill_opacity=0.5, + draggable=True, + transform=True, + rotation=False, + name="Computational region", + ) + self.region_rectangle.observe(on_rectangle_change, names="locations") + self.map.fit_bounds(region_bounds) + self.map.add(self.region_rectangle) + + save_button_control = self._ipyleaflet.WidgetControl( + widget=save_button, position="topright" + ) + self.map.add(save_button_control) + else: + if self.region_rectangle: + self.region_rectangle.transform = False + self.map.remove(self.region_rectangle) + self.region_rectangle = None + + save_button.disabled = True + + if save_button_control: + self.map.remove(save_button_control) + bottom_output_widget.layout.display = "none" + + def save_region(_change): + from_proj = "+proj=longlat +datum=WGS84 +no_defs" + to_proj = get_location_proj_string() + reprojected_region = reproject_region(changed_region, from_proj, to_proj) + new = update_region(reprojected_region) + bottom_output_widget.layout.display = "block" + update_output(new) + + region_mode_button.observe(toggle_region_mode, names="value") + save_button.on_click(save_region) + + region_mode_control = self._ipyleaflet.WidgetControl( + widget=region_mode_button, position="topright" + ) + self.map.add(region_mode_control) + + output_control = self._ipyleaflet.WidgetControl( + widget=bottom_output_widget, position="bottomleft" + ) + self.map.add(output_control) + def show(self): """This function returns a folium figure or ipyleaflet map object with a GRASS raster and/or vector overlaid on a basemap. If map has layer control enabled, additional layers cannot be added after calling show().""" - + if self._ipyleaflet: + self.draw_computational_region() self.map.fit_bounds(self._renderer.get_bbox()) if not self.layer_control_object: diff --git a/python/grass/jupyter/testsuite/interactivemap_test.py b/python/grass/jupyter/testsuite/interactivemap_test.py index 665ccc72136..80836cdf31e 100644 --- a/python/grass/jupyter/testsuite/interactivemap_test.py +++ b/python/grass/jupyter/testsuite/interactivemap_test.py @@ -37,6 +37,16 @@ def can_import_folium(): return False +def can_import_ipyleaflet(): + """Test ipyleaflet import to see if test can be run.""" + try: + import ipyleaflet # noqa + + return True + except ImportError: + return False + + class TestDisplay(TestCase): # Setup variables files = [] @@ -88,6 +98,14 @@ def test_save_as_html(self): interactive_map.save(filename) self.assertFileExists(filename) + @unittest.skipIf(not can_import_ipyleaflet(), "Cannot import ipyleaflet") + def test_draw_computational_region(self): + """Test the draw_computational_region method.""" + # Create InteractiveMap + interactive_map = gj.InteractiveMap() + interactive_map.draw_computational_region() + self.assertTrue(callable(interactive_map.draw_computational_region)) + if __name__ == "__main__": test() diff --git a/python/grass/jupyter/utils.py b/python/grass/jupyter/utils.py index 4d76b166361..0556158896f 100644 --- a/python/grass/jupyter/utils.py +++ b/python/grass/jupyter/utils.py @@ -201,6 +201,33 @@ def get_rendering_size(region, width, height, default_width=600, default_height= return (default_width, round(default_width * region_height / region_width)) +def get_region_bounds_latlon(): + """Gets the current computational region bounds in latlon.""" + region = gs.parse_command("g.region", flags="gbp") + return [ + (float(region["ll_s"]), float(region["ll_w"])), + (float(region["ll_n"]), float(region["ll_e"])), + ] + + +def update_region(region): + """Updates the computational region bounds. + + :return: the new region + """ + current = gs.region() + return gs.parse_command( + "g.region", + flags="ga", + n=region["north"], + s=region["south"], + e=region["east"], + w=region["west"], + nsres=current["nsres"], + ewres=current["ewres"], + ) + + def save_gif( input_files, output_filename, From 13091fbc2591534621d7088544c4ec11934c0085 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20de=20Sousa?= Date: Wed, 31 Jul 2024 19:58:13 +0100 Subject: [PATCH 069/471] v.out.ogr: Add basic unit tests (#3848) Add basic tests for GeoPackage and Shapefile export which use import to test the result (so they test round trip but focus on the export). --- vector/v.out.ogr/testsuite/test_v_out_ogr.py | 115 +++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 vector/v.out.ogr/testsuite/test_v_out_ogr.py diff --git a/vector/v.out.ogr/testsuite/test_v_out_ogr.py b/vector/v.out.ogr/testsuite/test_v_out_ogr.py new file mode 100644 index 00000000000..d269ccff606 --- /dev/null +++ b/vector/v.out.ogr/testsuite/test_v_out_ogr.py @@ -0,0 +1,115 @@ +"""Test of v.out.ogr + +@author Luís Moreira de Sousa +""" + +from pathlib import Path +from grass.gunittest.case import TestCase + + +class TestOgrExport(TestCase): + + # Vector map in NC test dataset + test_map = "boundary_county" + + # Result of import tests + temp_import = "test_ogr_import_map" + + # Column on which to test v.univar + univar_col = "PERIMETER" + + # Output of v.univar + univar_string = """n=926 +nmissing=0 +nnull=0 +min=9.64452 +max=3.70609e+06 +range=3.70608e+06 +sum=1.1223e+08 +mean=121199 +mean_abs=121199 +population_stddev=342855 +population_variance=1.17549e+11 +population_coeff_variation=2.82886 +sample_stddev=343040 +sample_variance=1.17676e+11 +kurtosis=33.681 +skewness=4.86561 +""" + + @classmethod + def setUpClass(cls): + """Use temporary region settings""" + cls.use_temp_region() + + @classmethod + def tearDownClass(cls): + """Remove the temporary region""" + cls.del_temp_region() + + def tearDown(self): + self.runModule( + "g.remove", type="vector", flags="f", pattern=f"{self.temp_import}*" + ) + for p in Path().glob(f"{self.test_map}*"): + p.unlink() + + def test_gpkg_format(self): + """Tests output to GeoPackage format""" + + self.assertModule( + "v.out.ogr", + "Export to GeoPackage Format", + input=self.test_map, + output=f"{self.test_map}.gpkg", + format="GPKG", + ) + + # Import back to verify + self.runModule( + "v.in.ogr", + input=f"{self.test_map}.gpkg", + output=self.temp_import, + ) + + self.runModule("g.region", vector=self.temp_import) + + self.assertVectorFitsUnivar( + map=self.temp_import, + reference=self.univar_string, + column=self.univar_col, + precision=1e-8, + ) + + def test_shp_format(self): + """Tests output to Shapefile format""" + + self.assertModule( + "v.out.ogr", + "Export to Shapefile Format", + input=self.test_map, + output=f"{self.test_map}.shp", + format="ESRI_Shapefile", + ) + + # Import back to verify + self.runModule( + "v.in.ogr", + input=f"{self.test_map}.shp", + output=self.temp_import, + ) + + self.runModule("g.region", vector=self.temp_import) + + self.assertVectorFitsUnivar( + map=self.temp_import, + reference=self.univar_string, + column=self.univar_col, + precision=1e-8, + ) + + +if __name__ == "__main__": + from grass.gunittest.main import test + + test() From c88fc0f73cb1d3f9266b5469831a1d6822b3130e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Wed, 31 Jul 2024 22:03:58 -0400 Subject: [PATCH 070/471] grass.pydispatch: Apply code changes from upstream pydispatcher repo (#4033) python(pydispatch): Apply code changes from upstream pydispatcher repo Manual combination of latest changes of repo https://github.com/mcfletch/pydispatcher --- python/grass/pydispatch/PKG-INFO | 2 +- python/grass/pydispatch/__init__.py | 11 +++---- python/grass/pydispatch/dispatcher.py | 8 ++--- python/grass/pydispatch/robustapply.py | 45 +++++++++++++++++++++----- python/grass/pydispatch/saferef.py | 15 ++++++--- 5 files changed, 55 insertions(+), 26 deletions(-) diff --git a/python/grass/pydispatch/PKG-INFO b/python/grass/pydispatch/PKG-INFO index 19752eec0fe..2ad96bdff66 100644 --- a/python/grass/pydispatch/PKG-INFO +++ b/python/grass/pydispatch/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 1.0 Name: PyDispatcher -Version: 2.0.3 +Version: 2.0.8 Summary: Multi-producer-multi-consumer signal dispatching mechanism Home-page: http://pydispatcher.sourceforge.net Author: Mike C. Fletcher diff --git a/python/grass/pydispatch/__init__.py b/python/grass/pydispatch/__init__.py index 30f1af7fd21..beee0d6101e 100644 --- a/python/grass/pydispatch/__init__.py +++ b/python/grass/pydispatch/__init__.py @@ -16,10 +16,9 @@ PyDispatcher because it provides very general API which enables to implement Signal API, wide and robust functionality which makes implementation and use of Signals easier. - -PyDispatcher metadata: - -:version: 2.0.3 -:author: Patrick K. O'Brien -:license: BSD-style, see license.txt for details """ + +__version__ = "2.0.8" +__author__ = "Patrick K. O'Brien" +__maintainer__ = "Mike C. Fletcher" +__license__ = "BSD" diff --git a/python/grass/pydispatch/dispatcher.py b/python/grass/pydispatch/dispatcher.py index 0915629fdd9..4757acf36cf 100644 --- a/python/grass/pydispatch/dispatcher.py +++ b/python/grass/pydispatch/dispatcher.py @@ -27,11 +27,7 @@ """ import weakref -from grass.pydispatch import saferef, robustapply, errors - -__author__ = "Patrick K. O'Brien " -__cvsid__ = "Id: dispatcher.py,v 1.1 2010/03/30 15:45:55 mcfletch Exp" -__version__ = "Revision: 1.1" +from grass.pydispatch import errors, saferef, robustapply class _Parameter: @@ -469,7 +465,7 @@ def _removeOldBackRefs(senderkey, signal, receiver, receivers): found = 0 signals = connections.get(signal) if signals is not None: - for sig, recs in connections.get(signal, {}).iteritems(): + for sig, recs in connections.get(signal, {}).items(): if sig != signal: for rec in recs: if rec is oldReceiver: diff --git a/python/grass/pydispatch/robustapply.py b/python/grass/pydispatch/robustapply.py index 1c10bc917c6..a050f54341a 100644 --- a/python/grass/pydispatch/robustapply.py +++ b/python/grass/pydispatch/robustapply.py @@ -40,22 +40,51 @@ def function(receiver): return receiver, getattr(receiver, func_code), 0 +VAR_ARGS = 4 +VAR_NAMES = 8 + + def robustApply(receiver, *arguments, **named): - """Call receiver with arguments and an appropriate subset of named""" + """Call receiver with arguments and an appropriate subset of named + + The effect of this wrapper is to allow for specifying a large number + of parameters which may not exist in the final function via named + parameters, and have those parameters ignored in the final call. + """ receiver, codeObject, startIndex = function(receiver) - acceptable = codeObject.co_varnames[ - startIndex + len(arguments) : codeObject.co_argcount + has_varargs = bool(codeObject.co_flags & VAR_ARGS) + has_varnames = bool(codeObject.co_flags & VAR_NAMES) + + posonly_count = getattr(codeObject, "co_posonlyargcount", 0) + + posnamed_arguments = codeObject.co_varnames[posonly_count : codeObject.co_argcount] + named_onlyarguments = codeObject.co_varnames[ + codeObject.co_argcount : len(codeObject.co_varnames) + + (-1 if has_varnames else 0) + + (-1 if has_varargs else 0) ] - for name in codeObject.co_varnames[startIndex : startIndex + len(arguments)]: + + # Implements: You can't have a parameter in both args and keywords, + # reporting an easily debugged message + # Implements: You can't have a posonly arg in named (as a side effect of the above) + for name in codeObject.co_varnames[ + 0 : min((len(arguments), codeObject.co_argcount)) + ]: if name in named: raise TypeError( """Argument %r specified both positionally and as a keyword""" """ for calling %r""" % (name, receiver) ) - if not (codeObject.co_flags & 8): + # Implements: You can only passed keyword parameters if the parameter exists and is + # not a positional-only parameter or a varargs or varkeywords parameter. + # Note that this silently drops TypeErrors for passing the name of the varargs or + # varkeyword variables because it only allows through the valid arg-names for + # the function + if not has_varnames: + acceptable = ( + posnamed_arguments[len(arguments) - posonly_count :] + named_onlyarguments + ) # fc does not have a **kwds type parameter, therefore # remove unacceptable arguments. - for arg in list(named): - if arg not in acceptable: - del named[arg] + named = dict([(k, v) for k, v in named.items() if k in acceptable]) return receiver(*arguments, **named) diff --git a/python/grass/pydispatch/saferef.py b/python/grass/pydispatch/saferef.py index 43be1175b19..bf67240d6dd 100644 --- a/python/grass/pydispatch/saferef.py +++ b/python/grass/pydispatch/saferef.py @@ -4,8 +4,12 @@ import traceback import sys -im_func = "__func__" -im_self = "__self__" +if sys.hexversion >= 0x3000000: + im_func = "__func__" + im_self = "__self__" +else: + im_func = "im_func" + im_self = "im_self" def safeRef(target, onDelete=None): @@ -126,9 +130,8 @@ def remove(weak, self=self): traceback.print_exc() except AttributeError: print( - """Exception during saferef %s cleanup """ - """function %s: %s""" % (self, function, e), - file=sys.stderr, + """Exception during saferef %s cleanup function %s: %s""" + % (self, function, e) ) self.deletionMethods = [onDelete] @@ -162,6 +165,8 @@ def __nonzero__(self): """Whether we are still a valid reference""" return self() is not None + __bool__ = __nonzero__ + def __cmp__(self, other): """Compare with another reference""" if not isinstance(other, self.__class__): From 17474a9194498560606df0552f473bf586311fce Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Fri, 2 Aug 2024 04:00:01 -0400 Subject: [PATCH 071/471] r3.in.v5d: Remove unused variables (#4114) --- raster3d/r3.in.v5d/v5d.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/raster3d/r3.in.v5d/v5d.c b/raster3d/r3.in.v5d/v5d.c index 810238981bd..445d6f29b9f 100644 --- a/raster3d/r3.in.v5d/v5d.c +++ b/raster3d/r3.in.v5d/v5d.c @@ -467,7 +467,7 @@ static void compute_ga_gb(int nr, int nc, int nl, const float data[], /* * Compute ga, gb values for whole grid. */ - int i, lev, allmissing, num; + int i, allmissing, num; float min, max, a, b; min = 1.0e30; @@ -549,7 +549,6 @@ static void compute_ga_gb(int nr, int nc, int nl, const float data[], delt = (gridmax - gridmin) / 100000.0; if (ABS(gridmin) < delt && gridmin != 0.0 && compressmode != 4) { - float min, max; for (j = 0; j < nrncnl; j++) { if (!IS_MISSING(data[j]) && data[j] < delt) From e671a4322778af043684130680c553bbe98d14cc Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Fri, 2 Aug 2024 04:59:01 -0400 Subject: [PATCH 072/471] i.ortho.photo: Fix uninitialized variable and potential buffer overflow (#4093) --- imagery/i.ortho.photo/i.ortho.photo/menu.c | 27 +++++++++++++--------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/imagery/i.ortho.photo/i.ortho.photo/menu.c b/imagery/i.ortho.photo/i.ortho.photo/menu.c index 76bfe6a3f04..40313c6c891 100644 --- a/imagery/i.ortho.photo/i.ortho.photo/menu.c +++ b/imagery/i.ortho.photo/i.ortho.photo/menu.c @@ -24,6 +24,8 @@ #include #include "orthophoto.h" +#define BUF_SIZE 99 + int main(int argc, char **argv) { char *p; @@ -33,7 +35,8 @@ int main(int argc, char **argv) char *desc_ortho_opt; char *moduletorun; const char *grname; - char tosystem[99]; + char tosystem[BUF_SIZE] = ""; + size_t len; /* initialize grass */ G_gisinit(argv[0]); @@ -82,8 +85,10 @@ int main(int argc, char **argv) /* group validity check */ /*----------------------*/ - strncpy(group.name, group_opt->answer, 99); - group.name[99] = '\0'; + len = G_strlcpy(group.name, group_opt->answer, BUF_SIZE); + if (len >= BUF_SIZE) { + G_fatal_error(_("Name <%s> is too long"), group_opt->answer); + } /* strip off mapset if it's there: I_() fns only work with current mapset */ if ((p = strchr(group.name, '@'))) *p = 0; @@ -96,26 +101,26 @@ int main(int argc, char **argv) moduletorun = ortho_opt->answer; /* run the program chosen */ if (strcmp(moduletorun, "g.gui.photo2image") == 0) { - strcpy(tosystem, "g.gui.photo2image"); + (void)G_strlcpy(tosystem, "g.gui.photo2image", BUF_SIZE); return system((const char *)tosystem); } else if (strcmp(moduletorun, "g.gui.image2target") == 0) { - strcpy(tosystem, "g.gui.image2target"); + (void)G_strlcpy(tosystem, "g.gui.image2target", BUF_SIZE); return system((const char *)tosystem); } else { if (strcmp(moduletorun, "i.group") == 0) - strcpy(tosystem, "i.group --ui group="); + (void)G_strlcpy(tosystem, "i.group --ui group=", BUF_SIZE); if (strcmp(moduletorun, "i.ortho.target") == 0) - strcpy(tosystem, "i.ortho.target --ui group="); + (void)G_strlcpy(tosystem, "i.ortho.target --ui group=", BUF_SIZE); if (strcmp(moduletorun, "i.ortho.elev") == 0) - strcpy(tosystem, "i.ortho.elev --ui group="); + (void)G_strlcpy(tosystem, "i.ortho.elev --ui group=", BUF_SIZE); if (strcmp(moduletorun, "i.ortho.camera") == 0) - strcpy(tosystem, "i.ortho.camera --ui group="); + (void)G_strlcpy(tosystem, "i.ortho.camera --ui group=", BUF_SIZE); if (strcmp(moduletorun, "i.ortho.init") == 0) - strcpy(tosystem, "i.ortho.init --ui group="); + (void)G_strlcpy(tosystem, "i.ortho.init --ui group=", BUF_SIZE); if (strcmp(moduletorun, "i.ortho.rectify") == 0) - strcpy(tosystem, "i.ortho.rectify --ui group="); + (void)G_strlcpy(tosystem, "i.ortho.rectify --ui group=", BUF_SIZE); strcat(tosystem, grname); return system((const char *)tosystem); } From 6dbd5d1ef8427d795a7c80a41d8a066315d6476a Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Fri, 2 Aug 2024 05:03:00 -0400 Subject: [PATCH 073/471] r.out.mpeg: Fix buffer overflow issues by replacing sprintf with snprintf (#4098) --- raster/r.out.mpeg/main.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/raster/r.out.mpeg/main.c b/raster/r.out.mpeg/main.c index 0ac3b631b80..e92675f48d5 100644 --- a/raster/r.out.mpeg/main.c +++ b/raster/r.out.mpeg/main.c @@ -395,9 +395,9 @@ static void mlist(const char *element, const char *wildarg, const char *outfile) if (strcmp(mapset, ".") == 0) mapset = G_mapset(); - sprintf(type_arg, "type=%s", element); - sprintf(pattern_arg, "pattern=%s", wildarg); - sprintf(mapset_arg, "mapset=%s", mapset); + snprintf(type_arg, sizeof(type_arg), "type=%s", element); + snprintf(pattern_arg, sizeof(pattern_arg), "pattern=%s", wildarg); + snprintf(mapset_arg, sizeof(mapset_arg), "mapset=%s", mapset); G_spawn_ex("g.list", "g.list", type_arg, pattern_arg, mapset_arg, SF_REDIRECT_FILE, SF_STDOUT, SF_MODE_APPEND, outfile, NULL); From 9cebe70d7893d987c8a5aa3f05099d0688800422 Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Fri, 2 Aug 2024 05:27:10 -0400 Subject: [PATCH 074/471] r.path: Fix buffer overflow check issue (#4087) --- raster/r.path/main.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/raster/r.path/main.c b/raster/r.path/main.c index e58995695de..5edcdfa1d95 100644 --- a/raster/r.path/main.c +++ b/raster/r.path/main.c @@ -139,6 +139,7 @@ int main(int argc, char **argv) struct line_cats *Cats; struct Map_info vout, *pvout; char *desc = NULL; + size_t len; G_gisinit(argv[0]); @@ -219,13 +220,23 @@ int main(int argc, char **argv) if (G_parser(argc, argv)) exit(EXIT_FAILURE); - strcpy(dir_name, opt.dir->answer); + len = G_strlcpy(dir_name, opt.dir->answer, sizeof(dir_name)); + if (len >= sizeof(dir_name)) { + G_fatal_error(_("Name <%s> is too long"), opt.dir->answer); + } *map_name = '\0'; *out_name = '\0'; if (opt.rast->answer) { - strcpy(out_name, opt.rast->answer); - if (opt.val->answer) - strcpy(map_name, opt.val->answer); + len = G_strlcpy(out_name, opt.rast->answer, sizeof(out_name)); + if (len >= sizeof(out_name)) { + G_fatal_error(_("Name <%s> is too long"), opt.rast->answer); + } + } + if (opt.rast->answer && opt.val->answer) { + len = G_strlcpy(map_name, opt.val->answer, sizeof(map_name)); + if (len >= sizeof(map_name)) { + G_fatal_error(_("Name <%s> is too long"), opt.val->answer); + } } pvout = NULL; From 85498f4189602a3ba292d7573a774760157c76d2 Mon Sep 17 00:00:00 2001 From: Nicklas Larsson Date: Fri, 2 Aug 2024 16:51:59 +0200 Subject: [PATCH 075/471] lib/vector: new functions for setting trim of GEOS WKT output (#4123) GEOS 3.12 switched the default setting of the trim option to true. To enable consistent behaviour to Vect_read_area_to_wkt() and Vect_line_to_wkt() across GEOS versions, they now call new 2nd generation functions, which set this trim option explicitly to false. --- include/grass/defs/vector.h | 4 ++- lib/vector/Vlib/geos_to_wktb.c | 57 +++++++++++++++++++++++++++------- 2 files changed, 49 insertions(+), 12 deletions(-) diff --git a/include/grass/defs/vector.h b/include/grass/defs/vector.h index a5e1618a8cb..42ed74c532b 100644 --- a/include/grass/defs/vector.h +++ b/include/grass/defs/vector.h @@ -616,9 +616,11 @@ GEOSGeometry *Vect_line_to_geos(const struct line_pnts *, int, int); GEOSGeometry *Vect_read_area_geos(struct Map_info *, int); GEOSCoordSequence *Vect_get_area_points_geos(struct Map_info *, int); GEOSCoordSequence *Vect_get_isle_points_geos(struct Map_info *, int); -char *Vect_line_to_wkt(const struct line_pnts *, int, int); +char *Vect_line_to_wkt(const struct line_pnts *, int, bool); +char *Vect_line_to_wkt2(const struct line_pnts *, int, bool, bool); unsigned char *Vect_line_to_wkb(const struct line_pnts *, int, int, size_t *); char *Vect_read_area_to_wkt(struct Map_info *, int); +char *Vect_read_area_to_wkt2(struct Map_info *, int, bool); unsigned char *Vect_read_area_to_wkb(struct Map_info *, int, size_t *); unsigned char *Vect_read_line_to_wkb(struct Map_info *, struct line_pnts *, struct line_cats *, int, size_t *, int *); diff --git a/lib/vector/Vlib/geos_to_wktb.c b/lib/vector/Vlib/geos_to_wktb.c index 5d549eb4a11..ce84dde7f0c 100644 --- a/lib/vector/Vlib/geos_to_wktb.c +++ b/lib/vector/Vlib/geos_to_wktb.c @@ -13,6 +13,7 @@ \author Soeren Gebbert */ +#include #include #include #include @@ -62,16 +63,30 @@ unsigned char *Vect_read_area_to_wkb(struct Map_info *Map, int area, /*! \brief Read vector area and return it as Well Known Text (WKT) - unsigned char array + unsigned char array + + Calls Vect_read_area_to_wkt2() with trim set to false. + + */ +char *Vect_read_area_to_wkt(struct Map_info *Map, int area) +{ + return Vect_read_area_to_wkt2(Map, area, false); +} + +/*! + \brief Read vector area and return it as Well Known Text (WKT) + unsigned char array \param Map pointer to Map_info structure \param area area id \param size The size of the returned unsigned char array + \param trim Set the number trimming option on, With trim set to true, the + writer will strip trailing 0's from the output coordinates. - \return pointer to char array + \return pointer to string (allocated) \return NULL on error */ -char *Vect_read_area_to_wkt(struct Map_info *Map, int area) +char *Vect_read_area_to_wkt2(struct Map_info *Map, int area, bool trim) { static int init = 0; @@ -86,18 +101,21 @@ char *Vect_read_area_to_wkt(struct Map_info *Map, int area) } GEOSWKTWriter_setOutputDimension(writer, 2); + GEOSWKTWriter_setTrim(writer, trim); GEOSGeometry *geom = Vect_read_area_geos(Map, area); if (!geom) { - return (NULL); + return NULL; } wkt = GEOSWKTWriter_write(writer, geom); + char *wkt_out = G_store(wkt); GEOSGeom_destroy(geom); + GEOSFree(wkt); - return (wkt); + return wkt_out; } /*! @@ -199,7 +217,7 @@ unsigned char *Vect_read_line_to_wkb(struct Map_info *Map, \param with_z Set to 1 if the feature is 3d, 0 otherwise \param size The size of the returned byte array - \return pointer to char array + \return pointer to string (allocated) \return NULL on error */ unsigned char *Vect_line_to_wkb(const struct line_pnts *points, int type, @@ -234,7 +252,18 @@ unsigned char *Vect_line_to_wkb(const struct line_pnts *points, int type, /*! \brief Create a Well Known Text (WKT) representation of - given feature type from points. + given feature type from points. + + Calls Vect_line_to_wkt2() with trim set to false. + */ +char *Vect_line_to_wkt(const struct line_pnts *points, int type, bool with_z) +{ + return Vect_line_to_wkt2(points, type, with_z, false); +} + +/*! + \brief Create a Well Known Text (WKT) representation of + given feature type from points. This function is not thread safe, it uses static variables for speedup. @@ -246,12 +275,15 @@ unsigned char *Vect_line_to_wkb(const struct line_pnts *points, int type, \param points pointer to line_pnts structure \param type feature type (see supported types) - \param with_z Set to 1 if the feature is 3d, 0 otherwise + \param with_z Set to true if the feature is 3d, false otherwise + \param trim Set the number trimming option on, With trim set to true, the + writer will strip trailing 0's from the output coordinates. \return pointer to char array \return NULL on error */ -char *Vect_line_to_wkt(const struct line_pnts *points, int type, int with_z) +char *Vect_line_to_wkt2(const struct line_pnts *points, int type, bool with_z, + bool trim) { static int init = 0; @@ -266,18 +298,21 @@ char *Vect_line_to_wkt(const struct line_pnts *points, int type, int with_z) } GEOSWKTWriter_setOutputDimension(writer, with_z ? 3 : 2); + GEOSWKTWriter_setTrim(writer, trim); GEOSGeometry *geom = Vect_line_to_geos(points, type, with_z); if (!geom) { - return (NULL); + return NULL; } wkt = GEOSWKTWriter_write(writer, geom); + char *wkt_out = G_store(wkt); GEOSGeom_destroy(geom); + GEOSFree(wkt); - return (wkt); + return wkt_out; } #endif /* HAVE_GEOS */ From 0c8b6deff363fac2b79cd7289f8ef9879952f744 Mon Sep 17 00:00:00 2001 From: Ivan Mincik Date: Fri, 2 Aug 2024 16:18:35 +0000 Subject: [PATCH 076/471] lib/init: add GRASS_CONFIG_DIR environment variable (#3899) - update grass.py to use grass.app.runtime.get_grass_config_dir() - update wxpython's core.utils.GetSettingsPath() to use grass.app.runtime.get_grass_config_dir() - update g.extension - update G_config_path() [libgis] --------- Co-authored-by: Martin Landa Co-authored-by: Nicklas Larsson --- gui/wxpython/core/utils.py | 20 +++-------- lib/gis/home.c | 9 +++-- lib/init/grass.py | 53 +++++++++++------------------- lib/init/variables.html | 7 ++++ python/grass/app/runtime.py | 29 +++++++++++++--- scripts/g.extension/g.extension.py | 10 +++--- 6 files changed, 68 insertions(+), 60 deletions(-) diff --git a/gui/wxpython/core/utils.py b/gui/wxpython/core/utils.py index 68242f04984..fe4f40c359a 100644 --- a/gui/wxpython/core/utils.py +++ b/gui/wxpython/core/utils.py @@ -3,7 +3,7 @@ @brief Misc utilities for wxGUI -(C) 2007-2015 by the GRASS Development Team +(C) 2007-2024 by the GRASS Development Team This program is free software under the GNU General Public License (>=v2). Read the file COPYING that comes with GRASS for details. @@ -23,10 +23,11 @@ from grass.script import core as grass from grass.script import task as gtask +from grass.app.runtime import get_grass_config_dir from core.gcmd import RunCommand from core.debug import Debug -from core.globalvar import ETCDIR, wxPythonPhoenix +from core.globalvar import wxPythonPhoenix def cmp(a, b): @@ -796,19 +797,8 @@ def GetFormats(writableOnly=False): def GetSettingsPath(): """Get full path to the settings directory""" - try: - verFd = open(os.path.join(ETCDIR, "VERSIONNUMBER")) - version = int(verFd.readlines()[0].split(" ")[0].split(".")[0]) - except (OSError, ValueError, TypeError, IndexError) as e: - sys.exit(_("ERROR: Unable to determine GRASS version. Details: %s") % e) - - verFd.close() - - # keep location of settings files rc and wx in sync with lib/init/grass.py - if sys.platform == "win32": - return os.path.join(os.getenv("APPDATA"), "GRASS%d" % version) - - return os.path.join(os.getenv("HOME"), ".grass%d" % version) + version_major, version_minor, _ = grass.version()["version"].split(".") + return get_grass_config_dir(version_major, version_minor, os.environ) def StoreEnvVariable(key, value=None, envFile=None): diff --git a/lib/gis/home.c b/lib/gis/home.c index 166f6f2f191..e39cbff49ca 100644 --- a/lib/gis/home.c +++ b/lib/gis/home.c @@ -100,15 +100,20 @@ const char *G_config_path(void) static int initialized_config; static const char *config_path = 0; char buf[GPATH_MAX]; + static const char *config_dir = NULL; if (G_is_initialized(&initialized_config)) return config_path; + config_dir = getenv("GRASS_CONFIG_DIR"); + if (!config_dir) #ifdef __MINGW32__ - sprintf(buf, "%s%c%s", getenv("APPDATA"), HOST_DIRSEP, CONFIG_DIR); + config_dir = getenv("APPDATA"); #else - sprintf(buf, "%s%c%s", G_home(), HOST_DIRSEP, CONFIG_DIR); + config_dir = G_home(); #endif + + snprintf(buf, GPATH_MAX, "%s%c%s", config_dir, HOST_DIRSEP, CONFIG_DIR); config_path = G_store(buf); #if 0 diff --git a/lib/init/grass.py b/lib/init/grass.py index 1231e299049..41fe07fa02f 100755 --- a/lib/init/grass.py +++ b/lib/init/grass.py @@ -306,6 +306,7 @@ def f(fmt, *args): FLAG {standard_flags} {env_vars}: + GRASS_CONFIG_DIR {config_dir_var} GRASS_GUI {gui_var} GRASS_HTML_BROWSER {html_var} GRASS_ADDON_PATH {addon_path_var} @@ -349,6 +350,7 @@ def help_message(default_gui): mapset=_("initial GRASS mapset"), full_mapset=_("fully qualified initial mapset directory"), env_vars=_("Environment variables relevant for startup"), + config_dir_var=_("set root path for configuration directory"), gui_var=_("select GUI (text, gui, gtext)"), html_var=_("set html web browser for help pages"), addon_path_var=_( @@ -379,8 +381,8 @@ def help_message(default_gui): sys.stderr.write(s) -def get_grass_config_dir(): - """Get configuration directory +def create_grass_config_dir(): + """Create configuration directory Determines path of GRASS GIS user configuration directory and creates it if it does not exist. @@ -388,41 +390,25 @@ def get_grass_config_dir(): Configuration directory is for example used for grass env file (the one which caries mapset settings from session to session). """ - # The code is in sync with grass.app.runtime (but not the same). - if WINDOWS: - grass_config_dirname = f"GRASS{GRASS_VERSION_MAJOR}" - win_conf_path = os.getenv("APPDATA") - # this can happen with some strange settings - if not win_conf_path: - fatal( - _( - "The APPDATA variable is not set, ask your operating" - " system support" - ) - ) - if not os.path.exists(win_conf_path): - fatal( - _( - "The APPDATA variable points to directory which does" - " not exist, ask your operating system support" - ) - ) - directory = os.path.join(win_conf_path, grass_config_dirname) - elif MACOS: - version = f"{GRASS_VERSION_MAJOR}.{GRASS_VERSION_MINOR}" - return os.path.join(os.getenv("HOME"), "Library", "GRASS", version) - else: - grass_config_dirname = f".grass{GRASS_VERSION_MAJOR}" - directory = os.path.join(os.getenv("HOME"), grass_config_dirname) + from grass.app.runtime import get_grass_config_dir + + try: + directory = get_grass_config_dir( + GRASS_VERSION_MAJOR, GRASS_VERSION_MINOR, os.environ + ) + except (RuntimeError, NotADirectoryError) as e: + fatal(f"{e}") + if not os.path.isdir(directory): try: - os.mkdir(directory) + os.makedirs(directory) except OSError as e: # Can happen as a race condition if not e.errno == errno.EEXIST or not os.path.isdir(directory): fatal( - _("Failed to create configuration directory '%s' with error: %s") - % (directory, e.strerror) + _( + "Failed to create configuration directory '{}' with error: {}" + ).format(directory, e.strerror) ) return directory @@ -2233,7 +2219,8 @@ def main(): # This has to be called before any _() function call! # Subsequent functions are using _() calls and # thus must be called only after Language has been set. - grass_config_dir = get_grass_config_dir() + find_grass_python_package() + grass_config_dir = create_grass_config_dir() set_language(grass_config_dir) # Set default GUI @@ -2296,8 +2283,6 @@ def main(): # Create the session grassrc file gisrc = create_gisrc(tmpdir, gisrcrc) - find_grass_python_package() - from grass.app.runtime import ( ensure_home, set_paths, diff --git a/lib/init/variables.html b/lib/init/variables.html index 4c84a7803ac..6fd10d7f173 100644 --- a/lib/init/variables.html +++ b/lib/init/variables.html @@ -124,6 +124,13 @@

          List of selected (GRASS related) shell environment variables

          available are RLE, ZLIB, and LZ4. The compressors BZIP2 and ZSTD must be enabled when configuring GRASS for compilation. +
          GRASS_CONFIG_DIR
          +
          [grass startup script]
          + specifies root path for GRASS configuration directory. + If not specified, the default placement of the + configuration directory is used: $HOME on GNU/Linux, + $HOME/Library on Mac OS X, and %APPDATA% on MS Windows.
          +
          GRASS_DB_ENCODING
          [various modules, wxGUI]
          encoding for vector attribute data (utf-8, ascii, iso8859-1, koi8-r)
          diff --git a/python/grass/app/runtime.py b/python/grass/app/runtime.py index 48ff8cde92b..5da00bae0df 100644 --- a/python/grass/app/runtime.py +++ b/python/grass/app/runtime.py @@ -28,16 +28,35 @@ def get_grass_config_dir(major_version, minor_version, env): Determines path of GRASS GIS user configuration directory. """ - # The code is in sync with grass.app.runtime (but not the same). + if env.get("GRASS_CONFIG_DIR"): + # use GRASS_CONFIG_DIR environmental variable is defined + env_dirname = "GRASS_CONFIG_DIR" + else: + env_dirname = "APPDATA" if WINDOWS else "HOME" + + config_dir = env.get(env_dirname) + if config_dir is None: + raise RuntimeError( + f"The {env_dirname} variable is not set, ask your operating" + " system support" + ) + + if not os.path.isdir(config_dir): + raise NotADirectoryError( + f"The {env_dirname} variable points to directory which does" + " not exist, ask your operating system support" + ) + if WINDOWS: config_dirname = f"GRASS{major_version}" - return os.path.join(env.get("APPDATA"), config_dirname) elif MACOS: - version = f"{major_version}.{minor_version}" - return os.path.join(env.get("HOME"), "Library", "GRASS", version) + config_dirname = os.path.join( + "Library", "GRASS", f"{major_version}.{minor_version}" + ) else: config_dirname = f".grass{major_version}" - return os.path.join(env.get("HOME"), config_dirname) + + return os.path.join(config_dir, config_dirname) def append_left_main_executable_paths(paths, install_path): diff --git a/scripts/g.extension/g.extension.py b/scripts/g.extension/g.extension.py index fb399db9d06..75916576397 100644 --- a/scripts/g.extension/g.extension.py +++ b/scripts/g.extension/g.extension.py @@ -2466,12 +2466,14 @@ def resolve_install_prefix(path, to_system): path = os.environ["GISBASE"] if path == "$GRASS_ADDON_BASE": if not os.getenv("GRASS_ADDON_BASE"): + from grass.app.runtime import get_grass_config_dir + + path = os.path.join( + get_grass_config_dir(VERSION[0], VERSION[1], os.environ), "addons" + ) gs.warning( - _( - "GRASS_ADDON_BASE is not defined, installing to ~/.grass{}/addons" - ).format(VERSION[0]) + _("GRASS_ADDON_BASE is not defined, installing to {}").format(path) ) - path = os.path.join(os.environ["HOME"], f".grass{VERSION[0]}", "addons") else: path = os.environ["GRASS_ADDON_BASE"] if os.path.exists(path) and not os.access(path, os.W_OK): From ae2281e8cd16a4e69ca86f1b9ff1835e4888ef13 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 2 Aug 2024 16:41:46 +0000 Subject: [PATCH 077/471] CI(deps): Update actions/upload-artifact action to v4.3.5 (#4125) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/actions/create-upload-suggestions/action.yml | 4 ++-- .github/workflows/macos.yml | 2 +- .github/workflows/osgeo4w.yml | 2 +- .github/workflows/pytest.yml | 2 +- .github/workflows/python-code-quality.yml | 4 ++-- .github/workflows/ubuntu.yml | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/actions/create-upload-suggestions/action.yml b/.github/actions/create-upload-suggestions/action.yml index 8ac14895db8..201d566bffc 100644 --- a/.github/actions/create-upload-suggestions/action.yml +++ b/.github/actions/create-upload-suggestions/action.yml @@ -177,7 +177,7 @@ runs: echo "diff-file-name=${INPUT_DIFF_FILE_NAME}" >> "${GITHUB_OUTPUT}" env: INPUT_DIFF_FILE_NAME: ${{ steps.tool-name-safe.outputs.diff-file-name }} - - uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4 + - uses: actions/upload-artifact@89ef406dd8d7e03cfd12d9e0a4a378f454709029 # v4.3.5 id: upload-diff if: >- ${{ (steps.files_changed.outputs.files_changed == 'true') && @@ -200,7 +200,7 @@ runs: echo 'Suggestions can only be added near to lines changed in this PR.' echo 'If any fixes can be added as code suggestions, they will be added shortly from another workflow.' } >> "${GITHUB_STEP_SUMMARY}" - - uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4 + - uses: actions/upload-artifact@89ef406dd8d7e03cfd12d9e0a4a378f454709029 # v4.3.5 id: upload-changes if: >- ${{ always() && diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index da6a1a4d295..34397e88873 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -72,7 +72,7 @@ jobs: nc_spm_full_v2alpha2.tar.gz" - name: Make HTML test report available if: ${{ always() }} - uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4 + uses: actions/upload-artifact@89ef406dd8d7e03cfd12d9e0a4a378f454709029 # v4.3.5 with: name: testreport-macOS path: testreport diff --git a/.github/workflows/osgeo4w.yml b/.github/workflows/osgeo4w.yml index 2fd1412d76b..a4ee546a4f3 100644 --- a/.github/workflows/osgeo4w.yml +++ b/.github/workflows/osgeo4w.yml @@ -83,7 +83,7 @@ jobs: - name: Make HTML test report available if: ${{ always() }} - uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4 + uses: actions/upload-artifact@89ef406dd8d7e03cfd12d9e0a4a378f454709029 # v4.3.5 with: name: testreport-${{ matrix.os }} path: testreport diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 70ad658265f..6707b1be4d3 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -114,7 +114,7 @@ jobs: token: ${{ secrets.CODECOV_TOKEN }} - name: Make python-only code coverage test report available if: ${{ !cancelled() }} - uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4 + uses: actions/upload-artifact@89ef406dd8d7e03cfd12d9e0a4a378f454709029 # v4.3.5 with: name: python-codecoverage-report-${{ matrix.os }}-${{ matrix.python-version }} path: coverage_html_report diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index ea8eb9eeac8..f83a5d43929 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -129,7 +129,7 @@ jobs: bandit -c pyproject.toml -iii -r . -f sarif -o bandit.sarif --exit-zero - name: Upload Bandit Scan Results - uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4 + uses: actions/upload-artifact@89ef406dd8d7e03cfd12d9e0a4a378f454709029 # v4.3.5 with: name: bandit.sarif path: bandit.sarif @@ -201,7 +201,7 @@ jobs: cp -rp dist.$ARCH/docs/html/libpython sphinx-grass - name: Make Sphinx documentation available - uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4 + uses: actions/upload-artifact@89ef406dd8d7e03cfd12d9e0a4a378f454709029 # v4.3.5 with: name: sphinx-grass path: sphinx-grass diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index e6f2899c2ce..a858f8984a9 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -147,7 +147,7 @@ jobs: - name: Make HTML test report available if: ${{ always() }} - uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4 + uses: actions/upload-artifact@89ef406dd8d7e03cfd12d9e0a4a378f454709029 # v4.3.5 with: name: testreport-${{ matrix.os }}-${{ matrix.config }}-${{ matrix.extra-include }} path: testreport From a342ea888d2016d90eb615389d827bdca5c4ea33 Mon Sep 17 00:00:00 2001 From: Nicklas Larsson Date: Fri, 2 Aug 2024 18:46:54 +0200 Subject: [PATCH 078/471] CI: update macOS runner dependencies (#4120) --- .github/workflows/macos_dependencies.txt | 71 +++++++++++++----------- 1 file changed, 40 insertions(+), 31 deletions(-) diff --git a/.github/workflows/macos_dependencies.txt b/.github/workflows/macos_dependencies.txt index 19edb646ac2..8f9507c15b0 100644 --- a/.github/workflows/macos_dependencies.txt +++ b/.github/workflows/macos_dependencies.txt @@ -1,39 +1,48 @@ -clang_osx-arm64 -clangxx_osx-arm64 -setuptools -python -python.app -numpy -gdal -freetype +blas cairo -matplotlib -pandoc -pillow -six -wxpython -sqlite -jpeg -libpng -libtiff -pkg-config -libiconv +clangxx_osx-arm64 +clang_osx-arm64 +cmake fftw -lapack -blas -giflib -proj +flex +freetype geos -krb5 gettext -lastools ghostscript -zstd +giflib +git +libjpeg-turbo +krb5 +lapack +lastools +libgdal-arrow-parquet +libgdal-core +libgdal-hdf4 +libgdal-hdf5 +libgdal-netcdf +libgdal-pdf +libgdal-pg +libgdal-postgisraster +libgdal-tiledb +libiconv +libjpeg-turbo +libpng +libsvm +libtiff +llvm-openmp +matplotlib +numpy<2 +pandoc pdal +pillow +pkg-config ply postgresql -# postgis>=3.1.4 -cmake -llvm-openmp -flex -git +proj +python +python.app +setuptools +six +sqlite +wxpython +zstd From cbc8fd564344d4a06b8c1da32dc63b7bb887a9dc Mon Sep 17 00:00:00 2001 From: Makiko Shukunobe Date: Fri, 2 Aug 2024 12:48:03 -0400 Subject: [PATCH 079/471] grass.pygrass: remove unused arg in ctypes.CFUNCTYPE (#4113) Co-authored-by: Anna Petrasova --- python/grass/pygrass/raster/rowio.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/python/grass/pygrass/raster/rowio.py b/python/grass/pygrass/raster/rowio.py index 377843642a1..ed43c9a8452 100644 --- a/python/grass/pygrass/raster/rowio.py +++ b/python/grass/pygrass/raster/rowio.py @@ -13,9 +13,7 @@ from grass.pygrass.raster.raster_type import TYPE as RTYPE -CMPFUNC = ctypes.CFUNCTYPE( - ctypes.c_int, ctypes.c_int, ctypes.c_void_p, ctypes.c_int, ctypes.c_int -) +CMPFUNC = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.c_void_p, ctypes.c_int) def getmaprow_CELL(fd, buf, row): From 7f176956c67a60fb282738b9a674feeea2f34c8b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 2 Aug 2024 18:47:52 -0400 Subject: [PATCH 080/471] CI(deps): Update dependency black to v24.8.0 (#4128) --- .github/workflows/python-code-quality.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index f83a5d43929..9cd48b27fdc 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -28,7 +28,7 @@ jobs: PYTHON_VERSION: "3.10" MIN_PYTHON_VERSION: "3.8" # renovate: datasource=pypi depName=black - BLACK_VERSION: "24.4.2" + BLACK_VERSION: "24.8.0" # renovate: datasource=pypi depName=flake8 FLAKE8_VERSION: "7.1.0" # renovate: datasource=pypi depName=pylint From a99009100bf711a168d2f953484de00863a7d1d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Sat, 3 Aug 2024 06:57:53 -0400 Subject: [PATCH 081/471] style: Revert "style: Fixes some unnecessary-lambda (PLW0108) (#3956)" (#4127) * Revert "style: Fixes some unnecessary-lambda (PLW0108) (#3956)" This reverts commit 0cc7cbf610e889075b8f465eecb0f319737b7456. * ruff: Ignore unnecessary-lambda (PLW0108) in gui --- gui/wxpython/animation/frame.py | 2 +- gui/wxpython/gmodeler/dialogs.py | 4 +++- gui/wxpython/gmodeler/panels.py | 4 +++- gui/wxpython/gui_core/forms.py | 4 +++- gui/wxpython/gui_core/menu.py | 2 +- gui/wxpython/gui_core/prompt.py | 8 +++++--- gui/wxpython/gui_core/widgets.py | 2 +- gui/wxpython/history/tree.py | 8 ++++++-- gui/wxpython/iclass/frame.py | 4 +++- gui/wxpython/lmgr/frame.py | 20 +++++++++++++++----- gui/wxpython/lmgr/layertree.py | 4 ++-- gui/wxpython/main_window/frame.py | 20 +++++++++++++++----- gui/wxpython/mapdisp/frame.py | 12 +++++++++--- gui/wxpython/mapdisp/main.py | 4 ++-- gui/wxpython/mapswipe/frame.py | 8 +++++--- gui/wxpython/rdigit/g.gui.rdigit.py | 2 +- gui/wxpython/vdigit/g.gui.vdigit.py | 2 +- pyproject.toml | 2 ++ 18 files changed, 78 insertions(+), 34 deletions(-) diff --git a/gui/wxpython/animation/frame.py b/gui/wxpython/animation/frame.py index cec08546366..6496920a5cd 100644 --- a/gui/wxpython/animation/frame.py +++ b/gui/wxpython/animation/frame.py @@ -353,7 +353,7 @@ def OnPreferences(self, event): if not self.dialogs["preferences"]: dlg = PreferencesDialog(parent=self, giface=self._giface) self.dialogs["preferences"] = dlg - dlg.formatChanged.connect(self.controller.UpdateAnimations) + dlg.formatChanged.connect(lambda: self.controller.UpdateAnimations()) dlg.CenterOnParent() self.dialogs["preferences"].Show() diff --git a/gui/wxpython/gmodeler/dialogs.py b/gui/wxpython/gmodeler/dialogs.py index 10729b83289..67c8841dc45 100644 --- a/gui/wxpython/gmodeler/dialogs.py +++ b/gui/wxpython/gmodeler/dialogs.py @@ -206,7 +206,9 @@ def __init__( parent=self, giface=giface, menuModel=menuModel.GetModel() ) self.cmd_prompt.promptRunCmd.connect(self.OnCommand) - self.cmd_prompt.commandSelected.connect(self.label.SetValue) + self.cmd_prompt.commandSelected.connect( + lambda command: self.label.SetValue(command) + ) self.search = SearchModuleWidget( parent=self.panel, model=menuModel.GetModel(), showTip=True ) diff --git a/gui/wxpython/gmodeler/panels.py b/gui/wxpython/gmodeler/panels.py index 8179f07e244..6d6bff45387 100644 --- a/gui/wxpython/gmodeler/panels.py +++ b/gui/wxpython/gmodeler/panels.py @@ -156,7 +156,9 @@ def __init__( self.goutput = GConsoleWindow( parent=self, giface=giface, gconsole=self._gconsole ) - self.goutput.showNotification.connect(self.SetStatusText) + self.goutput.showNotification.connect( + lambda message: self.SetStatusText(message) + ) # here events are binded twice self._gconsole.Bind( diff --git a/gui/wxpython/gui_core/forms.py b/gui/wxpython/gui_core/forms.py index 89aa24c5844..0c32a12c7f7 100644 --- a/gui/wxpython/gui_core/forms.py +++ b/gui/wxpython/gui_core/forms.py @@ -564,7 +564,9 @@ def __init__( self._gconsole.mapCreated.connect(self.OnMapCreated) self.goutput = self.notebookpanel.goutput if self.goutput: - self.goutput.showNotification.connect(self.SetStatusText) + self.goutput.showNotification.connect( + lambda message: self.SetStatusText(message) + ) self.notebookpanel.OnUpdateValues = self.updateValuesHook guisizer.Add(self.notebookpanel, proportion=1, flag=wx.EXPAND) diff --git a/gui/wxpython/gui_core/menu.py b/gui/wxpython/gui_core/menu.py index f435fe4c9f8..71d6d153f8b 100644 --- a/gui/wxpython/gui_core/menu.py +++ b/gui/wxpython/gui_core/menu.py @@ -227,7 +227,7 @@ def __init__(self, parent, handlerObj, giface, model, id=wx.ID_ANY, **kwargs): self._btnAdvancedSearch.Bind(wx.EVT_BUTTON, lambda evt: self.AdvancedSearch()) self._tree.selectionChanged.connect(self.OnItemSelected) - self._tree.itemActivated.connect(self.Run) + self._tree.itemActivated.connect(lambda node: self.Run(node)) self._layout() diff --git a/gui/wxpython/gui_core/prompt.py b/gui/wxpython/gui_core/prompt.py index 13b4d0955b7..088465a976e 100644 --- a/gui/wxpython/gui_core/prompt.py +++ b/gui/wxpython/gui_core/prompt.py @@ -167,9 +167,11 @@ def __init__(self, parent, giface, menuModel, margin=False): self._loadHistory() if giface: giface.currentMapsetChanged.connect(self._loadHistory) - giface.entryToHistoryAdded.connect(self._addEntryToCmdHistoryBuffer) + giface.entryToHistoryAdded.connect( + lambda entry: self._addEntryToCmdHistoryBuffer(entry) + ) giface.entryFromHistoryRemoved.connect( - self._removeEntryFromCmdHistoryBuffer + lambda index: self._removeEntryFromCmdHistoryBuffer(index) ) # # bindings @@ -431,7 +433,7 @@ def GetWordLeft(self, withDelimiter=False, ignoredDelimiter=None): else: delimiter = char parts.append(delimiter + textLeft.rpartition(char)[2]) - return min(parts, key=len) + return min(parts, key=lambda x: len(x)) def ShowList(self): """Show sorted auto-completion list if it is not empty""" diff --git a/gui/wxpython/gui_core/widgets.py b/gui/wxpython/gui_core/widgets.py index 3196ff173db..06afe3ab8d9 100644 --- a/gui/wxpython/gui_core/widgets.py +++ b/gui/wxpython/gui_core/widgets.py @@ -1335,7 +1335,7 @@ def _searchModule(self, keys, value): nodes.update(self._model.SearchNodes(key=key, value=value)) nodes = list(nodes) - nodes.sort(key=self._model.GetIndexOfNode) + nodes.sort(key=lambda node: self._model.GetIndexOfNode(node)) self._results = nodes self._resultIndex = -1 return sorted([node.data["command"] for node in nodes if node.data["command"]]) diff --git a/gui/wxpython/history/tree.py b/gui/wxpython/history/tree.py index 98ab8eb0994..9119473d9ff 100644 --- a/gui/wxpython/history/tree.py +++ b/gui/wxpython/history/tree.py @@ -137,8 +137,12 @@ def __init__( self.runIgnoredCmdPattern = Signal("HistoryBrowserTree.runIgnoredCmdPattern") self._giface.currentMapsetChanged.connect(self.UpdateHistoryModelFromScratch) - self._giface.entryToHistoryAdded.connect(self.InsertCommand) - self._giface.entryInHistoryUpdated.connect(self.UpdateCommand) + self._giface.entryToHistoryAdded.connect( + lambda entry: self.InsertCommand(entry) + ) + self._giface.entryInHistoryUpdated.connect( + lambda entry: self.UpdateCommand(entry) + ) self.SetToolTip(_("Double-click to open the tool")) self.selectionChanged.connect(self.OnItemSelected) diff --git a/gui/wxpython/iclass/frame.py b/gui/wxpython/iclass/frame.py index c6a401ad4d6..52af10d85e0 100644 --- a/gui/wxpython/iclass/frame.py +++ b/gui/wxpython/iclass/frame.py @@ -139,7 +139,9 @@ def __init__( # TODO: for vdigit: it does nothing here because areas do not produce # this info self.firstMapWindow.digitizingInfo.connect( - self.statusbarManager.statusbarItems["coordinates"].SetAdditionalInfo + lambda text: self.statusbarManager.statusbarItems[ + "coordinates" + ].SetAdditionalInfo(text) ) self.firstMapWindow.digitizingInfoUnavailable.connect( lambda: self.statusbarManager.statusbarItems[ diff --git a/gui/wxpython/lmgr/frame.py b/gui/wxpython/lmgr/frame.py index ddb91789508..de06305086f 100644 --- a/gui/wxpython/lmgr/frame.py +++ b/gui/wxpython/lmgr/frame.py @@ -389,7 +389,9 @@ def _createNotebook(self): def _createDataCatalog(self, parent): """Initialize Data Catalog widget""" self.datacatalog = DataCatalog(parent=parent, giface=self._giface) - self.datacatalog.showNotification.connect(self.SetStatusText) + self.datacatalog.showNotification.connect( + lambda message: self.SetStatusText(message) + ) def _createDisplay(self, parent): """Initialize Display widget""" @@ -410,7 +412,9 @@ def _createSearchModule(self, parent): giface=self._giface, model=self._moduleTreeBuilder.GetModel(), ) - self.search.showNotification.connect(self.SetStatusText) + self.search.showNotification.connect( + lambda message: self.SetStatusText(message) + ) else: self.search = None @@ -430,7 +434,9 @@ def _createConsole(self, parent): menuModel=self._moduleTreeBuilder.GetModel(), gcstyle=GC_PROMPT, ) - self.goutput.showNotification.connect(self.SetStatusText) + self.goutput.showNotification.connect( + lambda message: self.SetStatusText(message) + ) self._gconsole.mapCreated.connect(self.OnMapCreated) self._gconsole.Bind( @@ -443,7 +449,9 @@ def _createHistoryBrowser(self, parent): """Initialize history browser widget""" if not UserSettings.Get(group="manager", key="hideTabs", subkey="history"): self.history = HistoryBrowser(parent=parent, giface=self._giface) - self.history.showNotification.connect(self.SetStatusText) + self.history.showNotification.connect( + lambda message: self.SetStatusText(message) + ) self.history.runIgnoredCmdPattern.connect( lambda cmd: self.RunSpecialCmd(command=cmd), ) @@ -629,7 +637,9 @@ def _addPagesToNotebook(self): # add 'console' widget to main notebook page and add connect switch page signal self.notebook.AddPage(page=self.goutput, text=_("Console"), name="output") - self.goutput.contentChanged.connect(self._switchPage) + self.goutput.contentChanged.connect( + lambda notification: self._switchPage(notification) + ) # add 'history module' widget to main notebook page if self.history: diff --git a/gui/wxpython/lmgr/layertree.py b/gui/wxpython/lmgr/layertree.py index e2204555415..4779a40db9b 100644 --- a/gui/wxpython/lmgr/layertree.py +++ b/gui/wxpython/lmgr/layertree.py @@ -1248,7 +1248,7 @@ def OnPopupGroupOpacityLevel(self, event): lambda value: self.ChangeGroupLayerOpacity(layer=child, value=value) ) # Apply button - dlg.applyOpacity.connect(self._recalculateLayerButtonPosition) + dlg.applyOpacity.connect(lambda: self._recalculateLayerButtonPosition()) dlg.CentreOnParent() if dlg.ShowModal() == wx.ID_OK: @@ -1288,7 +1288,7 @@ def OnPopupOpacityLevel(self, event): ) ) # Apply button - dlg.applyOpacity.connect(self._recalculateLayerButtonPosition) + dlg.applyOpacity.connect(lambda: self._recalculateLayerButtonPosition()) dlg.CentreOnParent() if dlg.ShowModal() == wx.ID_OK: diff --git a/gui/wxpython/main_window/frame.py b/gui/wxpython/main_window/frame.py index 6b9a9be640f..facb3f8195a 100644 --- a/gui/wxpython/main_window/frame.py +++ b/gui/wxpython/main_window/frame.py @@ -331,7 +331,9 @@ def _createMainNotebook(self): def _createDataCatalog(self, parent): """Initialize Data Catalog widget""" self.datacatalog = DataCatalog(parent=parent, giface=self._giface) - self.datacatalog.showNotification.connect(self.SetStatusText) + self.datacatalog.showNotification.connect( + lambda message: self.SetStatusText(message) + ) def _createDisplay(self, parent): """Initialize Display widget""" @@ -353,7 +355,9 @@ def _createSearchModule(self, parent): giface=self._giface, model=self._moduleTreeBuilder.GetModel(), ) - self.search.showNotification.connect(self.SetStatusText) + self.search.showNotification.connect( + lambda message: self.SetStatusText(message) + ) else: self.search = None @@ -373,8 +377,12 @@ def _createConsole(self, parent): menuModel=self._moduleTreeBuilder.GetModel(), gcstyle=GC_PROMPT, ) - self.goutput.showNotification.connect(self.SetStatusText) - self.goutput.contentChanged.connect(self._focusPage) + self.goutput.showNotification.connect( + lambda message: self.SetStatusText(message) + ) + self.goutput.contentChanged.connect( + lambda notification: self._focusPage(notification) + ) self._gconsole.mapCreated.connect(self.OnMapCreated) self._gconsole.Bind( @@ -387,7 +395,9 @@ def _createHistoryBrowser(self, parent): """Initialize history browser widget""" if not UserSettings.Get(group="manager", key="hideTabs", subkey="history"): self.history = HistoryBrowser(parent=parent, giface=self._giface) - self.history.showNotification.connect(self.SetStatusText) + self.history.showNotification.connect( + lambda message: self.SetStatusText(message) + ) self.history.runIgnoredCmdPattern.connect( lambda cmd: self.RunSpecialCmd(command=cmd), ) diff --git a/gui/wxpython/mapdisp/frame.py b/gui/wxpython/mapdisp/frame.py index 96f147c3cb0..8cb8a51d407 100644 --- a/gui/wxpython/mapdisp/frame.py +++ b/gui/wxpython/mapdisp/frame.py @@ -324,7 +324,9 @@ def _addToolbarVDigit(self): ) self._setUpMapWindow(self.MapWindowVDigit) self.MapWindowVDigit.digitizingInfo.connect( - self.statusbarManager.statusbarItems["coordinates"].SetAdditionalInfo + lambda text: self.statusbarManager.statusbarItems[ + "coordinates" + ].SetAdditionalInfo(text) ) self.MapWindowVDigit.digitizingInfoUnavailable.connect( lambda: self.statusbarManager.statusbarItems[ @@ -364,7 +366,9 @@ def _addToolbarVDigit(self): def openATM(selection): self._layerManager.OnShowAttributeTable(None, selection=selection) - self.toolbars["vdigit"].openATM.connect(openATM) + self.toolbars["vdigit"].openATM.connect( + lambda selection: openATM(selection) + ) self.Map.layerAdded.connect(self._updateVDigitLayers) self.MapWindowVDigit.SetToolbar(self.toolbars["vdigit"]) @@ -1243,7 +1247,9 @@ def _onMeasure(self, controller): self.measureController = controller(self._giface, mapWindow=self.GetMapWindow()) # assure that the mode is ended and lines are cleared whenever other # tool is selected - self._toolSwitcher.toggleToolChanged.connect(self.measureController.Stop) + self._toolSwitcher.toggleToolChanged.connect( + lambda: self.measureController.Stop() + ) self.measureController.Start() def OnProfile(self, event): diff --git a/gui/wxpython/mapdisp/main.py b/gui/wxpython/mapdisp/main.py index e1774ed472b..5bef25ad128 100644 --- a/gui/wxpython/mapdisp/main.py +++ b/gui/wxpython/mapdisp/main.py @@ -579,8 +579,8 @@ def CreateMapDisplay(self, name, decorations=True): ), ) - self.Map.saveToFile.connect(self.mapDisplay.DOutFile) - self.Map.dToRast.connect(self.mapDisplay.DToRast) + self.Map.saveToFile.connect(lambda cmd: self.mapDisplay.DOutFile(cmd)) + self.Map.dToRast.connect(lambda cmd: self.mapDisplay.DToRast(cmd)) self.Map.query.connect( lambda ltype, maps: self.mapDisplay.SetQueryLayersAndActivate( ltype=ltype, maps=maps diff --git a/gui/wxpython/mapswipe/frame.py b/gui/wxpython/mapswipe/frame.py index 14c1fd91e28..6552ff044f3 100644 --- a/gui/wxpython/mapswipe/frame.py +++ b/gui/wxpython/mapswipe/frame.py @@ -85,8 +85,8 @@ def __init__( self.secondMapWindow.mapQueried.connect(self.Query) # bind tracking cursosr to mirror it - self.firstMapWindow.Bind(wx.EVT_MOTION, self.TrackCursor) - self.secondMapWindow.Bind(wx.EVT_MOTION, self.TrackCursor) + self.firstMapWindow.Bind(wx.EVT_MOTION, lambda evt: self.TrackCursor(evt)) + self.secondMapWindow.Bind(wx.EVT_MOTION, lambda evt: self.TrackCursor(evt)) self.MapWindow = self.firstMapWindow # current by default self.firstMapWindow.zoomhistory = self.secondMapWindow.zoomhistory @@ -784,7 +784,9 @@ def Query(self, x, y): else: self._queryDialog = QueryDialog(parent=self, data=result) self._queryDialog.Bind(wx.EVT_CLOSE, self._oncloseQueryDialog) - self._queryDialog.redirectOutput.connect(self._giface.WriteLog) + self._queryDialog.redirectOutput.connect( + lambda output: self._giface.WriteLog(output) + ) self._queryDialog.Show() def _oncloseQueryDialog(self, event): diff --git a/gui/wxpython/rdigit/g.gui.rdigit.py b/gui/wxpython/rdigit/g.gui.rdigit.py index a05fdb547bb..81f15d15531 100755 --- a/gui/wxpython/rdigit/g.gui.rdigit.py +++ b/gui/wxpython/rdigit/g.gui.rdigit.py @@ -139,7 +139,7 @@ def __init__( rdigit.OnMapSelection() # use Close instead of QuitRDigit for standalone tool self.rdigit.quitDigitizer.disconnect(self.QuitRDigit) - self.rdigit.quitDigitizer.connect(self.Close) + self.rdigit.quitDigitizer.connect(lambda: self.Close()) # add Map Display panel to Map Display frame sizer = wx.BoxSizer(wx.VERTICAL) diff --git a/gui/wxpython/vdigit/g.gui.vdigit.py b/gui/wxpython/vdigit/g.gui.vdigit.py index ea09f207909..be476a03ee1 100644 --- a/gui/wxpython/vdigit/g.gui.vdigit.py +++ b/gui/wxpython/vdigit/g.gui.vdigit.py @@ -104,7 +104,7 @@ def __init__(self, parent, vectorMap): self.toolbars["vdigit"].StartEditing(mapLayer) # use Close instead of QuitVDigit for standalone tool self.toolbars["vdigit"].quitDigitizer.disconnect(self.QuitVDigit) - self.toolbars["vdigit"].quitDigitizer.connect(self.Close) + self.toolbars["vdigit"].quitDigitizer.connect(lambda: self.Close()) # add Map Display panel to Map Display frame sizer = wx.BoxSizer(wx.VERTICAL) diff --git a/pyproject.toml b/pyproject.toml index 574b99df4cb..3fba3ea0370 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -304,10 +304,12 @@ ignore = [ # "INT002", # f-string-in-get-text-func-call # "INT001", # format-in-get-text-func-call # "INT003", # printf-in-get-text-func-call +# "PLW0108", # unnecessary-lambda # Ignore `E402` (import violations) in all `__init__.py` files "*/testsuite/**.py" = ["PT009", "PT027"] "__init__.py" = ["E402"] "general/g.parser/test.py" = ["INT003"] +"gui/**" = ["PLW0108"] # See https://github.com/OSGeo/grass/issues/4124 "gui/wxpython/animation/dialogs.py" = ["INT002"] "gui/wxpython/animation/temporal_manager.py" = ["INT003"] "gui/wxpython/core/debug.py" = ["INT002"] From 31b2becaff83d145ee5b3dcda98621021cbbea37 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 3 Aug 2024 12:56:47 +0000 Subject: [PATCH 082/471] CI(deps): Update ruff to v0.5.6 (#4126) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * CI(deps): Update ruff to v0.5.6 * Ignore 6 new issues of shadowing builtins --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Edouard Choinière <27212526+echoix@users.noreply.github.com> --- .github/workflows/python-code-quality.yml | 2 +- .pre-commit-config.yaml | 2 +- gui/wxpython/gui_core/query.py | 4 +++- gui/wxpython/gui_core/simplelmgr.py | 6 +++++- pyproject.toml | 19 ++++++++++++------- 5 files changed, 22 insertions(+), 11 deletions(-) diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index 9cd48b27fdc..bd8fc83890b 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -36,7 +36,7 @@ jobs: # renovate: datasource=pypi depName=bandit BANDIT_VERSION: "1.7.9" # renovate: datasource=pypi depName=ruff - RUFF_VERSION: "0.5.5" + RUFF_VERSION: "0.5.6" runs-on: ${{ matrix.os }} permissions: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 47d0cd36bb9..638a1e4787d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -37,7 +37,7 @@ repos: ) - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.5.5 + rev: v0.5.6 hooks: # Run the linter. - id: ruff diff --git a/gui/wxpython/gui_core/query.py b/gui/wxpython/gui_core/query.py index 0dfd21d8765..6250095cb29 100644 --- a/gui/wxpython/gui_core/query.py +++ b/gui/wxpython/gui_core/query.py @@ -148,7 +148,9 @@ def ShowContextMenu(self, node): id = NewId() ids.append(id) self.Bind( - wx.EVT_MENU, lambda evt, t=text[1], id=id: self._copyText(t), id=id + wx.EVT_MENU, + lambda evt, t=text[1], id=id: self._copyText(t), # noqa: A006 + id=id, ) menu.Append(id, text[0]) diff --git a/gui/wxpython/gui_core/simplelmgr.py b/gui/wxpython/gui_core/simplelmgr.py index 9f03a49396e..eb0c0a00688 100644 --- a/gui/wxpython/gui_core/simplelmgr.py +++ b/gui/wxpython/gui_core/simplelmgr.py @@ -159,7 +159,11 @@ def OnContextMenu(self, event): ] for label, text in zip(labels, texts): id = NewId() - self.Bind(wx.EVT_MENU, lambda evt, t=text, id=id: self._copyText(t), id=id) + self.Bind( + wx.EVT_MENU, + lambda evt, t=text, id=id: self._copyText(t), # noqa: A006 + id=id, + ) menu.Append(id, label) diff --git a/pyproject.toml b/pyproject.toml index 3fba3ea0370..18baa5eebac 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -227,13 +227,13 @@ ignore = [ "PTH202", # os-path-getsize "PTH204", # os-path-getmtime "PTH207", # glob - "RET501", # unnecessary-return-none - "RET502", # implicit-return-value - "RET503", # implicit-return - "RET505", # superfluous-else-return - "RET506", # superfluous-else-raise - "RET507", # superfluous-else-continue - "RET508", # superfluous-else-break + "RET501", # unnecessary-return-none + "RET502", # implicit-return-value + "RET503", # implicit-return + "RET505", # superfluous-else-return + "RET506", # superfluous-else-raise + "RET507", # superfluous-else-continue + "RET508", # superfluous-else-break "RSE102", # unnecessary-paren-on-raise-exception "RUF002", # ambiguous-unicode-character-docstring "RUF003", # ambiguous-unicode-character-comment @@ -301,6 +301,7 @@ ignore = [ [tool.ruff.lint.per-file-ignores] # See https://docs.astral.sh/ruff/settings/#lint_per-file-ignores +# "A005", # builtin-module-shadowing # "INT002", # f-string-in-get-text-func-call # "INT001", # format-in-get-text-func-call # "INT003", # printf-in-get-text-func-call @@ -326,6 +327,7 @@ ignore = [ "gui/wxpython/iclass/dialogs.py" = ["INT003"] "gui/wxpython/iclass/frame.py" = ["FLY002", "INT003"] "gui/wxpython/iclass/plots.py" = ["INT003"] +"gui/wxpython/iclass/statistics.py" = ["A005"] "gui/wxpython/image2target/ii2t_gis_set.py" = ["INT003"] "gui/wxpython/iscatt/controllers.py" = ["INT003"] "gui/wxpython/iscatt/dialogs.py" = ["INT003"] @@ -352,6 +354,7 @@ ignore = [ "gui/wxpython/web_services/dialogs.py" = ["INT003"] "gui/wxpython/web_services/widgets.py" = ["INT003"] "gui/wxpython/wxgui.py" = ["INT002"] +"gui/wxpython/wxplot/profile.py" = ["A005"] "lib/imagery/testsuite/test_imagery_sigsetfile.py" = ["FURB152"] "lib/init/grass.py" = ["INT003"] "python/grass/__init__.py" = ["PYI056"] @@ -364,11 +367,13 @@ ignore = [ "python/grass/jupyter/tests/reprojection_renderer_test.py" = ["PT013"] "python/grass/jupyter/testsuite/interactivemap_test.py" = ["PGH004"] "python/grass/jupyter/testsuite/map_test.py" = ["PGH004"] +"python/grass/pydispatch/signal.py" = ["A005"] "python/grass/pygrass/raster/category.py" = ["INT002"] "python/grass/pygrass/vector/__init__.py" = ["INT003"] "python/grass/pygrass/vector/geometry.py" = ["PYI024"] "python/grass/pygrass/vector/sql.py" = ["FLY002"] "python/grass/pygrass/vector/testsuite/test_table.py" = ["PLW0108"] +"python/grass/script/array.py" = ["A005"] "python/grass/script/raster.py" = ["INT003"] "python/grass/temporal/abstract_space_time_dataset.py" = ["INT003"] "python/grass/temporal/aggregation.py" = ["INT003"] From 6a3da775ab7f7feeab9a3ede6f8e35a18aa18d0e Mon Sep 17 00:00:00 2001 From: Martin Landa Date: Sun, 4 Aug 2024 14:58:00 +0200 Subject: [PATCH 083/471] docker: define GISBASE for Alpine (#4130) --- docker/alpine/Dockerfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docker/alpine/Dockerfile b/docker/alpine/Dockerfile index ea463f5906a..dfd840263e9 100644 --- a/docker/alpine/Dockerfile +++ b/docker/alpine/Dockerfile @@ -202,6 +202,9 @@ ENV GRASS_SKIP_MAPSET_OWNER_CHECK=1 \ COPY --from=build /usr/local/bin/grass /usr/local/bin/grass COPY --from=build /usr/local/grass* /usr/local/grass/ COPY --from=build /usr/lib/gdalplugins/*_GRASS.so /usr/lib/gdalplugins/ +# Set GISBASE +ENV GISBASE /usr/local/grass + # run simple LAZ test COPY docker/testdata/simple.laz /tmp/ COPY docker/testdata/test_grass_python.py docker/testdata/test_grass_session.py docker/alpine/grass_tests.sh /scripts/ From c53c938bac385a141670cf83d2511b17c5ce4baf Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 4 Aug 2024 19:45:14 +0200 Subject: [PATCH 084/471] CI(deps): Update msys2/setup-msys2 action to v2.24.1 (#4129) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/osgeo4w.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/osgeo4w.yml b/.github/workflows/osgeo4w.yml index a4ee546a4f3..5ad5a4723f9 100644 --- a/.github/workflows/osgeo4w.yml +++ b/.github/workflows/osgeo4w.yml @@ -30,7 +30,7 @@ jobs: git config --global core.autocrlf false git config --global core.eol lf - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - uses: msys2/setup-msys2@5df0ca6cbf14efcd08f8d5bd5e049a3cc8e07fd2 # v2.24.0 + - uses: msys2/setup-msys2@ddf331adaebd714795f1042345e6ca57bd66cea8 # v2.24.1 with: path-type: inherit location: D:\ From ba78f6fa659cdda86605d3221b5b00ce047d5f11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Sun, 4 Aug 2024 19:13:51 -0400 Subject: [PATCH 085/471] style: Fix unnecessary-placeholder (PIE790) (#4132) Ruff rule: https://docs.astral.sh/ruff/rules/unnecessary-placeholder/ Even a docstring is enough to not need a placeholder, as it is a statement. --- gui/wxpython/core/giface.py | 2 -- gui/wxpython/dbmgr/base.py | 1 - gui/wxpython/dbmgr/dialogs.py | 1 - gui/wxpython/gcp/manager.py | 2 -- gui/wxpython/gmodeler/dialogs.py | 2 -- gui/wxpython/gmodeler/model.py | 1 - gui/wxpython/gui_core/dialogs.py | 2 -- gui/wxpython/gui_core/forms.py | 1 - gui/wxpython/gui_core/widgets.py | 1 - gui/wxpython/image2target/ii2t_manager.py | 2 -- gui/wxpython/iscatt/iscatt_core.py | 1 - gui/wxpython/mapdisp/properties.py | 1 - gui/wxpython/mapswipe/frame.py | 1 - gui/wxpython/mapswipe/toolbars.py | 1 - gui/wxpython/modules/import_export.py | 3 --- gui/wxpython/nviz/mapwindow.py | 2 -- gui/wxpython/photo2image/ip2i_manager.py | 2 -- gui/wxpython/psmap/dialogs.py | 3 --- gui/wxpython/psmap/instructions.py | 1 - gui/wxpython/rlisetup/wizard.py | 1 - gui/wxpython/vdigit/dialogs.py | 1 - gui/wxpython/vdigit/preferences.py | 2 -- pyproject.toml | 1 - python/grass/pygrass/raster/history.py | 1 - python/grass/pygrass/vector/geometry.py | 3 --- python/grass/pygrass/vector/testsuite/test_geometry.py | 1 - python/grass/temporal/abstract_map_dataset.py | 1 - .../unittests_temporal_raster_algebra_spatial_topology.py | 1 - 28 files changed, 42 deletions(-) diff --git a/gui/wxpython/core/giface.py b/gui/wxpython/core/giface.py index f226d44eae9..864044593c0 100644 --- a/gui/wxpython/core/giface.py +++ b/gui/wxpython/core/giface.py @@ -49,8 +49,6 @@ class Layer: layer as used in lmgr. """ - pass - class LayerList: def GetSelectedLayers(self, checkedOnly=True): diff --git a/gui/wxpython/dbmgr/base.py b/gui/wxpython/dbmgr/base.py index 2a58ce13e88..231a2a10202 100644 --- a/gui/wxpython/dbmgr/base.py +++ b/gui/wxpython/dbmgr/base.py @@ -2962,7 +2962,6 @@ def UpdatePage(self): def OnLayerRightUp(self, event): """Layer description area, context menu""" - pass class TableListCtrl(ListCtrl, listmix.ListCtrlAutoWidthMixin): diff --git a/gui/wxpython/dbmgr/dialogs.py b/gui/wxpython/dbmgr/dialogs.py index c5b63d0fe13..757fde03cbf 100644 --- a/gui/wxpython/dbmgr/dialogs.py +++ b/gui/wxpython/dbmgr/dialogs.py @@ -189,7 +189,6 @@ def __init__( def OnSQLStatement(self, event): """Update SQL statement""" - pass def IsFound(self): """Check for status diff --git a/gui/wxpython/gcp/manager.py b/gui/wxpython/gcp/manager.py index afc3b239037..98323112972 100644 --- a/gui/wxpython/gcp/manager.py +++ b/gui/wxpython/gcp/manager.py @@ -1243,7 +1243,6 @@ def __del__(self): """Disable GCP manager mode""" # leaving the method here but was used only to delete gcpmanagement # from layer manager which is now not needed - pass def CreateGCPList(self): """Create GCP List Control""" @@ -2377,7 +2376,6 @@ def OnIdle(self, event): self.resize = False elif self.resize: event.RequestMore() - pass class GCPDisplay(FrameMixin, GCPPanel): diff --git a/gui/wxpython/gmodeler/dialogs.py b/gui/wxpython/gmodeler/dialogs.py index 67c8841dc45..0b4038b7b50 100644 --- a/gui/wxpython/gmodeler/dialogs.py +++ b/gui/wxpython/gmodeler/dialogs.py @@ -544,7 +544,6 @@ def __init__( def _layout(self): """Do layout (virtual method)""" - pass def GetCondition(self): """Get loop condition""" @@ -777,7 +776,6 @@ def OnBeginEdit(self, event): def OnEndEdit(self, event): """Finish editing of item""" - pass def GetListCtrl(self): """Used by ColumnSorterMixin""" diff --git a/gui/wxpython/gmodeler/model.py b/gui/wxpython/gmodeler/model.py index 4596e0b04eb..ebbcdb6381e 100644 --- a/gui/wxpython/gmodeler/model.py +++ b/gui/wxpython/gmodeler/model.py @@ -1390,7 +1390,6 @@ def _defineShape(self, width, height, x, y): :param width, height: dimension of the shape :param x, y: position of the shape """ - pass def IsIntermediate(self): """Checks if data item is intermediate""" diff --git a/gui/wxpython/gui_core/dialogs.py b/gui/wxpython/gui_core/dialogs.py index 05bb71ed7fa..c31939312a7 100644 --- a/gui/wxpython/gui_core/dialogs.py +++ b/gui/wxpython/gui_core/dialogs.py @@ -1471,13 +1471,11 @@ def _modelerDSeries(self): """Method used only by MapLayersDialogForModeler, for other subclasses does nothing. """ - pass def _addApplyButton(self): """Method used only by MapLayersDialog, for other subclasses does nothing. """ - pass def _fullyQualifiedNames(self): """Adds CheckBox which determines is fully qualified names are retuned.""" diff --git a/gui/wxpython/gui_core/forms.py b/gui/wxpython/gui_core/forms.py index 0c32a12c7f7..848e8e94c4e 100644 --- a/gui/wxpython/gui_core/forms.py +++ b/gui/wxpython/gui_core/forms.py @@ -2852,7 +2852,6 @@ def OnUpdateValues(self, event=None): needed. It's a hook, actually. Beware of what is 'self' in the method def, though. It will be called with no arguments. """ - pass def OnCheckBoxMulti(self, event): """Fill the values as a ','-separated string according to diff --git a/gui/wxpython/gui_core/widgets.py b/gui/wxpython/gui_core/widgets.py index 06afe3ab8d9..8a55144a08e 100644 --- a/gui/wxpython/gui_core/widgets.py +++ b/gui/wxpython/gui_core/widgets.py @@ -348,7 +348,6 @@ def RemoveNBPage(self, page): def SetPageImage(self, page, index): """Does nothing because we don't want images for this style""" - pass def __getattr__(self, name): return getattr(self.controller, name) diff --git a/gui/wxpython/image2target/ii2t_manager.py b/gui/wxpython/image2target/ii2t_manager.py index b4bc3d60f97..968e6c50b7d 100644 --- a/gui/wxpython/image2target/ii2t_manager.py +++ b/gui/wxpython/image2target/ii2t_manager.py @@ -1228,7 +1228,6 @@ def __del__(self): """Disable GCP manager mode""" # leaving the method here but was used only to delete gcpmanagement # from layer manager which is now not needed - pass def CreateGCPList(self): """Create GCP List Control""" @@ -2316,7 +2315,6 @@ def OnIdle(self, event): self.resize = False elif self.resize: event.RequestMore() - pass class GCPDisplay(FrameMixin, GCPPanel): diff --git a/gui/wxpython/iscatt/iscatt_core.py b/gui/wxpython/iscatt/iscatt_core.py index 2eaa463940d..97da83415e8 100644 --- a/gui/wxpython/iscatt/iscatt_core.py +++ b/gui/wxpython/iscatt/iscatt_core.py @@ -832,7 +832,6 @@ def GetRasterInfo(rast): if k == "datatype": if v != "CELL": return None - pass elif k in {"rows", "cols", "cells", "min", "max"}: v = int(v) else: diff --git a/gui/wxpython/mapdisp/properties.py b/gui/wxpython/mapdisp/properties.py index 92b9b38474b..5fd9fdfe031 100644 --- a/gui/wxpython/mapdisp/properties.py +++ b/gui/wxpython/mapdisp/properties.py @@ -44,7 +44,6 @@ def mapWindowProperty(self, value): def mapWindowPropertyChanged(self): """Returns signal from MapWindowProperties.""" - pass def _setValue(self, value): self.widget.SetValue(value) diff --git a/gui/wxpython/mapswipe/frame.py b/gui/wxpython/mapswipe/frame.py index 6552ff044f3..a7dd0df8f56 100644 --- a/gui/wxpython/mapswipe/frame.py +++ b/gui/wxpython/mapswipe/frame.py @@ -659,7 +659,6 @@ def OnAddText(self, event): So far not implemented. """ - pass def SetViewMode(self, mode): """Sets view mode. diff --git a/gui/wxpython/mapswipe/toolbars.py b/gui/wxpython/mapswipe/toolbars.py index 135a04567ae..943eddaa1ec 100644 --- a/gui/wxpython/mapswipe/toolbars.py +++ b/gui/wxpython/mapswipe/toolbars.py @@ -123,7 +123,6 @@ def SetActiveMap(self, index): """Set currently selected map. Unused, needed because of DoubleMapPanel API. """ - pass class SwipeMainToolbar(BaseToolbar): diff --git a/gui/wxpython/modules/import_export.py b/gui/wxpython/modules/import_export.py index c7b8897e783..15d4f76b586 100644 --- a/gui/wxpython/modules/import_export.py +++ b/gui/wxpython/modules/import_export.py @@ -274,7 +274,6 @@ def OnClose(self, event=None): def OnRun(self, event): """Import/Link data (each layes as separate vector map)""" - pass def OnCheckOverwrite(self, event): """Check/uncheck overwrite checkbox widget""" @@ -344,11 +343,9 @@ def OnAbort(self, event): .. todo:: not yet implemented """ - pass def OnCmdDone(self, event): """Do what has to be done after importing""" - pass def _getLayersToReprojetion(self, projMatch_idx, grassName_idx): """If there are layers with different projection from loation projection, diff --git a/gui/wxpython/nviz/mapwindow.py b/gui/wxpython/nviz/mapwindow.py index bbd6bcaa3bb..f4b93511714 100644 --- a/gui/wxpython/nviz/mapwindow.py +++ b/gui/wxpython/nviz/mapwindow.py @@ -2751,8 +2751,6 @@ def ZoomToMap(self, layers): def DisactivateWin(self): """Use when the class instance is hidden in MapFrame.""" - pass def ActivateWin(self): """Used when the class instance is activated in MapFrame.""" - pass diff --git a/gui/wxpython/photo2image/ip2i_manager.py b/gui/wxpython/photo2image/ip2i_manager.py index b4d24d19430..042053b65e2 100644 --- a/gui/wxpython/photo2image/ip2i_manager.py +++ b/gui/wxpython/photo2image/ip2i_manager.py @@ -596,7 +596,6 @@ def __del__(self): """Disable GCP manager mode""" # leaving the method here but was used only to delete gcpmanagement # from layer manager which is now not needed - pass def CreateGCPList(self): """Create GCP List Control""" @@ -1602,7 +1601,6 @@ def OnIdle(self, event): self.resize = False elif self.resize: event.RequestMore() - pass class GCPDisplay(FrameMixin, GCPPanel): diff --git a/gui/wxpython/psmap/dialogs.py b/gui/wxpython/psmap/dialogs.py index 740678de3cb..216782037be 100644 --- a/gui/wxpython/psmap/dialogs.py +++ b/gui/wxpython/psmap/dialogs.py @@ -2103,7 +2103,6 @@ def OnApply(self, event): def updateDialog(self): """Update information (not used)""" - pass # if "map" in self.parent.openDialogs: @@ -2149,7 +2148,6 @@ def OnApply(self, event): def updateDialog(self): """Update information (not used)""" - pass class VPropertiesDialog(Dialog): @@ -6729,7 +6727,6 @@ def update(self): def updateDialog(self): """Update text coordinates, after moving""" - pass class LabelsDialog(PsmapDialog): diff --git a/gui/wxpython/psmap/instructions.py b/gui/wxpython/psmap/instructions.py index f52bad96069..5a61a4d4bf2 100644 --- a/gui/wxpython/psmap/instructions.py +++ b/gui/wxpython/psmap/instructions.py @@ -573,7 +573,6 @@ def SetInstruction(self, instruction): def Read(self, instruction, text, **kwargs): """Read instruction and save them""" - pass def PercentToReal(self, e, n): """Converts text coordinates from percent of region to map coordinates""" diff --git a/gui/wxpython/rlisetup/wizard.py b/gui/wxpython/rlisetup/wizard.py index 12888948ec3..e485d3596ba 100644 --- a/gui/wxpython/rlisetup/wizard.py +++ b/gui/wxpython/rlisetup/wizard.py @@ -1780,7 +1780,6 @@ def OnEnterPage(self, event): def OnExitPage(self, event=None): """Function during exiting""" - pass # if event.GetDirection(): # self.SetNext(self.parent.samplingareapage) diff --git a/gui/wxpython/vdigit/dialogs.py b/gui/wxpython/vdigit/dialogs.py index 395f92a4102..be775c5e28b 100644 --- a/gui/wxpython/vdigit/dialogs.py +++ b/gui/wxpython/vdigit/dialogs.py @@ -774,4 +774,3 @@ def LoadData(self, data): def OnCheckItem(self, index, flag): """Mapset checked/unchecked""" - pass diff --git a/gui/wxpython/vdigit/preferences.py b/gui/wxpython/vdigit/preferences.py index b825748a025..e565c60dfe9 100644 --- a/gui/wxpython/vdigit/preferences.py +++ b/gui/wxpython/vdigit/preferences.py @@ -797,8 +797,6 @@ def OnChangeLayer(self, event): def OnChangeAddRecord(self, event): """Checkbox 'Add new record' status changed""" - pass - # self.category.SetValue(self.digit.SetCategory()) def OnChangeSnappingValue(self, event): """Change snapping value - update static text""" diff --git a/pyproject.toml b/pyproject.toml index 18baa5eebac..c0b9aea65fa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -158,7 +158,6 @@ ignore = [ "PERF401", # manual-list-comprehension "PERF402", # manual-list-copy "PERF403", # manual-dict-comprehension - "PIE790", # unnecessary-placeholder "PIE794", # duplicate-class-field-definition "PIE808", # unnecessary-range-start "PIE810", # multiple-starts-ends-with diff --git a/python/grass/pygrass/raster/history.py b/python/grass/pygrass/raster/history.py index 5041c9e55d8..5ec855cf1bb 100644 --- a/python/grass/pygrass/raster/history.py +++ b/python/grass/pygrass/raster/history.py @@ -57,7 +57,6 @@ def __repr__(self): def __del__(self): """Rast_free_history""" - pass def __eq__(self, hist): for attr in self.attrs: diff --git a/python/grass/pygrass/vector/geometry.py b/python/grass/pygrass/vector/geometry.py index 591ebf38978..99d7e2249cb 100644 --- a/python/grass/pygrass/vector/geometry.py +++ b/python/grass/pygrass/vector/geometry.py @@ -79,7 +79,6 @@ def read_WKT(string): def read_WKB(buff): """Read the binary buffer and return a geometry object""" - pass def intersects(lineA, lineB, with_z=False): @@ -940,7 +939,6 @@ def first_cat(self): """ # TODO: add this method. # libvect.Vect_get_line_cat(self.c_mapinfo, self.id, self.field) - pass def pop(self, indx): """Return the point in the index position and remove from the Line. @@ -1807,7 +1805,6 @@ def get_first_cat(self): ..warning: Not implemented """ - pass @mapinfo_must_be_set def contains_point(self, point, bbox=None): diff --git a/python/grass/pygrass/vector/testsuite/test_geometry.py b/python/grass/pygrass/vector/testsuite/test_geometry.py index c3ad5b6cd63..8680251d8c6 100644 --- a/python/grass/pygrass/vector/testsuite/test_geometry.py +++ b/python/grass/pygrass/vector/testsuite/test_geometry.py @@ -98,7 +98,6 @@ def test_repr(self): def test_buffer(self): """Test buffer method""" # TODO: verify if the buffer depends from the mapset's projection - pass class LineTestCase(TestCase): diff --git a/python/grass/temporal/abstract_map_dataset.py b/python/grass/temporal/abstract_map_dataset.py index aa2472a9ee9..f58bcb82d66 100644 --- a/python/grass/temporal/abstract_map_dataset.py +++ b/python/grass/temporal/abstract_map_dataset.py @@ -1240,7 +1240,6 @@ def read_semantic_label_from_grass(self): Currently only implemented in RasterDataset. Otherwise silently pass. """ - pass def set_semantic_label(self, semantic_label): """Set semantic label identifier diff --git a/python/grass/temporal/testsuite/unittests_temporal_raster_algebra_spatial_topology.py b/python/grass/temporal/testsuite/unittests_temporal_raster_algebra_spatial_topology.py index fc3bb6dfa75..481eae5d59d 100644 --- a/python/grass/temporal/testsuite/unittests_temporal_raster_algebra_spatial_topology.py +++ b/python/grass/temporal/testsuite/unittests_temporal_raster_algebra_spatial_topology.py @@ -91,7 +91,6 @@ def setUpClass(cls): def tearDown(self): self.runModule("t.remove", flags="rf", inputs="R", quiet=True) - pass @classmethod def tearDownClass(cls): From 186f3196b9a80c59a28a4c7876e4751dc0f9baed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Sun, 4 Aug 2024 19:14:54 -0400 Subject: [PATCH 086/471] style: Fix useless-import-alias (PLC0414) (#4133) --- gui/wxpython/gui_core/widgets.py | 2 +- gui/wxpython/modules/colorrules.py | 2 +- pyproject.toml | 1 - scripts/v.pack/v.pack.py | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/gui/wxpython/gui_core/widgets.py b/gui/wxpython/gui_core/widgets.py index 8a55144a08e..45393f2eb1d 100644 --- a/gui/wxpython/gui_core/widgets.py +++ b/gui/wxpython/gui_core/widgets.py @@ -80,7 +80,7 @@ from wx.lib.buttons import GenBitmapTextButton as BitmapTextButton if wxPythonPhoenix: - from wx import Validator as Validator + from wx import Validator else: from wx import PyValidator as Validator diff --git a/gui/wxpython/modules/colorrules.py b/gui/wxpython/modules/colorrules.py index 0772dc7f9d5..b5b1047382e 100644 --- a/gui/wxpython/modules/colorrules.py +++ b/gui/wxpython/modules/colorrules.py @@ -41,7 +41,7 @@ from gui_core.gselect import Select, LayerSelect, ColumnSelect, VectorDBInfo from core.render import Map from gui_core.forms import GUI -from core.debug import Debug as Debug +from core.debug import Debug from gui_core.widgets import ColorTablesComboBox from gui_core.wrap import ( SpinCtrl, diff --git a/pyproject.toml b/pyproject.toml index c0b9aea65fa..99ab7249e23 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -162,7 +162,6 @@ ignore = [ "PIE808", # unnecessary-range-start "PIE810", # multiple-starts-ends-with "PLC0206", # dict-index-missing-items - "PLC0414", # useless-import-alias "PLC0415", # import-outside-top-level "PLC1901", # compare-to-empty-string "PLC2701", # import-private-name diff --git a/scripts/v.pack/v.pack.py b/scripts/v.pack/v.pack.py index d25704b9925..cd8d96b86cd 100755 --- a/scripts/v.pack/v.pack.py +++ b/scripts/v.pack/v.pack.py @@ -40,7 +40,7 @@ from grass.script.utils import try_rmdir, try_remove from grass.script import core as grass -from grass.script import vector as vector +from grass.script import vector def cleanup(): From 1d98e3c5254ef33b62f3cb1c9ef6be580126aff2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 5 Aug 2024 00:13:44 +0000 Subject: [PATCH 087/471] CI(deps): Update flake8 to v7.1.1 (#4135) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/python-code-quality.yml | 2 +- .pre-commit-config.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index bd8fc83890b..77ae4a87419 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -30,7 +30,7 @@ jobs: # renovate: datasource=pypi depName=black BLACK_VERSION: "24.8.0" # renovate: datasource=pypi depName=flake8 - FLAKE8_VERSION: "7.1.0" + FLAKE8_VERSION: "7.1.1" # renovate: datasource=pypi depName=pylint PYLINT_VERSION: "2.12.2" # renovate: datasource=pypi depName=bandit diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 638a1e4787d..3730bce903f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -57,7 +57,7 @@ repos: python/libgrass_interface_generator/ ) - repo: https://github.com/pycqa/flake8 - rev: 7.1.0 + rev: 7.1.1 hooks: - id: flake8 exclude: | From afdb4dbe829cb31a729db6d0b2aeec9706b39d95 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 4 Aug 2024 22:34:38 -0400 Subject: [PATCH 088/471] CI(deps): Lock file maintenance (#4136) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> From 0496add5629514c9dc0aaec58a70ff18bbe43375 Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Mon, 5 Aug 2024 02:27:16 -0400 Subject: [PATCH 089/471] g.findfile: fix potential buffer overflow (#4056) --- general/g.findfile/main.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/general/g.findfile/main.c b/general/g.findfile/main.c index 1871c5da6e1..ef75cb5ab92 100644 --- a/general/g.findfile/main.c +++ b/general/g.findfile/main.c @@ -33,6 +33,7 @@ int main(int argc, char *argv[]) struct Option *mapset_opt; struct Option *file_opt; struct Flag *n_flag, *l_flag; + size_t len; module = G_define_module(); G_add_keyword(_("general")); @@ -104,8 +105,12 @@ int main(int argc, char *argv[]) strcpy(name, file_opt->answer); G_free_tokens(map_mapset); } - else - strcpy(name, file_opt->answer); + else { + len = G_strlcpy(name, file_opt->answer, sizeof(name)); + if (len >= sizeof(name)) { + G_fatal_error(_("Name <%s> is too long"), file_opt->answer); + } + } mapset = G_find_file2(elem_opt->answer, name, search_mapset); if (mapset) { From eb9bda64ded84e01d7187e6918618e3546f86411 Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Mon, 5 Aug 2024 05:07:04 -0400 Subject: [PATCH 090/471] i.smap: fix sizeof not portable issue (#4060) --- imagery/i.smap/multialloc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imagery/i.smap/multialloc.c b/imagery/i.smap/multialloc.c index a4472f74db5..e3be7693990 100644 --- a/imagery/i.smap/multialloc.c +++ b/imagery/i.smap/multialloc.c @@ -37,7 +37,7 @@ char *multialloc(size_t s, /* individual array element size */ for (i = 0; i < d - 1; i++, q++) { /* for each of the dimensions * but the last */ max *= (*q); - r[0] = (char *)G_malloc(max * sizeof(char **)); + r[0] = (char *)G_malloc(max * sizeof(char *)); r = (char **)r[0]; /* step through to beginning of next * dimension array */ } From ba3476d4bc5aea8ef228e6543c4f7deb684988fa Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Mon, 5 Aug 2024 05:22:31 -0400 Subject: [PATCH 091/471] i.aster.toar: fix potential buffer overflows (#4079) --- imagery/i.aster.toar/main.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/imagery/i.aster.toar/main.c b/imagery/i.aster.toar/main.c index 5dc94a51d1e..8536966882b 100644 --- a/imagery/i.aster.toar/main.c +++ b/imagery/i.aster.toar/main.c @@ -165,21 +165,21 @@ int main(int argc, char *argv[]) /*Prepare the output file names */ /********************/ - sprintf(result0, "%s%s", result, ".1"); - sprintf(result1, "%s%s", result, ".2"); - sprintf(result2, "%s%s", result, ".3N"); - sprintf(result3, "%s%s", result, ".3B"); - sprintf(result4, "%s%s", result, ".4"); - sprintf(result5, "%s%s", result, ".5"); - sprintf(result6, "%s%s", result, ".6"); - sprintf(result7, "%s%s", result, ".7"); - sprintf(result8, "%s%s", result, ".8"); - sprintf(result9, "%s%s", result, ".9"); - sprintf(result10, "%s%s", result, ".10"); - sprintf(result11, "%s%s", result, ".11"); - sprintf(result12, "%s%s", result, ".12"); - sprintf(result13, "%s%s", result, ".13"); - sprintf(result14, "%s%s", result, ".14"); + snprintf(result0, sizeof(result0), "%s%s", result, ".1"); + snprintf(result1, sizeof(result1), "%s%s", result, ".2"); + snprintf(result2, sizeof(result2), "%s%s", result, ".3N"); + snprintf(result3, sizeof(result3), "%s%s", result, ".3B"); + snprintf(result4, sizeof(result4), "%s%s", result, ".4"); + snprintf(result5, sizeof(result5), "%s%s", result, ".5"); + snprintf(result6, sizeof(result6), "%s%s", result, ".6"); + snprintf(result7, sizeof(result7), "%s%s", result, ".7"); + snprintf(result8, sizeof(result8), "%s%s", result, ".8"); + snprintf(result9, sizeof(result9), "%s%s", result, ".9"); + snprintf(result10, sizeof(result10), "%s%s", result, ".10"); + snprintf(result11, sizeof(result11), "%s%s", result, ".11"); + snprintf(result12, sizeof(result12), "%s%s", result, ".12"); + snprintf(result13, sizeof(result13), "%s%s", result, ".13"); + snprintf(result14, sizeof(result14), "%s%s", result, ".14"); /********************/ /*Prepare radiance boundaries */ From f9f7482911cfccb0b5c2cf9e117c84bbd8837855 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 5 Aug 2024 06:27:43 -0400 Subject: [PATCH 092/471] CI(deps): Lock file maintenance (#4140) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> From e87f788b817f4f99146ce1e613318c55f7fef6b3 Mon Sep 17 00:00:00 2001 From: Chung-Yuan Liang <77927944+cyliang368@users.noreply.github.com> Date: Mon, 5 Aug 2024 18:55:45 -0400 Subject: [PATCH 093/471] lib/gis: Add a helper function to determine the number of threads for OpenMP (#3929) * determine the number of threads for OpenMP in opt parser * add OpenMP library paths * solve segfault when nprocs option is not specified in the command * only change nprocs for C module * update the description of the function, which does not return any value * add helper function, left parser unchanged * return threads rather than change the answer of nprocs * modify code based on review comments * remove threads > logic cores check, update based on review comments --- include/grass/defs/gis.h | 3 +++ lib/gis/Makefile | 5 +++-- lib/gis/omp_threads.c | 45 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 lib/gis/omp_threads.c diff --git a/include/grass/defs/gis.h b/include/grass/defs/gis.h index 1eacd9ed933..4125a3aeed3 100644 --- a/include/grass/defs/gis.h +++ b/include/grass/defs/gis.h @@ -548,6 +548,9 @@ int G_name_is_fully_qualified(const char *, char *, char *); char *G_fully_qualified_name(const char *, const char *); int G_unqualified_name(const char *, const char *, char *, char *); +/* omp_threads.c */ +int G_set_omp_num_threads(struct Option *); + /* open.c */ int G_open_new(const char *, const char *); int G_open_old(const char *, const char *, const char *); diff --git a/lib/gis/Makefile b/lib/gis/Makefile index a1579dbf862..90b39f374a4 100644 --- a/lib/gis/Makefile +++ b/lib/gis/Makefile @@ -2,8 +2,9 @@ MODULE_TOPDIR = ../.. LIB = GIS -EXTRA_INC = $(ZLIBINCPATH) $(BZIP2INCPATH) $(ZSTDINCPATH) $(PTHREADINCPATH) $(REGEXINCPATH) -EXTRA_CFLAGS = -DGRASS_VERSION_DATE=\"'$(GRASS_VERSION_DATE)'\" +LIBES = $(OPENMP_LIBPATH) $(OPENMP_LIB) +EXTRA_INC = $(ZLIBINCPATH) $(BZIP2INCPATH) $(ZSTDINCPATH) $(PTHREADINCPATH) $(REGEXINCPATH) $(OPENMP_INCPATH) +EXTRA_CFLAGS = $(OPENMP_CFLAGS) -DGRASS_VERSION_DATE=\"'$(GRASS_VERSION_DATE)'\" PROJSRC = ellipse.table ellipse.table.solar.system datum.table \ datumtransform.table FIPS.code state27 state83 projections diff --git a/lib/gis/omp_threads.c b/lib/gis/omp_threads.c new file mode 100644 index 00000000000..f5169d8624e --- /dev/null +++ b/lib/gis/omp_threads.c @@ -0,0 +1,45 @@ +#if defined(_OPENMP) +#include +#endif + +#include +#include +#include +#include +#include + +/*! \brief Set the number of threads for OpenMP + The intended usage is at the beginning of a C tool when parameters are + processed, namely the G_OPT_M_NPROCS standard option. + + \param opt A nprocs Option struct to specify the number of threads + \return the number of threads set up for OpenMP parallel computing +*/ + +int G_set_omp_num_threads(struct Option *opt) +{ + /* make sure Option is not null */ + if (opt == NULL) + G_fatal_error(_("Option is NULL.")); + else if (opt->key == NULL) + G_fatal_error(_("Option key is NULL.")); + + int threads = atoi(opt->answer); +#if defined(_OPENMP) + int num_logic_procs = omp_get_num_procs(); + if (threads < 1) { + threads += num_logic_procs; + threads = (threads < 1) ? 1 : threads; + } + omp_set_num_threads(threads); + G_verbose_message(_("%d threads are set up for parallel computing."), + threads); +#else + if (threads != 1) { + G_warning(_("GRASS GIS is not compiled with OpenMP support, parallel " + "computation is disabled. Only one thread will be used.")); + threads = 1; + } +#endif + return threads; +} From 57db287d6fece9481e57bfcd7485233ba8f5c5f4 Mon Sep 17 00:00:00 2001 From: Riya Saxena <77328768+29riyasaxena@users.noreply.github.com> Date: Tue, 6 Aug 2024 04:30:01 +0530 Subject: [PATCH 094/471] grass.jupyter: Add Parallelization to TimeSeriesMap and SeriesMap (#4097) * Add Parallelization to TimeSeriesMap * Specify nprocs * Correct NPROCS * Modify SeriesMap * Modify BaseSeriesMap * remove unused attribute --------- Co-authored-by: Anna Petrasova --- python/grass/jupyter/baseseriesmap.py | 12 +++++++- python/grass/jupyter/seriesmap.py | 40 ++++++++++++------------- python/grass/jupyter/timeseriesmap.py | 42 ++++++++++----------------- python/grass/jupyter/utils.py | 16 ++++++++++ 4 files changed, 60 insertions(+), 50 deletions(-) diff --git a/python/grass/jupyter/baseseriesmap.py b/python/grass/jupyter/baseseriesmap.py index db915297e9c..e75a0ea1637 100644 --- a/python/grass/jupyter/baseseriesmap.py +++ b/python/grass/jupyter/baseseriesmap.py @@ -5,10 +5,12 @@ import tempfile import weakref import shutil +import multiprocessing import grass.script as gs from .map import Map +from .utils import get_number_of_cores class BaseSeriesMap: @@ -87,7 +89,7 @@ def _render_baselayers(self, img): for grass_module, kwargs in self._base_layer_calls: img.run(grass_module, **kwargs) - def _render(self): + def _render(self, tasks): """ Renders the base image for the dataset. @@ -121,6 +123,14 @@ def _render(self): self._render_baselayers(img) # Render layers in respective classes + cores = get_number_of_cores(len(tasks), env=self._env) + with multiprocessing.Pool(processes=cores) as pool: + results = pool.starmap(self._render_worker, tasks) + + for i, filename in results: + self._base_filename_dict[i] = filename + + self._layers_rendered = True def show(self, slider_width=None): """Create interactive timeline slider. diff --git a/python/grass/jupyter/seriesmap.py b/python/grass/jupyter/seriesmap.py index 9e470babb70..86d46018864 100644 --- a/python/grass/jupyter/seriesmap.py +++ b/python/grass/jupyter/seriesmap.py @@ -135,39 +135,35 @@ def add_names(self, names): self._labels = names self._indices = list(range(len(self._labels))) + def _render_worker(self, i): + """Function to render a single layer.""" + filename = os.path.join(self._tmpdir.name, f"{i}.png") + shutil.copyfile(self.base_file, filename) + img = Map( + width=self._width, + height=self._height, + filename=filename, + use_region=True, + env=self._env, + read_file=True, + ) + for grass_module, kwargs in self._base_calls[i]: + img.run(grass_module, **kwargs) + return i, filename + def render(self): """Renders image for each raster in series. Save PNGs to temporary directory. Must be run before creating a visualization (i.e. show or save). """ - self._render() if not self._baseseries_added: raise RuntimeError( "Cannot render series since none has been added." "Use SeriesMap.add_rasters() or SeriesMap.add_vectors()" ) - - # Render each layer - for i in range(self.baseseries): - # Create file - filename = os.path.join(self._tmpdir.name, f"{i}.png") - # Copying the base_file ensures that previous results are overwritten - shutil.copyfile(self.base_file, filename) - self._base_filename_dict[i] = filename - # Render image - img = Map( - width=self._width, - height=self._height, - filename=filename, - use_region=True, - env=self._env, - read_file=True, - ) - for grass_module, kwargs in self._base_calls[i]: - img.run(grass_module, **kwargs) - - self._layers_rendered = True + tasks = [(i,) for i in range(self.baseseries)] + self._render(tasks) def save( self, diff --git a/python/grass/jupyter/timeseriesmap.py b/python/grass/jupyter/timeseriesmap.py index 2dc0b4ede56..0c7f11a562f 100644 --- a/python/grass/jupyter/timeseriesmap.py +++ b/python/grass/jupyter/timeseriesmap.py @@ -281,48 +281,36 @@ def _render_layer(self, layer, filename): if self._legend: self._render_legend(img) - def render(self): - """Renders image for each time-step in space-time dataset. - - Save PNGs to temporary directory. Must be run before creating a visualization - (i.e. show or save). Can be time-consuming to run with large - space-time datasets. - """ + def _render_worker(self, date, layer, filename): + """Function to render a single layer.""" + shutil.copyfile(self.base_file, filename) + if layer == "None": + self._render_blank_layer(filename) + else: + self._render_layer(layer, filename) + return date, filename - self._render() + def render(self): + """Renders image for each time-step in space-time dataset.""" if not self._baseseries_added: raise RuntimeError( "Cannot render space time dataset since none has been added." - "Use TimeSeriesMap.add_raster_series() or " + " Use TimeSeriesMap.add_raster_series() or " "TimeSeriesMap.add_vector_series() to add dataset" ) # Create name for empty layers - # Random name needed to avoid potential conflict with layer names - # A new random_name_none is created each time the render function is run, - # and any existing random_name_none file will be ignored random_name_none = gs.append_random("none", 8) + ".png" - # Render each layer + # Prepare tasks with tuples + tasks = [] for date, layer in self._date_layer_dict.items(): if layer == "None": - # Create file filename = os.path.join(self._tmpdir.name, random_name_none) - self._base_filename_dict[date] = filename - # Render blank layer if it hasn't been done already - if not os.path.exists(filename): - shutil.copyfile(self.base_file, filename) - self._render_blank_layer(filename) else: - # Create file filename = os.path.join(self._tmpdir.name, f"{layer}.png") - # Copying the base_file ensures that previous results are overwritten - shutil.copyfile(self.base_file, filename) - self._base_filename_dict[date] = filename - # Render image - self._render_layer(layer, filename) - - self._layers_rendered = True + tasks.append((date, layer, filename)) + self._render(tasks) def save( self, diff --git a/python/grass/jupyter/utils.py b/python/grass/jupyter/utils.py index 0556158896f..64ffb51b850 100644 --- a/python/grass/jupyter/utils.py +++ b/python/grass/jupyter/utils.py @@ -10,6 +10,9 @@ # for details. """Utility functions warpping existing processes in a suitable way""" +import os +import multiprocessing + from pathlib import Path import grass.script as gs @@ -201,6 +204,19 @@ def get_rendering_size(region, width, height, default_width=600, default_height= return (default_width, round(default_width * region_height / region_width)) +def get_number_of_cores(requested, env=None): + """Get the number of cores to use for multiprocessing.""" + nprocs = gs.gisenv(env).get("NPROCS") + if nprocs is not None: + return int(nprocs) + + try: + num_cores = len(os.sched_getaffinity(0)) + except AttributeError: + num_cores = multiprocessing.cpu_count() + return min(requested, max(1, num_cores - 1)) + + def get_region_bounds_latlon(): """Gets the current computational region bounds in latlon.""" region = gs.parse_command("g.region", flags="gbp") From 32de21bad8a31c6c22646395ae4bf532666b096e Mon Sep 17 00:00:00 2001 From: Kriti Birda <164247895+kritibirda26@users.noreply.github.com> Date: Tue, 6 Aug 2024 04:35:58 +0530 Subject: [PATCH 095/471] r.report: add JSON support (#3935) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * r.report: add json support * address PR feedback * fix segfault * address PR feedback * update tests to use CI dataset * change created field to iso8601 datetime format * fix created field test * fix map description field * Apply suggestions from code review Co-authored-by: Edouard Choinière <27212526+echoix@users.noreply.github.com> * update documentation * Update raster/r.report/format.c Co-authored-by: Nicklas Larsson --------- Co-authored-by: Edouard Choinière <27212526+echoix@users.noreply.github.com> Co-authored-by: Nicklas Larsson --- raster/r.report/Makefile | 2 +- raster/r.report/format.c | 90 +++ raster/r.report/global.h | 11 + raster/r.report/main.c | 2 + raster/r.report/parse.c | 11 + raster/r.report/prt_json.c | 205 ++++++ raster/r.report/prt_report.c | 73 +- raster/r.report/r.report.html | 738 +++++++++++++++++++++ raster/r.report/report.c | 29 +- raster/r.report/testsuite/test_r_report.py | 609 +++++++++++++++++ 10 files changed, 1686 insertions(+), 84 deletions(-) create mode 100644 raster/r.report/prt_json.c diff --git a/raster/r.report/Makefile b/raster/r.report/Makefile index 34d6942690f..0bab2b40c4a 100644 --- a/raster/r.report/Makefile +++ b/raster/r.report/Makefile @@ -2,7 +2,7 @@ MODULE_TOPDIR = ../.. PGM = r.report -LIBES = $(RASTERLIB) $(GISLIB) +LIBES = $(RASTERLIB) $(GISLIB) $(PARSONLIB) DEPENDENCIES = $(RASTERDEP) $(GISDEP) include $(MODULE_TOPDIR)/include/Make/Module.make diff --git a/raster/r.report/format.c b/raster/r.report/format.c index 8319fe747ed..41cc3ca6991 100644 --- a/raster/r.report/format.c +++ b/raster/r.report/format.c @@ -73,3 +73,93 @@ int format_double(double v, char *buf, int n, int dp) return 0; } + +void compute_unit_format(int unit1, int unit2, enum OutputFormat format) +{ + int i, ns, len; + char num[100]; + int need_format; + + /* examine units, determine output format */ + for (i = unit1; i <= unit2; i++) { + if (format == PLAIN) { + need_format = 1; + } + else { + need_format = 0; + } + unit[i].label[0] = ""; + unit[i].label[1] = ""; + + switch (unit[i].type) { + case CELL_COUNTS: + unit[i].label[0] = " cell"; + unit[i].label[1] = "count"; + + if (need_format) { + need_format = 0; + unit[i].len = 5; + ns = 0; + snprintf(num, sizeof(num), "%ld", count_sum(&ns, -1)); + len = strlen(num); + if (len > unit[i].len) + unit[i].len = len; + } + break; + + case PERCENT_COVER: + unit[i].label[0] = " % "; + unit[i].label[1] = "cover"; + + if (need_format) { + need_format = 0; + unit[i].dp = 2; + unit[i].len = 6; + unit[i].eformat = 0; + } + break; + + case SQ_METERS: + unit[i].label[0] = "square"; + unit[i].label[1] = "meters"; + unit[i].factor = 1.0; + break; + + case SQ_KILOMETERS: + unit[i].label[0] = " square "; + unit[i].label[1] = "kilometers"; + unit[i].factor = 1.0e-6; + break; + + case ACRES: + unit[i].label[0] = ""; + unit[i].label[1] = "acres"; + unit[i].factor = 2.47105381467165e-4; /* 640 acres in a sq mile */ + break; + + case HECTARES: + unit[i].label[0] = ""; + unit[i].label[1] = "hectares"; + unit[i].factor = 1.0e-4; + break; + + case SQ_MILES: + unit[i].label[0] = "square"; + unit[i].label[1] = " miles"; + unit[i].factor = 3.86102158542446e-7; /* 1 / ( (0.0254m/in * 12in/ft + * 5280ft/mi)^2 ) */ + break; + + default: + G_fatal_error("Unit %d not yet supported", unit[i].type); + } + if (need_format) { + unit[i].dp = 6; + unit[i].len = 10; + unit[i].eformat = 0; + ns = 0; + format_parms(area_sum(&ns, -1) * unit[i].factor, &unit[i].len, + &unit[i].dp, &(unit[i].eformat), e_format); + } + } +} diff --git a/raster/r.report/global.h b/raster/r.report/global.h index 1b23ab6b813..a05ac1ae1d5 100644 --- a/raster/r.report/global.h +++ b/raster/r.report/global.h @@ -6,6 +6,7 @@ #endif #include +#include #define SORT_DEFAULT 0 #define SORT_ASC 1 @@ -52,6 +53,8 @@ extern int nunits; #define DEFAULT_PAGE_LENGTH "0" #define DEFAULT_PAGE_WIDTH "79" +enum OutputFormat { PLAIN, JSON }; + extern int page_width; extern int page_length; extern int masking; @@ -67,6 +70,7 @@ extern char *stats_file; extern char *no_data_str; extern int stats_flag; extern int nsteps, cat_ranges, as_int; +extern enum OutputFormat format; extern int *is_fp; extern DCELL *DMAX, *DMIN; @@ -81,6 +85,7 @@ extern struct Categories *labels; int format_parms(double, int *, int *, int *, int); int scient_format(double, char *, int, int); int format_double(double, char *, int, int); +void compute_unit_format(int, int, enum OutputFormat); /* header.c */ int header(int, int); @@ -112,6 +117,12 @@ char *construct_cat_label(int, CELL); /* prt_unit.c */ int print_unit(int, int, int); +/* prt_json.c */ +JSON_Value *make_units(int, int); +JSON_Value *make_category(int, int, JSON_Value *); +JSON_Value *make_categories(int, int, int); +void print_json(); + /* report.c */ int report(void); diff --git a/raster/r.report/main.c b/raster/r.report/main.c index bb04db163c2..5b13e23411e 100644 --- a/raster/r.report/main.c +++ b/raster/r.report/main.c @@ -52,6 +52,8 @@ int maskfd; CELL *mask; CELL NULL_CELL; +enum OutputFormat format; + char fs[2]; struct Categories *labels; diff --git a/raster/r.report/parse.c b/raster/r.report/parse.c index 2b18491b8a0..44e7bc5dbd3 100644 --- a/raster/r.report/parse.c +++ b/raster/r.report/parse.c @@ -17,6 +17,7 @@ int parse_command_line(int argc, char *argv[]) struct Option *nv; struct Option *nsteps; struct Option *sort; + struct Option *format; } parms; struct { struct Flag *f; @@ -103,6 +104,9 @@ int parse_command_line(int argc, char *argv[]) _("Sort by cell counts in descending order")); parms.sort->guisection = _("Formatting"); + parms.format = G_define_standard_option(G_OPT_F_FORMAT); + parms.format->guisection = _("Formatting"); + flags.h = G_define_flag(); flags.h->key = 'h'; flags.h->description = _("Suppress page headers"); @@ -172,6 +176,13 @@ int parse_command_line(int argc, char *argv[]) cat_ranges = flags.C->answer; as_int = flags.i->answer; + if (strcmp(parms.format->answer, "json") == 0) { + format = JSON; + } + else { + format = PLAIN; + } + for (i = 0; parms.cell->answers[i]; i++) parse_layer(parms.cell->answers[i]); if (parms.units->answers) diff --git a/raster/r.report/prt_json.c b/raster/r.report/prt_json.c new file mode 100644 index 00000000000..94590ea7b9f --- /dev/null +++ b/raster/r.report/prt_json.c @@ -0,0 +1,205 @@ +#include +#include +#include +#include "global.h" +#include +#include + +JSON_Value *make_units(int ns, int nl) +{ + JSON_Value *units_value = json_value_init_array(); + JSON_Array *units_array = json_array(units_value); + for (int i = 0; i < nunits; i++) { + int _ns = ns; + + JSON_Value *unit_value = json_value_init_object(); + JSON_Object *unit_object = json_object(unit_value); + + if (unit[i].type == CELL_COUNTS) { + json_object_set_string(unit_object, "unit", "cell counts"); + json_object_set_number(unit_object, "value", count_sum(&_ns, nl)); + } + else if (unit[i].type == PERCENT_COVER) { + json_object_set_string(unit_object, "unit", "% cover"); + int k = ns - 1; + while (k >= 0 && same_cats(k, ns, nl - 1)) + k--; + k++; + double area = area_sum(&k, nl - 1); + area = 100.0 * area_sum(&_ns, nl) / area; + json_object_set_number(unit_object, "value", area); + } + else { + char *unit_name = NULL; + if (unit[i].type == ACRES) { + unit_name = "acres"; + } + else if (unit[i].type == HECTARES) { + unit_name = "hectares"; + } + else if (unit[i].type == SQ_MILES) { + unit_name = "square miles"; + } + else if (unit[i].type == SQ_METERS) { + unit_name = "square meters"; + } + else if (unit[i].type == SQ_KILOMETERS) { + unit_name = "square kilometers"; + } + json_object_set_string(unit_object, "unit", unit_name); + json_object_set_number(unit_object, "value", + area_sum(&_ns, nl) * unit[i].factor); + } + json_array_append_value(units_array, unit_value); + } + return units_value; +} + +JSON_Value *make_category(int ns, int nl, JSON_Value *sub_categories) +{ + JSON_Value *object_value = json_value_init_object(); + JSON_Object *object = json_object(object_value); + + CELL *cats = Gstats[ns].cats; + json_object_set_number(object, "category", cats[nl]); + + DCELL dLow, dHigh; + + if (!is_fp[nl] || as_int) + json_object_set_string(object, "label", + Rast_get_c_cat(&cats[nl], &layers[nl].labels)); + else { + /* find or construct the label for floating point range to print */ + if (Rast_is_c_null_value(&cats[nl])) + json_object_set_null(object, "label"); + else if (cat_ranges) { + json_object_set_string(object, "label", + Rast_get_ith_d_cat(&layers[nl].labels, + cats[nl], &dLow, &dHigh)); + } + else { + dLow = (DMAX[nl] - DMIN[nl]) / (double)nsteps * + (double)(cats[nl] - 1) + + DMIN[nl]; + dHigh = (DMAX[nl] - DMIN[nl]) / (double)nsteps * (double)cats[nl] + + DMIN[nl]; + + json_object_set_string(object, "label", "from to"); + + JSON_Value *range_value = json_value_init_object(); + JSON_Object *range_object = json_object(range_value); + json_object_set_number(range_object, "from", dLow); + json_object_set_number(range_object, "to", dHigh); + json_object_set_value(object, "range", range_value); + } + } + + JSON_Value *units_value = make_units(ns, nl); + json_object_set_value(object, "units", units_value); + + if (sub_categories != NULL) { + json_object_set_value(object, "categories", sub_categories); + } + return object_value; +} + +JSON_Value *make_categories(int start, int end, int level) +{ + JSON_Value *array_value = json_value_init_array(); + JSON_Array *array = json_array(array_value); + if (level == nlayers - 1) { + for (int i = start; i < end; i++) { + JSON_Value *category = make_category(i, level, NULL); + json_array_append_value(array, category); + } + } + else { + while (start < end) { + int curr = start; + while ((curr < end) && same_cats(start, curr, level)) { + curr++; + } + JSON_Value *sub_categories = + make_categories(start, curr, level + 1); + JSON_Value *category = make_category(start, level, sub_categories); + json_array_append_value(array, category); + start = curr; + } + } + return array_value; +} + +void print_json() +{ + compute_unit_format(0, nunits - 1, JSON); + + JSON_Value *root_value = json_value_init_object(); + JSON_Object *root_object = json_object(root_value); + + json_object_set_string(root_object, "location", G_location()); + + char date[64]; + time_t now; + struct tm *tm_info; + + time(&now); + tm_info = localtime(&now); + strftime(date, 64, "%Y-%m-%dT%H:%M:%S%z", tm_info); + json_object_set_string(root_object, "created", date); + + JSON_Value *region_value = json_value_init_object(); + JSON_Object *region_object = json_object(region_value); + json_object_set_number(region_object, "north", window.north); + json_object_set_number(region_object, "south", window.south); + json_object_set_number(region_object, "east", window.east); + json_object_set_number(region_object, "west", window.west); + json_object_set_number(region_object, "ew_res", window.ew_res); + json_object_set_number(region_object, "ns_res", window.ns_res); + json_object_set_value(root_object, "region", region_value); + + char *mask = maskinfo(); + if (strcmp(mask, "none") == 0) { + json_object_set_null(root_object, "mask"); + } + else { + json_object_set_string(root_object, "mask", mask); + } + + JSON_Value *maps_value = json_value_init_array(); + JSON_Array *maps_array = json_array(maps_value); + + for (int i = 0; i < nlayers; i++) { + JSON_Value *map_value = json_value_init_object(); + JSON_Object *map_object = json_object(map_value); + json_object_set_string(map_object, "name", layers[i].name); + + char *label; + label = Rast_get_cats_title(&(layers[i].labels)); + if (label == NULL || *label == 0) { + json_object_set_null(map_object, "label"); + } + else { + G_strip(label); + json_object_set_string(map_object, "label", label); + } + + json_object_set_string(map_object, "type", "raster"); + json_array_append_value(maps_array, map_value); + } + json_object_set_value(root_object, "maps", maps_value); + + JSON_Value *root_categories_value = make_categories(0, nstats, 0); + json_object_set_value(root_object, "categories", root_categories_value); + + JSON_Value *totals = make_units(0, -1); + json_object_set_value(root_object, "totals", totals); + + char *serialized_string = NULL; + serialized_string = json_serialize_to_string_pretty(root_value); + if (serialized_string == NULL) { + G_fatal_error(_("Failed to initialize pretty JSON string.")); + } + puts(serialized_string); + json_free_serialized_string(serialized_string); + json_value_free(root_value); +} diff --git a/raster/r.report/prt_report.c b/raster/r.report/prt_report.c index 8edb0ded88b..d8eba602a34 100644 --- a/raster/r.report/prt_report.c +++ b/raster/r.report/prt_report.c @@ -13,83 +13,12 @@ int print_report(int unit1, int unit2) int i; int divider_level; int after_header; - int need_format; int with_stats; char *cp; int spacing; char dot; - /* examine units, determine output format */ - for (i = unit1; i <= unit2; i++) { - need_format = 1; - unit[i].label[0] = ""; - unit[i].label[1] = ""; - - switch (unit[i].type) { - case CELL_COUNTS: - need_format = 0; - unit[i].len = 5; - unit[i].label[0] = " cell"; - unit[i].label[1] = "count"; - ns = 0; - sprintf(num, "%ld", count_sum(&ns, -1)); - len = strlen(num); - if (len > unit[i].len) - unit[i].len = len; - break; - - case PERCENT_COVER: - need_format = 0; - unit[i].dp = 2; - unit[i].len = 6; - unit[i].label[0] = " % "; - unit[i].label[1] = "cover"; - unit[i].eformat = 0; - break; - - case SQ_METERS: - unit[i].label[0] = "square"; - unit[i].label[1] = "meters"; - unit[i].factor = 1.0; - break; - - case SQ_KILOMETERS: - unit[i].label[0] = " square "; - unit[i].label[1] = "kilometers"; - unit[i].factor = 1.0e-6; - break; - - case ACRES: - unit[i].label[0] = ""; - unit[i].label[1] = "acres"; - unit[i].factor = 2.47105381467165e-4; /* 640 acres in a sq mile */ - break; - - case HECTARES: - unit[i].label[0] = ""; - unit[i].label[1] = "hectares"; - unit[i].factor = 1.0e-4; - break; - - case SQ_MILES: - unit[i].label[0] = "square"; - unit[i].label[1] = " miles"; - unit[i].factor = 3.86102158542446e-7; /* 1 / ( (0.0254m/in * 12in/ft - * 5280ft/mi)^2 ) */ - break; - - default: - G_fatal_error("Unit %d not yet supported", unit[i].type); - } - if (need_format) { - unit[i].dp = 6; - unit[i].len = 10; - unit[i].eformat = 0; - ns = 0; - format_parms(area_sum(&ns, -1) * unit[i].factor, &unit[i].len, - &unit[i].dp, &(unit[i].eformat), e_format); - } - } + compute_unit_format(unit1, unit2, PLAIN); /* figure out how big the category numbers are when printed */ for (nl = 0; nl < nlayers; nl++) diff --git a/raster/r.report/r.report.html b/raster/r.report/r.report.html index d4706471e3e..f4779f27310 100644 --- a/raster/r.report/r.report.html +++ b/raster/r.report/r.report.html @@ -132,6 +132,744 @@

          EXAMPLE

          +-----------------------------------------------------------------------------+ +The output from r.report can be output in JSON by passing the format=json option. + +
          +r.report -n -a map=towns,elevation units=miles,meters,kilometers,acres,hectares,cells,percent nsteps=2 format=json
          +
          + +
          +{
          +    "location": "nc_spm_08_grass7",
          +    "created": "2024-07-24T14:59:09+0530",
          +    "region": {
          +        "north": 320000,
          +        "south": 10000,
          +        "east": 935000,
          +        "west": 120000,
          +        "ew_res": 500,
          +        "ns_res": 500
          +    },
          +    "mask": null,
          +    "maps": [
          +        {
          +            "name": "towns",
          +            "label": "South West Wake: Cities and towns derived from zipcodes",
          +            "type": "raster",
          +        },
          +        {
          +            "name": "zipcodes",
          +            "label": "South West Wake: Zipcode areas derived from vector map",
          +            "type": "raster",
          +        }
          +    ],
          +    "categories": [
          +        {
          +            "category": 1,
          +            "label": "CARY",
          +            "units": [
          +                {
          +                    "unit": "square miles",
          +                    "value": 10.231707201374819
          +                },
          +                {
          +                    "unit": "square meters",
          +                    "value": 26500000
          +                },
          +                {
          +                    "unit": "square kilometers",
          +                    "value": 26.5
          +                },
          +                {
          +                    "unit": "acres",
          +                    "value": 6548.2926088798722
          +                },
          +                {
          +                    "unit": "hectares",
          +                    "value": 2650
          +                },
          +                {
          +                    "unit": "cell counts",
          +                    "value": 106
          +                },
          +                {
          +                    "unit": "% cover",
          +                    "value": 13.086419753086419
          +                }
          +            ],
          +            "categories": [
          +                {
          +                    "category": 1,
          +                    "label": "from to",
          +                    "range": {
          +                        "from": 55.578792572021484,
          +                        "to": 105.9543285369873
          +                    },
          +                    "units": [
          +                        {
          +                            "unit": "square miles",
          +                            "value": 0.8687298567205034
          +                        },
          +                        {
          +                            "unit": "square meters",
          +                            "value": 2250000
          +                        },
          +                        {
          +                            "unit": "square kilometers",
          +                            "value": 2.25
          +                        },
          +                        {
          +                            "unit": "acres",
          +                            "value": 555.98710830112122
          +                        },
          +                        {
          +                            "unit": "hectares",
          +                            "value": 225
          +                        },
          +                        {
          +                            "unit": "cell counts",
          +                            "value": 9
          +                        },
          +                        {
          +                            "unit": "% cover",
          +                            "value": 8.4905660377358494
          +                        }
          +                    ]
          +                },
          +                {
          +                    "category": 2,
          +                    "label": "from to",
          +                    "range": {
          +                        "from": 105.9543285369873,
          +                        "to": 156.32986450195312
          +                    },
          +                    "units": [
          +                        {
          +                            "unit": "square miles",
          +                            "value": 9.3629773446543147
          +                        },
          +                        {
          +                            "unit": "square meters",
          +                            "value": 24250000
          +                        },
          +                        {
          +                            "unit": "square kilometers",
          +                            "value": 24.25
          +                        },
          +                        {
          +                            "unit": "acres",
          +                            "value": 5992.305500578751
          +                        },
          +                        {
          +                            "unit": "hectares",
          +                            "value": 2425
          +                        },
          +                        {
          +                            "unit": "cell counts",
          +                            "value": 97
          +                        },
          +                        {
          +                            "unit": "% cover",
          +                            "value": 91.509433962264154
          +                        }
          +                    ]
          +                }
          +            ]
          +        },
          +        {
          +            "category": 2,
          +            "label": "GARNER",
          +            "units": [
          +                {
          +                    "unit": "square miles",
          +                    "value": 5.5019557592298556
          +                },
          +                {
          +                    "unit": "square meters",
          +                    "value": 14250000
          +                },
          +                {
          +                    "unit": "square kilometers",
          +                    "value": 14.25
          +                },
          +                {
          +                    "unit": "acres",
          +                    "value": 3521.2516859071011
          +                },
          +                {
          +                    "unit": "hectares",
          +                    "value": 1425
          +                },
          +                {
          +                    "unit": "cell counts",
          +                    "value": 57
          +                },
          +                {
          +                    "unit": "% cover",
          +                    "value": 7.0370370370370372
          +                }
          +            ],
          +            "categories": [
          +                {
          +                    "category": 1,
          +                    "label": "from to",
          +                    "range": {
          +                        "from": 55.578792572021484,
          +                        "to": 105.9543285369873
          +                    },
          +                    "units": [
          +                        {
          +                            "unit": "square miles",
          +                            "value": 4.3436492836025176
          +                        },
          +                        {
          +                            "unit": "square meters",
          +                            "value": 11250000
          +                        },
          +                        {
          +                            "unit": "square kilometers",
          +                            "value": 11.25
          +                        },
          +                        {
          +                            "unit": "acres",
          +                            "value": 2779.9355415056061
          +                        },
          +                        {
          +                            "unit": "hectares",
          +                            "value": 1125
          +                        },
          +                        {
          +                            "unit": "cell counts",
          +                            "value": 45
          +                        },
          +                        {
          +                            "unit": "% cover",
          +                            "value": 78.94736842105263
          +                        }
          +                    ]
          +                },
          +                {
          +                    "category": 2,
          +                    "label": "from to",
          +                    "range": {
          +                        "from": 105.9543285369873,
          +                        "to": 156.32986450195312
          +                    },
          +                    "units": [
          +                        {
          +                            "unit": "square miles",
          +                            "value": 1.158306475627338
          +                        },
          +                        {
          +                            "unit": "square meters",
          +                            "value": 3000000
          +                        },
          +                        {
          +                            "unit": "square kilometers",
          +                            "value": 3
          +                        },
          +                        {
          +                            "unit": "acres",
          +                            "value": 741.31614440149497
          +                        },
          +                        {
          +                            "unit": "hectares",
          +                            "value": 300
          +                        },
          +                        {
          +                            "unit": "cell counts",
          +                            "value": 12
          +                        },
          +                        {
          +                            "unit": "% cover",
          +                            "value": 21.05263157894737
          +                        }
          +                    ]
          +                }
          +            ]
          +        },
          +        {
          +            "category": 3,
          +            "label": "APEX",
          +            "units": [
          +                {
          +                    "unit": "square miles",
          +                    "value": 0.9652553963561149
          +                },
          +                {
          +                    "unit": "square meters",
          +                    "value": 2500000
          +                },
          +                {
          +                    "unit": "square kilometers",
          +                    "value": 2.5
          +                },
          +                {
          +                    "unit": "acres",
          +                    "value": 617.76345366791247
          +                },
          +                {
          +                    "unit": "hectares",
          +                    "value": 250
          +                },
          +                {
          +                    "unit": "cell counts",
          +                    "value": 10
          +                },
          +                {
          +                    "unit": "% cover",
          +                    "value": 1.2345679012345678
          +                }
          +            ],
          +            "categories": [
          +                {
          +                    "category": 1,
          +                    "label": "from to",
          +                    "range": {
          +                        "from": 55.578792572021484,
          +                        "to": 105.9543285369873
          +                    },
          +                    "units": [
          +                        {
          +                            "unit": "square miles",
          +                            "value": 0.096525539635611488
          +                        },
          +                        {
          +                            "unit": "square meters",
          +                            "value": 250000
          +                        },
          +                        {
          +                            "unit": "square kilometers",
          +                            "value": 0.25
          +                        },
          +                        {
          +                            "unit": "acres",
          +                            "value": 61.776345366791247
          +                        },
          +                        {
          +                            "unit": "hectares",
          +                            "value": 25
          +                        },
          +                        {
          +                            "unit": "cell counts",
          +                            "value": 1
          +                        },
          +                        {
          +                            "unit": "% cover",
          +                            "value": 10
          +                        }
          +                    ]
          +                },
          +                {
          +                    "category": 2,
          +                    "label": "from to",
          +                    "range": {
          +                        "from": 105.9543285369873,
          +                        "to": 156.32986450195312
          +                    },
          +                    "units": [
          +                        {
          +                            "unit": "square miles",
          +                            "value": 0.8687298567205034
          +                        },
          +                        {
          +                            "unit": "square meters",
          +                            "value": 2250000
          +                        },
          +                        {
          +                            "unit": "square kilometers",
          +                            "value": 2.25
          +                        },
          +                        {
          +                            "unit": "acres",
          +                            "value": 555.98710830112122
          +                        },
          +                        {
          +                            "unit": "hectares",
          +                            "value": 225
          +                        },
          +                        {
          +                            "unit": "cell counts",
          +                            "value": 9
          +                        },
          +                        {
          +                            "unit": "% cover",
          +                            "value": 90
          +                        }
          +                    ]
          +                }
          +            ]
          +        },
          +        {
          +            "category": 4,
          +            "label": "RALEIGH-CITY",
          +            "units": [
          +                {
          +                    "unit": "square miles",
          +                    "value": 6.0811089970435237
          +                },
          +                {
          +                    "unit": "square meters",
          +                    "value": 15750000
          +                },
          +                {
          +                    "unit": "square kilometers",
          +                    "value": 15.75
          +                },
          +                {
          +                    "unit": "acres",
          +                    "value": 3891.9097581078486
          +                },
          +                {
          +                    "unit": "hectares",
          +                    "value": 1575
          +                },
          +                {
          +                    "unit": "cell counts",
          +                    "value": 63
          +                },
          +                {
          +                    "unit": "% cover",
          +                    "value": 7.7777777777777777
          +                }
          +            ],
          +            "categories": [
          +                {
          +                    "category": 1,
          +                    "label": "from to",
          +                    "range": {
          +                        "from": 55.578792572021484,
          +                        "to": 105.9543285369873
          +                    },
          +                    "units": [
          +                        {
          +                            "unit": "square miles",
          +                            "value": 5.3089046799586326
          +                        },
          +                        {
          +                            "unit": "square meters",
          +                            "value": 13750000
          +                        },
          +                        {
          +                            "unit": "square kilometers",
          +                            "value": 13.75
          +                        },
          +                        {
          +                            "unit": "acres",
          +                            "value": 3397.6989951735186
          +                        },
          +                        {
          +                            "unit": "hectares",
          +                            "value": 1375
          +                        },
          +                        {
          +                            "unit": "cell counts",
          +                            "value": 55
          +                        },
          +                        {
          +                            "unit": "% cover",
          +                            "value": 87.301587301587304
          +                        }
          +                    ]
          +                },
          +                {
          +                    "category": 2,
          +                    "label": "from to",
          +                    "range": {
          +                        "from": 105.9543285369873,
          +                        "to": 156.32986450195312
          +                    },
          +                    "units": [
          +                        {
          +                            "unit": "square miles",
          +                            "value": 0.7722043170848919
          +                        },
          +                        {
          +                            "unit": "square meters",
          +                            "value": 2000000
          +                        },
          +                        {
          +                            "unit": "square kilometers",
          +                            "value": 2
          +                        },
          +                        {
          +                            "unit": "acres",
          +                            "value": 494.21076293432998
          +                        },
          +                        {
          +                            "unit": "hectares",
          +                            "value": 200
          +                        },
          +                        {
          +                            "unit": "cell counts",
          +                            "value": 8
          +                        },
          +                        {
          +                            "unit": "% cover",
          +                            "value": 12.698412698412698
          +                        }
          +                    ]
          +                }
          +            ]
          +        },
          +        {
          +            "category": 5,
          +            "label": "RALEIGH-SOUTH",
          +            "units": [
          +                {
          +                    "unit": "square miles",
          +                    "value": 47.394039961085241
          +                },
          +                {
          +                    "unit": "square meters",
          +                    "value": 122750000
          +                },
          +                {
          +                    "unit": "square kilometers",
          +                    "value": 122.75
          +                },
          +                {
          +                    "unit": "acres",
          +                    "value": 30332.185575094503
          +                },
          +                {
          +                    "unit": "hectares",
          +                    "value": 12275
          +                },
          +                {
          +                    "unit": "cell counts",
          +                    "value": 491
          +                },
          +                {
          +                    "unit": "% cover",
          +                    "value": 60.617283950617285
          +                }
          +            ],
          +            "categories": [
          +                {
          +                    "category": 1,
          +                    "label": "from to",
          +                    "range": {
          +                        "from": 55.578792572021484,
          +                        "to": 105.9543285369873
          +                    },
          +                    "units": [
          +                        {
          +                            "unit": "square miles",
          +                            "value": 25.579268003437047
          +                        },
          +                        {
          +                            "unit": "square meters",
          +                            "value": 66250000
          +                        },
          +                        {
          +                            "unit": "square kilometers",
          +                            "value": 66.25
          +                        },
          +                        {
          +                            "unit": "acres",
          +                            "value": 16370.731522199681
          +                        },
          +                        {
          +                            "unit": "hectares",
          +                            "value": 6625
          +                        },
          +                        {
          +                            "unit": "cell counts",
          +                            "value": 265
          +                        },
          +                        {
          +                            "unit": "% cover",
          +                            "value": 53.971486761710793
          +                        }
          +                    ]
          +                },
          +                {
          +                    "category": 2,
          +                    "label": "from to",
          +                    "range": {
          +                        "from": 105.9543285369873,
          +                        "to": 156.32986450195312
          +                    },
          +                    "units": [
          +                        {
          +                            "unit": "square miles",
          +                            "value": 21.814771957648198
          +                        },
          +                        {
          +                            "unit": "square meters",
          +                            "value": 56500000
          +                        },
          +                        {
          +                            "unit": "square kilometers",
          +                            "value": 56.5
          +                        },
          +                        {
          +                            "unit": "acres",
          +                            "value": 13961.454052894822
          +                        },
          +                        {
          +                            "unit": "hectares",
          +                            "value": 5650
          +                        },
          +                        {
          +                            "unit": "cell counts",
          +                            "value": 226
          +                        },
          +                        {
          +                            "unit": "% cover",
          +                            "value": 46.028513238289207
          +                        }
          +                    ]
          +                }
          +            ]
          +        },
          +        {
          +            "category": 6,
          +            "label": "RALEIGH-WEST",
          +            "units": [
          +                {
          +                    "unit": "square miles",
          +                    "value": 8.0116197897557537
          +                },
          +                {
          +                    "unit": "square meters",
          +                    "value": 20750000
          +                },
          +                {
          +                    "unit": "square kilometers",
          +                    "value": 20.75
          +                },
          +                {
          +                    "unit": "acres",
          +                    "value": 5127.4366654436735
          +                },
          +                {
          +                    "unit": "hectares",
          +                    "value": 2075
          +                },
          +                {
          +                    "unit": "cell counts",
          +                    "value": 83
          +                },
          +                {
          +                    "unit": "% cover",
          +                    "value": 10.246913580246913
          +                }
          +            ],
          +            "categories": [
          +                {
          +                    "category": 1,
          +                    "label": "from to",
          +                    "range": {
          +                        "from": 55.578792572021484,
          +                        "to": 105.9543285369873
          +                    },
          +                    "units": [
          +                        {
          +                            "unit": "square miles",
          +                            "value": 0.096525539635611488
          +                        },
          +                        {
          +                            "unit": "square meters",
          +                            "value": 250000
          +                        },
          +                        {
          +                            "unit": "square kilometers",
          +                            "value": 0.25
          +                        },
          +                        {
          +                            "unit": "acres",
          +                            "value": 61.776345366791247
          +                        },
          +                        {
          +                            "unit": "hectares",
          +                            "value": 25
          +                        },
          +                        {
          +                            "unit": "cell counts",
          +                            "value": 1
          +                        },
          +                        {
          +                            "unit": "% cover",
          +                            "value": 1.2048192771084338
          +                        }
          +                    ]
          +                },
          +                {
          +                    "category": 2,
          +                    "label": "from to",
          +                    "range": {
          +                        "from": 105.9543285369873,
          +                        "to": 156.32986450195312
          +                    },
          +                    "units": [
          +                        {
          +                            "unit": "square miles",
          +                            "value": 7.9150942501201422
          +                        },
          +                        {
          +                            "unit": "square meters",
          +                            "value": 20500000
          +                        },
          +                        {
          +                            "unit": "square kilometers",
          +                            "value": 20.5
          +                        },
          +                        {
          +                            "unit": "acres",
          +                            "value": 5065.6603200768823
          +                        },
          +                        {
          +                            "unit": "hectares",
          +                            "value": 2050
          +                        },
          +                        {
          +                            "unit": "cell counts",
          +                            "value": 82
          +                        },
          +                        {
          +                            "unit": "% cover",
          +                            "value": 98.795180722891573
          +                        }
          +                    ]
          +                }
          +            ]
          +        }
          +    ],
          +    "totals": [
          +        {
          +            "unit": "square miles",
          +            "value": 78.185687104845314
          +        },
          +        {
          +            "unit": "square meters",
          +            "value": 202500000
          +        },
          +        {
          +            "unit": "square kilometers",
          +            "value": 202.5
          +        },
          +        {
          +            "unit": "acres",
          +            "value": 50038.839747100916
          +        },
          +        {
          +            "unit": "hectares",
          +            "value": 20250
          +        },
          +        {
          +            "unit": "cell counts",
          +            "value": 810
          +        },
          +        {
          +            "unit": "% cover",
          +            "value": 100
          +        }
          +    ]
          +}
          +
          +

          SEE ALSO

          diff --git a/raster/r.report/report.c b/raster/r.report/report.c index a10a3fc34bd..1e97f33c5e2 100644 --- a/raster/r.report/report.c +++ b/raster/r.report/report.c @@ -4,18 +4,25 @@ int report(void) { int unit1, unit2; - if (stats_flag == STATS_ONLY) - return 1; + switch (format) { + case JSON: + print_json(); + break; + case PLAIN: + if (stats_flag == STATS_ONLY) + return 1; - if (nunits == 0) - print_report(0, -1); - else - for (unit1 = 0; unit1 < nunits; unit1 = unit2 + 1) { - unit2 = unit1 + 2; - if (unit2 >= nunits) - unit2 = nunits - 1; - print_report(unit1, unit2); - } + if (nunits == 0) + print_report(0, -1); + else + for (unit1 = 0; unit1 < nunits; unit1 = unit2 + 1) { + unit2 = unit1 + 2; + if (unit2 >= nunits) + unit2 = nunits - 1; + print_report(unit1, unit2); + } + break; + } return 0; } diff --git a/raster/r.report/testsuite/test_r_report.py b/raster/r.report/testsuite/test_r_report.py index 7d872e17ff8..53e49fdc174 100644 --- a/raster/r.report/testsuite/test_r_report.py +++ b/raster/r.report/testsuite/test_r_report.py @@ -9,9 +9,15 @@ for details. """ +import json import os +from itertools import zip_longest +from datetime import datetime + from grass.gunittest.case import TestCase +from grass.gunittest.gmodules import SimpleModule + class TestRasterreport(TestCase): outfile = "test_out.csv" @@ -58,6 +64,609 @@ def test_output(self): self.assertModule("r.report", map="lakes", output=self.outfile) self.assertFileExists(self.outfile) + def _assert_report_equal(self, reference, data): + keys = ["location", "region", "mask", "maps", "totals"] + for key in keys: + self.assertEqual(reference[key], data[key]) + + for category1, category2 in zip_longest( + reference["categories"], data["categories"] + ): + self.assertEqual(category1["category"], category2["category"]) + self.assertEqual(category1["label"], category2["label"]) + + for unit1, unit2 in zip_longest(category1["units"], category2["units"]): + self.assertEqual(unit1["unit"], unit2["unit"]) + self.assertAlmostEqual(unit1["value"], unit2["value"], places=6) + + for sub_category1, sub_category2 in zip_longest( + category1["categories"], category2["categories"] + ): + self.assertEqual(sub_category1["category"], sub_category2["category"]) + self.assertEqual(sub_category1["label"], sub_category2["label"]) + + for sub_unit1, sub_unit2 in zip_longest( + sub_category1["units"], sub_category2["units"] + ): + self.assertEqual(sub_unit1["unit"], sub_unit2["unit"]) + self.assertAlmostEqual( + sub_unit1["value"], sub_unit2["value"], places=6 + ) + + def test_json(self): + """Test JSON format""" + reference = { + "location": "nc_spm_full_v2alpha2", + "region": { + "north": 228500, + "south": 215000, + "east": 645000, + "west": 630000, + "ew_res": 10, + "ns_res": 10, + }, + "mask": None, + "maps": [ + { + "name": "towns", + "label": "South West Wake: Cities and towns derived from zipcodes", + "type": "raster", + }, + { + "name": "zipcodes", + "label": "South West Wake: Zipcode areas derived from vector map", + "type": "raster", + }, + ], + "categories": [ + { + "category": 1, + "label": "CARY", + "units": [ + {"unit": "cell counts", "value": 260849}, + {"unit": "% cover", "value": 12.881432098765432}, + ], + "categories": [ + { + "category": 27511, + "label": "CARY", + "units": [ + {"unit": "cell counts", "value": 105800}, + {"unit": "% cover", "value": 40.559864135956055}, + ], + }, + { + "category": 27513, + "label": "CARY", + "units": [ + {"unit": "cell counts", "value": 20530}, + {"unit": "% cover", "value": 7.8704537874402432}, + ], + }, + { + "category": 27518, + "label": "CARY", + "units": [ + {"unit": "cell counts", "value": 134519}, + {"unit": "% cover", "value": 51.569682076603705}, + ], + }, + ], + }, + { + "category": 2, + "label": "GARNER", + "units": [ + {"unit": "cell counts", "value": 141572}, + {"unit": "% cover", "value": 6.99120987654321}, + ], + "categories": [ + { + "category": 27529, + "label": "GARNER", + "units": [ + {"unit": "cell counts", "value": 141572}, + {"unit": "% cover", "value": 100}, + ], + } + ], + }, + { + "category": 3, + "label": "APEX", + "units": [ + {"unit": "cell counts", "value": 25444}, + {"unit": "% cover", "value": 1.2564938271604937}, + ], + "categories": [ + { + "category": 27539, + "label": "APEX", + "units": [ + {"unit": "cell counts", "value": 25444}, + {"unit": "% cover", "value": 100}, + ], + } + ], + }, + { + "category": 4, + "label": "RALEIGH-CITY", + "units": [ + {"unit": "cell counts", "value": 160514}, + {"unit": "% cover", "value": 7.926617283950617}, + ], + "categories": [ + { + "category": 27601, + "label": "RALEIGH", + "units": [ + {"unit": "cell counts", "value": 45468}, + {"unit": "% cover", "value": 28.326501115167524}, + ], + }, + { + "category": 27604, + "label": "RALEIGH", + "units": [ + {"unit": "cell counts", "value": 47389}, + {"unit": "% cover", "value": 29.523281458315161}, + ], + }, + { + "category": 27605, + "label": "RALEIGH", + "units": [ + {"unit": "cell counts", "value": 23677}, + {"unit": "% cover", "value": 14.750738253361078}, + ], + }, + { + "category": 27608, + "label": "RALEIGH", + "units": [ + {"unit": "cell counts", "value": 43980}, + {"unit": "% cover", "value": 27.399479173156237}, + ], + }, + ], + }, + { + "category": 5, + "label": "RALEIGH-SOUTH", + "units": [ + {"unit": "cell counts", "value": 1227632}, + {"unit": "% cover", "value": 60.623802469135804}, + ], + "categories": [ + { + "category": 27603, + "label": "RALEIGH", + "units": [ + {"unit": "cell counts", "value": 429179}, + {"unit": "% cover", "value": 34.959906551800536}, + ], + }, + { + "category": 27606, + "label": "RALEIGH", + "units": [ + {"unit": "cell counts", "value": 662642}, + {"unit": "% cover", "value": 53.977250511553954}, + ], + }, + { + "category": 27610, + "label": "RALEIGH", + "units": [ + {"unit": "cell counts", "value": 135811}, + {"unit": "% cover", "value": 11.062842936645509}, + ], + }, + ], + }, + { + "category": 6, + "label": "RALEIGH-WEST", + "units": [ + {"unit": "cell counts", "value": 208989}, + {"unit": "% cover", "value": 10.320444444444444}, + ], + "categories": [ + { + "category": 27607, + "label": "RALEIGH", + "units": [ + {"unit": "cell counts", "value": 208989}, + {"unit": "% cover", "value": 100}, + ], + } + ], + }, + ], + "totals": [ + {"unit": "cell counts", "value": 2025000}, + {"unit": "% cover", "value": 100}, + ], + } + module = SimpleModule("r.report", map="towns,zipcodes", format="json") + self.runModule(module) + data = json.loads(module.outputs.stdout) + self._assert_report_equal(reference, data) + + def test_json2(self): + """Test JSON format with more options""" + reference = { + "location": "nc_spm_full_v2alpha2", + "created": "2024-07-24T14:59:09+0530", + "region": { + "north": 228500, + "south": 215000, + "east": 645000, + "west": 630000, + "ew_res": 10, + "ns_res": 10, + }, + "mask": None, + "maps": [ + { + "name": "towns", + "label": "South West Wake: Cities and towns derived from zipcodes", + "type": "raster", + }, + { + "name": "elevation", + "label": "South-West Wake county: Elevation NED 10m", + "type": "raster", + }, + ], + "categories": [ + { + "category": 1, + "label": "CARY", + "units": [ + {"unit": "square miles", "value": 10.07143619536385}, + {"unit": "square meters", "value": 26084900}, + {"unit": "square kilometers", "value": 26.084899999999998}, + {"unit": "acres", "value": 6445.719165032852}, + {"unit": "hectares", "value": 2608.4900000000002}, + {"unit": "cell counts", "value": 260849}, + {"unit": "% cover", "value": 12.881432098765432}, + ], + "categories": [ + { + "category": 1, + "label": "from to", + "range": { + "from": 55.578792572021484, + "to": 105.9543285369873, + }, + "units": [ + {"unit": "square miles", "value": 0.9655642780829489}, + {"unit": "square meters", "value": 2500800}, + {"unit": "square kilometers", "value": 2.5008}, + {"unit": "acres", "value": 617.9611379730862}, + {"unit": "hectares", "value": 250.08}, + {"unit": "cell counts", "value": 25008}, + {"unit": "% cover", "value": 9.58715578744791}, + ], + }, + { + "category": 2, + "label": "from to", + "range": { + "from": 105.9543285369873, + "to": 156.32986450195312, + }, + "units": [ + {"unit": "square miles", "value": 9.1058719172809}, + {"unit": "square meters", "value": 23584100}, + {"unit": "square kilometers", "value": 23.5841}, + {"unit": "acres", "value": 5827.758027059766}, + {"unit": "hectares", "value": 2358.4100000000003}, + {"unit": "cell counts", "value": 235841}, + {"unit": "% cover", "value": 90.41284421255209}, + ], + }, + ], + }, + { + "category": 2, + "label": "GARNER", + "units": [ + {"unit": "square miles", "value": 5.4661254789171165}, + {"unit": "square meters", "value": 14157200}, + {"unit": "square kilometers", "value": 14.1572}, + {"unit": "acres", "value": 3498.3203065069483}, + {"unit": "hectares", "value": 1415.72}, + {"unit": "cell counts", "value": 141572}, + {"unit": "% cover", "value": 6.99120987654321}, + ], + "categories": [ + { + "category": 1, + "label": "from to", + "range": { + "from": 55.578792572021484, + "to": 105.9543285369873, + }, + "units": [ + {"unit": "square miles", "value": 4.24917008540718}, + {"unit": "square meters", "value": 11005300}, + {"unit": "square kilometers", "value": 11.0053}, + {"unit": "acres", "value": 2719.4688546605908}, + {"unit": "hectares", "value": 1100.53}, + {"unit": "cell counts", "value": 110053}, + {"unit": "% cover", "value": 77.73641680558302}, + ], + }, + { + "category": 2, + "label": "from to", + "range": { + "from": 105.9543285369873, + "to": 156.32986450195312, + }, + "units": [ + {"unit": "square miles", "value": 1.2169553935099355}, + {"unit": "square meters", "value": 3151900}, + {"unit": "square kilometers", "value": 3.1519}, + {"unit": "acres", "value": 778.8514518463573}, + {"unit": "hectares", "value": 315.19}, + {"unit": "cell counts", "value": 31519}, + {"unit": "% cover", "value": 22.263583194416974}, + ], + }, + ], + }, + { + "category": 3, + "label": "APEX", + "units": [ + {"unit": "square miles", "value": 0.9823983321953995}, + {"unit": "square meters", "value": 2544400}, + {"unit": "square kilometers", "value": 2.5444}, + {"unit": "acres", "value": 628.7349326050546}, + {"unit": "hectares", "value": 254.44000000000003}, + {"unit": "cell counts", "value": 25444}, + {"unit": "% cover", "value": 1.2564938271604937}, + ], + "categories": [ + { + "category": 1, + "label": "from to", + "range": { + "from": 55.578792572021484, + "to": 105.9543285369873, + }, + "units": [ + {"unit": "square miles", "value": 0.03262563239683668}, + {"unit": "square meters", "value": 84500}, + { + "unit": "square kilometers", + "value": 0.08449999999999999, + }, + {"unit": "acres", "value": 20.880404733975443}, + {"unit": "hectares", "value": 8.450000000000001}, + {"unit": "cell counts", "value": 845}, + {"unit": "% cover", "value": 3.321018707750354}, + ], + }, + { + "category": 2, + "label": "from to", + "range": { + "from": 105.9543285369873, + "to": 156.32986450195312, + }, + "units": [ + {"unit": "square miles", "value": 0.9497726997985628}, + {"unit": "square meters", "value": 2459900}, + { + "unit": "square kilometers", + "value": 2.4598999999999998, + }, + {"unit": "acres", "value": 607.8545278710792}, + {"unit": "hectares", "value": 245.99}, + {"unit": "cell counts", "value": 24599}, + {"unit": "% cover", "value": 96.67898129224965}, + ], + }, + ], + }, + { + "category": 4, + "label": "RALEIGH-CITY", + "units": [ + {"unit": "square miles", "value": 6.1974801876282175}, + {"unit": "square meters", "value": 16051400}, + {"unit": "square kilometers", "value": 16.0514}, + {"unit": "acres", "value": 3966.387320082052}, + {"unit": "hectares", "value": 1605.14}, + {"unit": "cell counts", "value": 160514}, + {"unit": "% cover", "value": 7.926617283950617}, + ], + "categories": [ + { + "category": 1, + "label": "from to", + "range": { + "from": 55.578792572021484, + "to": 105.9543285369873, + }, + "units": [ + {"unit": "square miles", "value": 5.062455672160989}, + {"unit": "square meters", "value": 13111700}, + { + "unit": "square kilometers", + "value": 13.111699999999999, + }, + {"unit": "acres", "value": 3239.971630183027}, + {"unit": "hectares", "value": 1311.17}, + {"unit": "cell counts", "value": 131117}, + {"unit": "% cover", "value": 81.68570965772456}, + ], + }, + { + "category": 2, + "label": "from to", + "range": { + "from": 105.9543285369873, + "to": 156.32986450195312, + }, + "units": [ + {"unit": "square miles", "value": 1.1350245154672285}, + {"unit": "square meters", "value": 2939700}, + { + "unit": "square kilometers", + "value": 2.9396999999999998, + }, + {"unit": "acres", "value": 726.415689899025}, + {"unit": "hectares", "value": 293.97}, + {"unit": "cell counts", "value": 29397}, + {"unit": "% cover", "value": 18.31429034227544}, + ], + }, + ], + }, + { + "category": 5, + "label": "RALEIGH-SOUTH", + "units": [ + {"unit": "square miles", "value": 47.39913650957801}, + {"unit": "square meters", "value": 122763200}, + {"unit": "square kilometers", "value": 122.7632}, + {"unit": "acres", "value": 30335.44736612987}, + {"unit": "hectares", "value": 12276.32}, + {"unit": "cell counts", "value": 1227632}, + {"unit": "% cover", "value": 60.6238024691358}, + ], + "categories": [ + { + "category": 1, + "label": "from to", + "range": { + "from": 55.578792572021484, + "to": 105.9543285369873, + }, + "units": [ + {"unit": "square miles", "value": 24.823086925931666}, + {"unit": "square meters", "value": 64291500}, + {"unit": "square kilometers", "value": 64.2915}, + {"unit": "acres", "value": 15886.775632596238}, + {"unit": "hectares", "value": 6429.150000000001}, + {"unit": "cell counts", "value": 642915}, + {"unit": "% cover", "value": 52.37033573579053}, + ], + }, + { + "category": 2, + "label": "from to", + "range": { + "from": 105.9543285369873, + "to": 156.32986450195312, + }, + "units": [ + {"unit": "square miles", "value": 22.576049583646338}, + {"unit": "square meters", "value": 58471700}, + {"unit": "square kilometers", "value": 58.4717}, + {"unit": "acres", "value": 14448.671733533633}, + {"unit": "hectares", "value": 5847.17}, + {"unit": "cell counts", "value": 584717}, + {"unit": "% cover", "value": 47.62966426420947}, + ], + }, + ], + }, + { + "category": 6, + "label": "RALEIGH-WEST", + "units": [ + {"unit": "square miles", "value": 8.069110401162725}, + {"unit": "square meters", "value": 20898900}, + {"unit": "square kilometers", "value": 20.898899999999998}, + {"unit": "acres", "value": 5164.230656744135}, + {"unit": "hectares", "value": 2089.8900000000003}, + {"unit": "cell counts", "value": 208989}, + {"unit": "% cover", "value": 10.320444444444444}, + ], + "categories": [ + { + "category": 1, + "label": "from to", + "range": { + "from": 55.578792572021484, + "to": 105.9543285369873, + }, + "units": [ + {"unit": "square miles", "value": 0.23822503182068916}, + {"unit": "square meters", "value": 617000}, + {"unit": "square kilometers", "value": 0.617}, + {"unit": "acres", "value": 152.4640203652408}, + {"unit": "hectares", "value": 61.7}, + {"unit": "cell counts", "value": 6170}, + {"unit": "% cover", "value": 2.9523084947054627}, + ], + }, + { + "category": 2, + "label": "from to", + "range": { + "from": 105.9543285369873, + "to": 156.32986450195312, + }, + "units": [ + {"unit": "square miles", "value": 7.8308853693420355}, + {"unit": "square meters", "value": 20281900}, + {"unit": "square kilometers", "value": 20.2819}, + {"unit": "acres", "value": 5011.766636378894}, + {"unit": "hectares", "value": 2028.19}, + {"unit": "cell counts", "value": 202819}, + {"unit": "% cover", "value": 97.04769150529454}, + ], + }, + ], + }, + ], + "totals": [ + {"unit": "square miles", "value": 78.18568710484531}, + {"unit": "square meters", "value": 202500000}, + {"unit": "square kilometers", "value": 202.5}, + {"unit": "acres", "value": 50038.839747100916}, + {"unit": "hectares", "value": 20250}, + {"unit": "cell counts", "value": 2025000}, + {"unit": "% cover", "value": 100}, + ], + } + module = SimpleModule( + "r.report", + map="towns,elevation", + units=[ + "miles", + "meters", + "kilometers", + "acres", + "hectares", + "cells", + "percent", + ], + nsteps=2, + format="json", + ) + self.runModule(module) + data = json.loads(module.outputs.stdout) + + # created field represents the time of running the command. Therefore, its exact value + # cannot be tested. We only check that it is present and in the ISO8601 datetime format + self.assertIn("created", data) + try: + # on Python 3.11 and below, datetime.fromisoformat doesn't support zone info with offset + datetime.strptime(data["created"], "%Y-%m-%dT%H:%M:%S%z") + except ValueError: + self.fail("created field is not in isoformat: %s" % (data["created"],)) + + self._assert_report_equal(reference, data) + if __name__ == "__main__": from grass.gunittest.main import test From 7f7f0ef3742d66c397cfab3ab2ebeb9cbb03f295 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 6 Aug 2024 11:05:50 +0000 Subject: [PATCH 096/471] CI(deps): Update pre-commit hook psf/black-pre-commit-mirror to v24.8.0 (#4142) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3730bce903f..035239f08f2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -49,7 +49,7 @@ repos: - id: markdownlint-fix # Using this mirror lets us use mypyc-compiled black, which is about 2x faster - repo: https://github.com/psf/black-pre-commit-mirror - rev: 24.4.2 + rev: 24.8.0 hooks: - id: black-jupyter exclude: | From 39d3f8701376e5b752d7b9cbc0ace17fdb556106 Mon Sep 17 00:00:00 2001 From: Chung-Yuan Liang <77927944+cyliang368@users.noreply.github.com> Date: Tue, 6 Aug 2024 09:50:15 -0400 Subject: [PATCH 097/471] r.texture: control the number of threads (#3917) use helper function to control the number of threads for OpenMP --- raster/r.texture/main.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/raster/r.texture/main.c b/raster/r.texture/main.c index 7ff1c7d9865..6d1ec8f0458 100644 --- a/raster/r.texture/main.c +++ b/raster/r.texture/main.c @@ -312,19 +312,7 @@ int main(int argc, char *argv[]) out_set.flag_null = flag.null; out_set.flag_ind = flag.ind; - threads = atoi(parm.nproc->answer); -#if defined(_OPENMP) - /* Set the number of threads */ - omp_set_num_threads(threads); - if (threads > 1) - G_message(_("Using %d threads for parallel computing."), threads); -#else - if (threads > 1) { - G_warning(_("GRASS GIS is not compiled with OpenMP support, parallel " - "computation is disabled.")); - threads = 1; - } -#endif + threads = G_set_omp_num_threads(parm.nproc); execute_texture(data, &dim, measure_menu, measure_idx, &out_set, threads); for (i = 0; i < dim.n_outputs; i++) { @@ -332,6 +320,7 @@ int main(int argc, char *argv[]) Rast_short_history(mapname[i], "raster", &history); Rast_command_history(&history); Rast_write_history(mapname[i], &history); + Rast_free_history(&history); } /* Free allocated memory */ From 47b03d16d6325e234a0487c339bb80e8576a6356 Mon Sep 17 00:00:00 2001 From: Chung-Yuan Liang <77927944+cyliang368@users.noreply.github.com> Date: Tue, 6 Aug 2024 12:44:30 -0400 Subject: [PATCH 098/471] r.horizon: Support parallel computing for the raster mode by OpenMP (#3890) --- raster/r.horizon/Makefile | 5 +- .../r.horizon/benchmark/benchmark_rhorizon.py | 67 +++ raster/r.horizon/main.c | 22 +- raster/r.horizon/r.horizon.html | 32 +- .../r.horizon/rhorizon_raster_efficiency.png | Bin 0 -> 12248 bytes raster/r.horizon/rhorizon_raster_speedup.png | Bin 0 -> 12178 bytes raster/r.horizon/rhorizon_raster_time.png | Bin 0 -> 11604 bytes .../testsuite/test_r_horizon_parallel.py | 528 ++++++++++++++++++ 8 files changed, 647 insertions(+), 7 deletions(-) create mode 100644 raster/r.horizon/benchmark/benchmark_rhorizon.py create mode 100644 raster/r.horizon/rhorizon_raster_efficiency.png create mode 100644 raster/r.horizon/rhorizon_raster_speedup.png create mode 100644 raster/r.horizon/rhorizon_raster_time.png create mode 100644 raster/r.horizon/testsuite/test_r_horizon_parallel.py diff --git a/raster/r.horizon/Makefile b/raster/r.horizon/Makefile index 4407e3c4073..08d3e8db030 100644 --- a/raster/r.horizon/Makefile +++ b/raster/r.horizon/Makefile @@ -2,9 +2,10 @@ MODULE_TOPDIR = ../.. PGM = r.horizon -LIBES = $(PARSONLIB) $(GPROJLIB) $(RASTERLIB) $(GISLIB) $(MATHLIB) $(PROJLIB) +LIBES = $(PARSONLIB) $(GPROJLIB) $(RASTERLIB) $(GISLIB) $(MATHLIB) $(PROJLIB) $(OPENMP_LIBPATH) $(OPENMP_LIB) DEPENDENCIES = $(GPROJDEP) $(RASTERDEP) $(GISDEP) -EXTRA_INC = $(PROJINC) $(GDALCFLAGS) +EXTRA_CFLAGS = $(OPENMP_CFLAGS) +EXTRA_INC = $(PROJINC) $(GDALCFLAGS) $(OPENMP_INCPATH) include $(MODULE_TOPDIR)/include/Make/Module.make diff --git a/raster/r.horizon/benchmark/benchmark_rhorizon.py b/raster/r.horizon/benchmark/benchmark_rhorizon.py new file mode 100644 index 00000000000..99ec0184aeb --- /dev/null +++ b/raster/r.horizon/benchmark/benchmark_rhorizon.py @@ -0,0 +1,67 @@ +"""Benchmarking of r.horizon +point mode, one direction + +@author Chung-Yuan Liang, 2024 +""" + +from grass.exceptions import CalledModuleError +from grass.pygrass.modules import Module + +import grass.benchmark as bm + + +def main(): + results = [] + metrics = ["time", "speedup", "efficiency"] + mapsizes = [1e6, 2e6, 4e6, 8e6] + + # run benchmarks + for mapsize in mapsizes: + benchmark( + size=int(mapsize**0.5), + step=0, + label=f"r.horizon_{int(mapsize/1e6)}M", + results=results, + ) + + # plot results + for metric in metrics: + bm.nprocs_plot( + results, + title=f"r.horizon raster mode {metric}", + metric=metric, + ) + + +def benchmark(size, step, label, results): + reference = "benchmark_r_horizon_reference_map" + output = "benchmark_r_horizon" + + generate_map(rows=size, cols=size, fname=reference) + module = Module( + "r.horizon", + elevation=reference, + output=output, + direction=0, + step=step, + ) + + results.append(bm.benchmark_nprocs(module, label=label, max_nprocs=8, repeat=3)) + Module( + "g.remove", quiet=True, flags="f", type="raster", pattern="benchmark_r_horizon*" + ) + + +def generate_map(rows, cols, fname): + Module("g.region", flags="p", rows=rows, cols=cols, res=1) + # Generate using r.random.surface if r.surf.fractal fails + try: + print("Generating reference map using r.surf.fractal...") + Module("r.surf.fractal", output=fname, overwrite=True) + except CalledModuleError: + print("r.surf.fractal fails, using r.random.surface instead...") + Module("r.random.surface", output=fname, overwrite=True) + + +if __name__ == "__main__": + main() diff --git a/raster/r.horizon/main.c b/raster/r.horizon/main.c index ff0b2429456..fdec073a76e 100644 --- a/raster/r.horizon/main.c +++ b/raster/r.horizon/main.c @@ -31,6 +31,9 @@ Program was refactored by Anna Petrasova to remove most global variables. * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#if defined(_OPENMP) +#include +#endif #include #include @@ -163,7 +166,7 @@ int main(int argc, char *argv[]) struct { struct Option *elevin, *dist, *coord, *direction, *horizon, *step, *start, *end, *bufferzone, *e_buff, *w_buff, *n_buff, *s_buff, - *maxdistance, *format, *output; + *maxdistance, *format, *output, *nprocs; } parm; struct { @@ -175,6 +178,7 @@ int main(int argc, char *argv[]) G_add_keyword(_("raster")); G_add_keyword(_("solar")); G_add_keyword(_("sun position")); + G_add_keyword(_("parallel")); module->label = _("Computes horizon angle height from a digital elevation model."); module->description = @@ -309,6 +313,8 @@ int main(int argc, char *argv[]) _("Name of file for output (use output=- for stdout)"); parm.output->guisection = _("Point mode"); + parm.nprocs = G_define_standard_option(G_OPT_M_NPROCS); + flag.horizonDistance = G_define_flag(); flag.horizonDistance->key = 'l'; flag.horizonDistance->description = @@ -328,6 +334,8 @@ int main(int argc, char *argv[]) if (G_parser(argc, argv)) exit(EXIT_FAILURE); + G_set_omp_num_threads(parm.nprocs); + struct Cell_head cellhd; struct Cell_head new_cellhd; G_get_set_window(&cellhd); @@ -857,6 +865,7 @@ void calculate_point_mode(const Settings *settings, const Geometry *geometry, horizons = json_value_get_array(horizons_value); break; } + for (int i = 0; i < printCount; i++) { JSON_Value *value; JSON_Object *object; @@ -1179,6 +1188,7 @@ void calculate_raster_mode(const Settings *settings, const Geometry *geometry, _("Calculating map %01d of %01d (angle %.2f, raster map <%s>)"), (k + 1), arrayNumInt, angle_deg, shad_filename); +#pragma omp parallel for schedule(static, 1) default(shared) for (int j = hor_row_start; j < hor_row_end; j++) { G_percent(j - hor_row_start, hor_numrows - 1, 2); for (int i = hor_col_start; i < hor_col_end; i++) { @@ -1219,12 +1229,11 @@ void calculate_raster_mode(const Settings *settings, const Geometry *geometry, if (settings->degreeOutput) { shadow_angle *= rad2deg; } - horizon_raster[j - buffer_s][i - buffer_w] = shadow_angle; } /* undefs */ - } - } + } /* end of loop over columns */ + } /* end of parallel section */ G_debug(1, "OUTGR() starts..."); OUTGR(settings, shad_filename, cellhd); @@ -1265,4 +1274,9 @@ void calculate_raster_mode(const Settings *settings, const Geometry *geometry, Rast_write_history(shad_filename, &history); G_free(shad_filename); } + + /* free memory */ + for (int l = 0; l < hor_numrows; l++) + G_free(horizon_raster[l]); + G_free(horizon_raster); } diff --git a/raster/r.horizon/r.horizon.html b/raster/r.horizon/r.horizon.html index 5770fd248dd..513af64b8c4 100644 --- a/raster/r.horizon/r.horizon.html +++ b/raster/r.horizon/r.horizon.html @@ -121,7 +121,9 @@

          Input parameters:

          raster coordinates (e.g., for latitude-longitude buffers are measured in degrees). -

          METHOD

          +

          NOTES

          + +

          Method

          The calculation method is based on the method used in r.sun to calculate shadows. It starts at a very shallow angle and walks @@ -145,6 +147,34 @@

          METHOD

          All horizon values are positive (or zero). While negative values are in theory possible, r.horizon currently does not support them. + +

          Performance

          +Parallel processing is implemented for the raster mode. +To enable parallel processing, the user can specify the number of threads +to be used with the nprocs parameter. +Figures below show benchmark results running on +Intel® Core™ i5-13600K CPU @ 3.5GHz. +See benchmark scripts +in the source code for more details. + +

          +The iterations of the algorithm used in r.horizon +depends on the topography. As a result, the benchmark results may vary +depending on the topography of the study area. + +

          + time for r.horizon with different map sizes + speedup for r.horizon with different map sizes + efficiency for r.horizon with different map sizes +
          + Figure: Benchmark shows execution time, parallel speedup and + efficiency for different numbers of cells (1M, 2M, 4M, and 8M). +
          + +

          EXAMPLES

          The examples are intended for the North Carolina sample dataset. diff --git a/raster/r.horizon/rhorizon_raster_efficiency.png b/raster/r.horizon/rhorizon_raster_efficiency.png new file mode 100644 index 0000000000000000000000000000000000000000..7b37d1fff46a43196329a6d8db6d959a9de4db5d GIT binary patch literal 12248 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 literal 0 HcmV?d00001 diff --git a/raster/r.horizon/rhorizon_raster_speedup.png b/raster/r.horizon/rhorizon_raster_speedup.png new file mode 100644 index 0000000000000000000000000000000000000000..7609a75c16311755b5d2fad7fbd3debaec6a02fa GIT binary patch literal 12178 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 literal 0 HcmV?d00001 diff --git a/raster/r.horizon/rhorizon_raster_time.png b/raster/r.horizon/rhorizon_raster_time.png new file mode 100644 index 0000000000000000000000000000000000000000..d02f8f4450d8f8957d60b2c0ed3898c273251d7e GIT binary patch literal 11604 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 literal 0 HcmV?d00001 diff --git a/raster/r.horizon/testsuite/test_r_horizon_parallel.py b/raster/r.horizon/testsuite/test_r_horizon_parallel.py new file mode 100644 index 00000000000..605f94365b2 --- /dev/null +++ b/raster/r.horizon/testsuite/test_r_horizon_parallel.py @@ -0,0 +1,528 @@ +""" +TEST: test_r_horizon.py + +AUTHOR(S): Anna Petrasova + +PURPOSE: Test r.horizon + +COPYRIGHT: (C) 2015-2024 Anna Petrasova + + This program is free software under the GNU General Public + License (>=v2). Read the file COPYING that comes with GRASS + for details. +""" + +import json + +from grass.gunittest.case import TestCase +from grass.gunittest.main import test +from grass.gunittest.gmodules import SimpleModule +from grass.script import raster_what + + +ref1 = """azimuth,horizon_height +180.000000,0.023101 +""" + +ref2 = """azimuth,horizon_height +180.000000,0.023101 +200.000000,0.034850 +220.000000,0.050549 +240.000000,0.048211 +260.000000,0.053101 +280.000000,0.039774 +300.000000,0.032360 +320.000000,0.014804 +340.000000,0.000000 +360.000000,0.004724 +20.000000,0.012612 +40.000000,0.015207 +60.000000,0.014344 +80.000000,0.011044 +100.000000,0.012192 +120.000000,0.007462 +140.000000,0.004071 +160.000000,0.015356 +""" + +ref3 = """azimuth,horizon_height +180.000000,0.023101 +200.000000,0.034850 +220.000000,0.050549 +240.000000,0.048211 +260.000000,0.053101 +280.000000,0.039774 +300.000000,0.032360 +320.000000,0.014804 +340.000000,0.000000 +360.000000,0.004724 +20.000000,0.012612 +40.000000,0.015207 +60.000000,0.014344 +80.000000,0.011044 +100.000000,0.012192 +120.000000,0.007462 +140.000000,0.004071 +160.000000,0.015356 +""" + +ref4 = """azimuth,horizon_height +0.000000,0.197017 +20.000000,0.196832 +40.000000,0.196875 +60.000000,0.196689 +80.000000,0.196847 +100.000000,0.196645 +120.000000,0.196969 +140.000000,0.196778 +160.000000,0.196863 +180.000000,0.197017 +200.000000,0.196832 +220.000000,0.196875 +240.000000,0.196689 +260.000000,0.196847 +280.000000,0.196645 +300.000000,0.196969 +320.000000,0.196778 +340.000000,0.196863 +""" + +ref5 = """azimuth,horizon_height,horizon_distance +0.000000,0.197017,5000.040000 +20.000000,0.196832,5004.837660 +40.000000,0.196875,5003.728610 +60.000000,0.196689,5008.552685 +80.000000,0.196847,5004.448022 +100.000000,0.196645,5009.690609 +120.000000,0.196969,5001.279836 +140.000000,0.196778,5006.246099 +160.000000,0.196863,5004.018385 +180.000000,0.197017,5000.040000 +200.000000,0.196832,5004.837660 +220.000000,0.196875,5003.728610 +240.000000,0.196689,5008.552685 +260.000000,0.196847,5004.448022 +280.000000,0.196645,5009.690609 +300.000000,0.196969,5001.279836 +320.000000,0.196778,5006.246099 +340.000000,0.196863,5004.018385 +""" + +ref6 = """azimuth,horizon_height,horizon_distance +180.000000,0.023101,420.000000 +200.000000,0.034850,436.577599 +220.000000,0.050549,184.390889 +240.000000,0.048211,197.230829 +260.000000,0.053101,162.788206 +280.000000,0.039774,253.179778 +300.000000,0.032360,277.848880 +320.000000,0.014804,262.488095 +340.000000,0.000000,0.000000 +360.000000,0.004724,2780.017986 +20.000000,0.012612,1148.259553 +40.000000,0.015207,1334.166406 +60.000000,0.014344,1867.966809 +80.000000,0.011044,2964.203097 +100.000000,0.012192,1828.223181 +120.000000,0.007462,4270.667395 +140.000000,0.004071,5659.231397 +160.000000,0.015356,1666.883319 +""" + + +class TestHorizon(TestCase): + circle = "circle" + horizon = "test_horizon_from_elevation" + horizon_output = "test_horizon_output_from_elevation" + + @classmethod + def setUpClass(cls): + cls.use_temp_region() + cls.runModule("g.region", raster="elevation") + cls.runModule( + "r.circle", + flags="b", + output=cls.circle, + coordinates=(637505, 221755), + min=5000, + multiplier=1000, + ) + cls.runModule("r.null", map=cls.circle, null=0) + + @classmethod + def tearDownClass(cls): + cls.runModule("g.remove", flags="f", type="raster", name=cls.circle) + cls.del_temp_region() + + def setUp(self): + self.runModule("g.region", raster="elevation") + + def tearDown(self): + """Remove horizon map after each test method""" + self.runModule("g.remove", flags="f", type="raster", name=self.horizon) + self.runModule( + "g.remove", flags="f", type="raster", pattern=self.horizon_output + "*" + ) + + def test_point_mode_one_direction(self): + """Test mode with 1 point and 1 direction""" + module = SimpleModule( + "r.horizon", + elevation="elevation", + coordinates=(634720, 216180), + output=self.horizon, + direction=180, + step=0, + nprocs=4, + ) + self.assertModule(module) + stdout = module.outputs.stdout + self.assertMultiLineEqual(first=ref1, second=stdout) + + def test_point_mode_multiple_direction(self): + """Test mode with 1 point and multiple directions""" + module = SimpleModule( + "r.horizon", + elevation="elevation", + coordinates=(634720, 216180), + output=self.horizon, + direction=180, + step=20, + nprocs=4, + ) + self.assertModule(module) + stdout = module.outputs.stdout + self.assertMultiLineEqual(first=ref2, second=stdout) + + # include nulls along the edge + self.runModule("g.region", raster="elevation", w="w-100") + self.assertModule(module) + stdout = module.outputs.stdout + self.assertMultiLineEqual(first=ref2, second=stdout) + + def test_point_mode_multiple_points_and_directions(self): + """Test mode with 2 identical points and multiple directions""" + module = SimpleModule( + "r.horizon", + elevation="elevation", + coordinates=(634720, 216180, 634720, 216180), + output=self.horizon, + direction=180, + step=20, + nprocs=4, + ) + self.assertModule(module) + stdout = module.outputs.stdout + self.assertMultiLineEqual(first=ref2 + ref2, second=stdout) + + def test_point_mode_multiple_direction_json(self): + """Test mode with 1 point and multiple directions with JSON""" + module = SimpleModule( + "r.horizon", + elevation="elevation", + coordinates=(634720, 216180), + output=self.horizon, + direction=180, + step=20, + nprocs=4, + format="json", + ) + self.assertModule(module) + stdout = json.loads(module.outputs.stdout) + horizons = [] + reference = {} + for line in ref6.splitlines()[1:]: + azimuth, horizon, distance = line.split(",") + horizons.append( + { + "azimuth": float(azimuth), + "angle": float(horizon), + "distance": float(distance), + } + ) + reference["x"] = 634720.0 + reference["y"] = 216180.0 + reference["horizons"] = horizons + + self.assertListEqual([reference], stdout) + + def test_point_mode_multiple_points_and_directions_json(self): + """Test mode with 2 identical points and multiple directions with JSON""" + module = SimpleModule( + "r.horizon", + elevation="elevation", + coordinates=(634720, 216180, 634720, 216180), + output=self.horizon, + direction=180, + step=20, + nprocs=4, + format="json", + ) + self.assertModule(module) + stdout = json.loads(module.outputs.stdout) + horizons = [] + reference = {} + for line in ref6.splitlines()[1:]: + azimuth, horizon, distance = line.split(",") + horizons.append( + { + "azimuth": float(azimuth), + "angle": float(horizon), + "distance": float(distance), + } + ) + reference["x"] = 634720.0 + reference["y"] = 216180.0 + reference["horizons"] = horizons + + self.assertListEqual([reference, reference], stdout) + + def test_point_mode_multiple_direction_artificial(self): + """Test mode with 1 point and multiple directions with artificial surface""" + module = SimpleModule( + "r.horizon", + elevation=self.circle, + coordinates=(637505, 221755), + output=self.horizon, + direction=0, + step=20, + nprocs=4, + ) + self.assertModule(module) + stdout = module.outputs.stdout + self.assertMultiLineEqual(first=ref4, second=stdout) + + def test_point_mode_multiple_direction_artificial_distance(self): + """With 1 point, more directions on artificial surface, distance in output""" + module = SimpleModule( + "r.horizon", + elevation=self.circle, + coordinates=(637505, 221755), + output=self.horizon, + direction=0, + step=20, + nprocs=4, + flags="l", + ) + self.assertModule(module) + stdout = module.outputs.stdout + self.assertMultiLineEqual(first=ref5, second=stdout) + + module = SimpleModule( + "r.horizon", + elevation=self.circle, + coordinates=(637505, 221755), + output=self.horizon, + direction=0, + step=20, + nprocs=4, + flags="l", + format="json", + ) + self.assertModule(module) + stdout = json.loads(module.outputs.stdout) + horizons = [] + reference = {} + for line in ref5.splitlines()[1:]: + azimuth, horizon, distance = line.split(",") + horizons.append( + { + "azimuth": float(azimuth), + "angle": float(horizon), + "distance": float(distance), + } + ) + reference["x"] = 637505.0 + reference["y"] = 221755.0 + reference["horizons"] = horizons + + self.assertListEqual([reference], stdout) + + def test_raster_mode_one_direction(self): + """Test mode with one direction and against point mode""" + module = SimpleModule( + "r.horizon", + elevation="elevation", + output=self.horizon_output, + direction=50, + nprocs=4, + ) + self.assertModule(module) + ref = { + "min": 0, + "max": 0.70678365230560, + "stddev": 0.0360724286360789, + } + output = "test_horizon_output_from_elevation_050" + self.assertRasterFitsUnivar( + raster=output, + reference=ref, + precision=1e6, + ) + + # test if point mode matches raster mode + coordinates = [ + (634725, 216185), + (633315, 217595), + (633555, 223405), + (639955, 220605), + (637505, 219705), + (641105, 222225), + ] + for coordinate in coordinates: + module = SimpleModule( + "r.horizon", + elevation="elevation", + coordinates=coordinate, + output=self.horizon, + direction=50, + step=0, + ) + self.assertModule(module) + stdout = module.outputs.stdout + first = float(stdout.splitlines()[-1].split(",")[-1]) + what = raster_what(output, coord=coordinate) + second = float(what[0][output]["value"]) + self.assertAlmostEqual(first=first, second=second, delta=0.000001) + + def test_raster_mode_multiple_direction(self): + self.runModule("g.region", raster="elevation", res=100) + module = SimpleModule( + "r.horizon", + elevation="elevation", + output=self.horizon_output, + start=10, + end=50, + step=15.512, + nprocs=4, + ) + self.assertModule(module) + module_list = SimpleModule( + "g.list", type="raster", pattern=self.horizon_output + "*" + ) + self.runModule(module_list) + stdout = module_list.outputs.stdout.strip() + self.assertMultiLineEqual( + first=( + "test_horizon_output_from_elevation_010_000\n" + "test_horizon_output_from_elevation_025_512\n" + "test_horizon_output_from_elevation_041_024" + ), + second=stdout, + ) + + def test_raster_mode_multiple_direction_offset(self): + self.runModule("g.region", raster="elevation", res=100) + module = SimpleModule( + "r.horizon", + elevation="elevation", + output=self.horizon_output, + start=10, + end=50, + step=15.512, + nprocs=4, + direction=80, + ) + self.assertModule(module) + module_list = SimpleModule( + "g.list", type="raster", pattern=self.horizon_output + "*" + ) + self.runModule(module_list) + stdout = module_list.outputs.stdout.strip() + self.assertMultiLineEqual( + first=( + "test_horizon_output_from_elevation_090_000\n" + "test_horizon_output_from_elevation_105_512\n" + "test_horizon_output_from_elevation_121_024" + ), + second=stdout, + ) + + def test_raster_mode_bufferzone(self): + """Test buffer 100 m and 109 m with resolution 10 gives the same result""" + self.runModule( + "g.region", + raster="elevation", + n="n-5000", + s="s+5000", + e="e-5000", + w="w+5000", + ) + # raises ValueError from pygrass parameter check + self.assertRaises( + ValueError, + SimpleModule, + "r.horizon", + elevation="elevation", + output=self.horizon_output, + direction=50, + bufferzone=-100, + nprocs=4, + ) + self.assertRaises( + ValueError, + SimpleModule, + "r.horizon", + elevation="elevation", + output=self.horizon_output, + direction=50, + e_buff=100, + n_buff=0, + s_buff=-100, + w_buff=-100, + nprocs=4, + ) + module = SimpleModule( + "r.horizon", + elevation="elevation", + output=self.horizon_output, + direction=50, + bufferzone=100, + nprocs=4, + ) + self.assertModule(module) + ref = { + "mean": 0.0344791, + } + output = "test_horizon_output_from_elevation_050" + self.assertRasterFitsUnivar( + raster=output, + reference=ref, + precision=1e-6, + ) + module = SimpleModule( + "r.horizon", + elevation="elevation", + output=self.horizon_output, + direction=50, + bufferzone=103, + nprocs=4, + ) + self.assertModule(module) + self.assertRasterFitsUnivar( + raster=output, + reference=ref, + precision=1e-6, + ) + module = SimpleModule( + "r.horizon", + elevation="elevation", + output=self.horizon_output, + direction=50, + bufferzone=95, + nprocs=4, + ) + self.assertModule(module) + ref = { + "mean": 0.0344624, + } + self.assertRasterFitsUnivar( + raster=output, + reference=ref, + precision=1e-6, + ) + self.runModule("g.region", raster="elevation") + + +if __name__ == "__main__": + test() From 1527322f85cd465437e4f71eae5ce93e751466ea Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 6 Aug 2024 18:22:04 -0400 Subject: [PATCH 099/471] CI(deps): Update github/codeql-action action to v3.26.0 (#4146) --- .github/workflows/codeql-analysis.yml | 4 ++-- .github/workflows/python-code-quality.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index daf1e8762b0..960941d4ede 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -56,7 +56,7 @@ jobs: if: ${{ matrix.language == 'c-cpp' }} - name: Initialize CodeQL - uses: github/codeql-action/init@afb54ba388a7dca6ecae48f608c4ff05ff4cc77a # v3.25.15 + uses: github/codeql-action/init@eb055d739abdc2e8de2e5f4ba1a8b246daa779aa # v3.26.0 with: languages: ${{ matrix.language }} config-file: ./.github/codeql/codeql-config.yml @@ -81,6 +81,6 @@ jobs: run: .github/workflows/build_ubuntu-22.04.sh "${HOME}/install" - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@afb54ba388a7dca6ecae48f608c4ff05ff4cc77a # v3.25.15 + uses: github/codeql-action/analyze@eb055d739abdc2e8de2e5f4ba1a8b246daa779aa # v3.26.0 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index 77ae4a87419..2cf7390084b 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -135,7 +135,7 @@ jobs: path: bandit.sarif - name: Upload SARIF File into Security Tab - uses: github/codeql-action/upload-sarif@afb54ba388a7dca6ecae48f608c4ff05ff4cc77a # v3.25.15 + uses: github/codeql-action/upload-sarif@eb055d739abdc2e8de2e5f4ba1a8b246daa779aa # v3.26.0 with: sarif_file: bandit.sarif From 12957454ac3095866b8ece00a3948da47f9cdbd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Wed, 7 Aug 2024 08:33:03 -0400 Subject: [PATCH 100/471] style: Fix extraneous-parentheses (UP034) (#4144) * style: Fix extraneous-parentheses (UP034) Ruff rule: https://docs.astral.sh/ruff/rules/extraneous-parentheses/ 7 instances fixed * Apply suggestions from code review Co-authored-by: Stefan Blumentrath --------- Co-authored-by: Stefan Blumentrath --- gui/wxpython/animation/data.py | 2 +- gui/wxpython/gmodeler/model.py | 2 +- gui/wxpython/iscatt/core_c.py | 2 +- lib/init/grass.py | 6 ++---- pyproject.toml | 1 - python/grass/pygrass/modules/grid/grid.py | 4 ++-- scripts/g.extension/g.extension.py | 2 +- 7 files changed, 8 insertions(+), 11 deletions(-) diff --git a/gui/wxpython/animation/data.py b/gui/wxpython/animation/data.py index ee4e51cbef1..3799cf43ef1 100644 --- a/gui/wxpython/animation/data.py +++ b/gui/wxpython/animation/data.py @@ -89,7 +89,7 @@ def SetLayerList(self, layerList): timeseriesList.append((layer.name, layer.mapType)) self._firstStdsNameType = layer.name, layer.mapType else: - mapSeriesList.append((layer.maps)) + mapSeriesList.append(layer.maps) if not timeseriesList: self._firstStdsNameType = None, None # this throws GException diff --git a/gui/wxpython/gmodeler/model.py b/gui/wxpython/gmodeler/model.py index ebbcdb6381e..0d154824ebe 100644 --- a/gui/wxpython/gmodeler/model.py +++ b/gui/wxpython/gmodeler/model.py @@ -3487,7 +3487,7 @@ def cleanup(): r""" %s("g.remove", flags="f", type="vector", name=%s) """ - % (run_command, ",".join(('"' + x + '"' for x in vect))) + % (run_command, ",".join(f'"{x}"' for x in vect)) ) if rast3d: self.fd.write( diff --git a/gui/wxpython/iscatt/core_c.py b/gui/wxpython/iscatt/core_c.py index cda449b0dd5..a2a3d5da9da 100644 --- a/gui/wxpython/iscatt/core_c.py +++ b/gui/wxpython/iscatt/core_c.py @@ -58,7 +58,7 @@ def ApplyColormap(vals, vals_mask, colmap, out_vals): colmap_p = colmap.ctypes.data_as(c_uint8_p) out_vals_p = out_vals.ctypes.data_as(c_uint8_p) - vals_size = vals.reshape((-1)).shape[0] + vals_size = vals.reshape(-1).shape[0] I_apply_colormap(vals_p, vals_mask_p, vals_size, colmap_p, out_vals_p) diff --git a/lib/init/grass.py b/lib/init/grass.py index 41fe07fa02f..2a9a585b92b 100755 --- a/lib/init/grass.py +++ b/lib/init/grass.py @@ -1237,10 +1237,8 @@ def set_language(grass_config_dir): # If we got so far, attempts to set up language and locale have # failed on this system. sys.stderr.write( - ( - "Failed to enforce user specified language " - f"'{language}' with error: '{e}'\n" - ) + "Failed to enforce user specified language " + f"'{language}' with error: '{e}'\n" ) sys.stderr.write( "A LANGUAGE environmental variable has been set.\n" diff --git a/pyproject.toml b/pyproject.toml index 99ab7249e23..d3762103711 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -290,7 +290,6 @@ ignore = [ "UP030", # format-literals "UP031", # printf-string-formatting "UP032", # f-string - "UP034", # extraneous-parentheses "UP036", # outdated-version-block "W605", # invalid-escape-sequence "YTT204", # sys-version-info-minor-cmp-int diff --git a/python/grass/pygrass/modules/grid/grid.py b/python/grass/pygrass/modules/grid/grid.py index 1163a8f3b6d..959e6a3b144 100644 --- a/python/grass/pygrass/modules/grid/grid.py +++ b/python/grass/pygrass/modules/grid/grid.py @@ -346,8 +346,8 @@ def get_cmd(cmdd): if isinstance(vals, list) ) ) - cmd.extend(("-%s" % (flg) for flg in cmdd["flags"] if len(flg) == 1)) - cmd.extend(("--%s" % (flg[0]) for flg in cmdd["flags"] if len(flg) > 1)) + cmd.extend(f"-{flg}" for flg in cmdd["flags"] if len(flg) == 1) + cmd.extend(f"--{flg[0]}" for flg in cmdd["flags"] if len(flg) > 1) return cmd diff --git a/scripts/g.extension/g.extension.py b/scripts/g.extension/g.extension.py index 75916576397..14844a8af76 100644 --- a/scripts/g.extension/g.extension.py +++ b/scripts/g.extension/g.extension.py @@ -2243,7 +2243,7 @@ def remove_extension_files(edict, force=False): os.remove(fpath) except OSError: msg = "Unable to remove file '%s'" - err.append((_(msg) % fpath)) + err.append(_(msg) % fpath) removed = False if len(err) > 0: for error_line in err: From eaee7b87195e61abac4314cb32a5c2bd4cb67ff4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 7 Aug 2024 11:54:57 -0400 Subject: [PATCH 101/471] CI(deps): Update docker/build-push-action action to v6.6.0 (#4148) --- .github/workflows/docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index d3104a6ed4b..823dbc95a5c 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -76,7 +76,7 @@ jobs: password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build and push id: docker_build - uses: docker/build-push-action@5176d81f87c23d6fc96624dfdbcd9f3830bbe445 # v6.5.0 + uses: docker/build-push-action@4f7cdeb0f05278b464e71357394bf2c61f94138e # v6.6.0 with: push: true pull: true From 51bc2c3dd6ea4a0f9b21287cdf6dac2827963c11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Wed, 7 Aug 2024 12:06:30 -0400 Subject: [PATCH 102/471] style: Fix dict-index-missing-items (PLC0206) (#4138) * style: Fix dict-index-missing-items (PLC0206) Ruff rule: https://docs.astral.sh/ruff/rules/dict-index-missing-items/ 9 instances solved * Use inline ignore for vector/v.univar/testsuite/test_v_univar.py * Update r.pack.py Co-authored-by: Stefan Blumentrath --------- Co-authored-by: Stefan Blumentrath --- gui/wxpython/datacatalog/tree.py | 6 +++--- gui/wxpython/gmodeler/model.py | 2 +- pyproject.toml | 1 - python/grass/temporal/temporal_algebra.py | 6 +++--- python/grass/temporal/temporal_granularity.py | 4 ++-- scripts/i.pansharpen/i.pansharpen.py | 2 +- scripts/r.pack/r.pack.py | 4 ++-- temporal/t.unregister/t.unregister.py | 3 +-- vector/v.univar/testsuite/test_v_univar.py | 2 +- 9 files changed, 14 insertions(+), 16 deletions(-) diff --git a/gui/wxpython/datacatalog/tree.py b/gui/wxpython/datacatalog/tree.py index 9ea22c1c327..faa0a6aac73 100644 --- a/gui/wxpython/datacatalog/tree.py +++ b/gui/wxpython/datacatalog/tree.py @@ -1677,9 +1677,9 @@ def DisplayLayer(self): all_names.append(name) # if self.selected_location[0].data['name'] == gisenv()['LOCATION_NAME'] and # self.selected_mapset[0]: - for ltype in names: - if names[ltype]: - self._giface.lmgr.AddMaps(list(reversed(names[ltype])), ltype, True) + for ltype, value in names.items(): + if value: + self._giface.lmgr.AddMaps(list(reversed(value)), ltype, True) if len(self._giface.GetLayerList()) == 1: # zoom to map if there is only one map layer diff --git a/gui/wxpython/gmodeler/model.py b/gui/wxpython/gmodeler/model.py index 0d154824ebe..1d0e508d7c5 100644 --- a/gui/wxpython/gmodeler/model.py +++ b/gui/wxpython/gmodeler/model.py @@ -2256,7 +2256,7 @@ def _processConditions(self): pos, size = self._getDim(node) text = self._filterValue(self._getNodeText(node, "condition")).strip() aid = {"if": [], "else": []} - for b in aid.keys(): + for b in aid.keys(): # noqa: PLC0206 bnode = node.find(b) if bnode is None: continue diff --git a/pyproject.toml b/pyproject.toml index d3762103711..ea6c188fc7f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -161,7 +161,6 @@ ignore = [ "PIE794", # duplicate-class-field-definition "PIE808", # unnecessary-range-start "PIE810", # multiple-starts-ends-with - "PLC0206", # dict-index-missing-items "PLC0415", # import-outside-top-level "PLC1901", # compare-to-empty-string "PLC2701", # import-private-name diff --git a/python/grass/temporal/temporal_algebra.py b/python/grass/temporal/temporal_algebra.py index c2546ec0d9e..3792402e5ca 100644 --- a/python/grass/temporal/temporal_algebra.py +++ b/python/grass/temporal/temporal_algebra.py @@ -1193,10 +1193,10 @@ def remove_maps(self): for map in self.removable_maps.values(): map_names[map.get_type()].append(map.get_name()) - for key in map_names.keys(): - if map_names[key]: + for key, value in map_names.items(): + if value: self.msgr.message(_("Removing un-needed or empty %s maps" % (key))) - self._remove_maps(map_names[key], key) + self._remove_maps(value, key) def _remove_maps(self, namelist, map_type): """Remove maps of specific type diff --git a/python/grass/temporal/temporal_granularity.py b/python/grass/temporal/temporal_granularity.py index 29836959cb8..9e4ab653bb1 100644 --- a/python/grass/temporal/temporal_granularity.py +++ b/python/grass/temporal/temporal_granularity.py @@ -466,7 +466,7 @@ def compute_absolute_time_granularity(maps): # start time is required in TGIS and expected to be present if end: map_datetime_delta = compute_datetime_delta(start, end) - for time_unit in granularity_units: + for time_unit in granularity_units.keys(): # noqa: PLC0206 if ( time_unit in map_datetime_delta and map_datetime_delta[time_unit] > 0 @@ -484,7 +484,7 @@ def compute_absolute_time_granularity(maps): else: gap_datetime_delta = compute_datetime_delta(previous_start, start) # Add to the set of the smallest granularity in the granularity_units dict - for time_unit in granularity_units: + for time_unit in granularity_units.keys(): # noqa: PLC0206 if ( time_unit in gap_datetime_delta and gap_datetime_delta[time_unit] > 0 diff --git a/scripts/i.pansharpen/i.pansharpen.py b/scripts/i.pansharpen/i.pansharpen.py index ff0146d7708..f344c5ac7fd 100755 --- a/scripts/i.pansharpen/i.pansharpen.py +++ b/scripts/i.pansharpen/i.pansharpen.py @@ -732,7 +732,7 @@ def matchhist(original, target, matched): stats = gs.decode(stats_out.communicate()[0]).split("\n")[:-1] stats_dict = dict(s.split(":", 1) for s in stats) total_cells = 0 # total non-null cells - for j in stats_dict: + for j in stats_dict.keys(): # noqa: PLC0206 stats_dict[j] = int(stats_dict[j]) if j != "*": total_cells += stats_dict[j] diff --git a/scripts/r.pack/r.pack.py b/scripts/r.pack/r.pack.py index 51420b74a77..cd7d6c39282 100644 --- a/scripts/r.pack/r.pack.py +++ b/scripts/r.pack/r.pack.py @@ -130,11 +130,11 @@ def main(): # Copy vrt files if vrt_files: - for f in vrt_files.keys(): + for f, value in vrt_files.items(): f_tmp_dir = os.path.join(tmp, f) if not os.path.exists(f_tmp_dir): os.mkdir(f_tmp_dir) - path = os.path.join(vrt_files[f], element, f) + path = os.path.join(value, element, f) if os.path.exists(path): grass.debug("copying vrt file {}".format(path)) if os.path.isfile(path): diff --git a/temporal/t.unregister/t.unregister.py b/temporal/t.unregister/t.unregister.py index 492df3e9a7a..e7e234320c0 100755 --- a/temporal/t.unregister/t.unregister.py +++ b/temporal/t.unregister/t.unregister.py @@ -179,8 +179,7 @@ def main(): sp.update_command_string(dbif=dbif) elif len(update_dict) > 0: count = 0 - for key in update_dict.keys(): - id = update_dict[key] + for id in update_dict.values(): sp = tgis.open_old_stds(id, type, dbif) sp.update_from_registered_maps(dbif) gs.percent(count, len(update_dict), 1) diff --git a/vector/v.univar/testsuite/test_v_univar.py b/vector/v.univar/testsuite/test_v_univar.py index 166d47a5350..766871d951c 100644 --- a/vector/v.univar/testsuite/test_v_univar.py +++ b/vector/v.univar/testsuite/test_v_univar.py @@ -167,7 +167,7 @@ def test_json(self): self.assertAlmostEqual(p1["value"], p2["value"]) self.assertCountEqual(list(expected.keys()), list(results.keys())) - for key in expected: + for key in expected.keys(): # noqa: PLC0206 self.assertAlmostEqual(expected[key], results[key]) From 04461231f573431ac32cd436a28186be441d9d3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Wed, 7 Aug 2024 17:34:21 -0400 Subject: [PATCH 103/471] style: Fix parenthesize-chained-operators (RUF021) (#4145) --- display/d.text/test.py | 2 +- gui/wxpython/nviz/mapwindow.py | 2 +- gui/wxpython/psmap/frame.py | 7 ++----- gui/wxpython/psmap/instructions.py | 7 ++----- gui/wxpython/vdigit/toolbars.py | 4 ++-- gui/wxpython/web_services/widgets.py | 7 ++----- man/build_html.py | 2 +- pyproject.toml | 1 - python/grass/gunittest/runner.py | 2 +- python/grass/pygrass/modules/grid/grid.py | 2 +- scripts/r.in.wms/srs.py | 2 +- 11 files changed, 14 insertions(+), 24 deletions(-) diff --git a/display/d.text/test.py b/display/d.text/test.py index 6e7fe676a65..1089096d2ef 100755 --- a/display/d.text/test.py +++ b/display/d.text/test.py @@ -52,7 +52,7 @@ def text(in_text): for i in range(36): font(fonts[int(i % len(fonts))]) - size((36 - i if (i >= 9 and i <= 18 or i > 27) else i) % 9) + size((36 - i if ((i >= 9 and i <= 18) or i > 27) else i) % 9) rotate(i * 10) color(colors[i % len(colors)]) xy( diff --git a/gui/wxpython/nviz/mapwindow.py b/gui/wxpython/nviz/mapwindow.py index f4b93511714..398beb2b738 100644 --- a/gui/wxpython/nviz/mapwindow.py +++ b/gui/wxpython/nviz/mapwindow.py @@ -2472,7 +2472,7 @@ def NvizCmdCommand(self): cmd += mode[3] if "wire" in mode[4]: cmd += mode[4] - if "coarse" in mode[0] or "both" in mode[0] and "wire" in mode[3]: + if "coarse" in mode[0] or ("both" in mode[0] and "wire" in mode[3]): cmd += mode[5] # # attributes diff --git a/gui/wxpython/psmap/frame.py b/gui/wxpython/psmap/frame.py index 8725a0a957d..2334dc0acab 100644 --- a/gui/wxpython/psmap/frame.py +++ b/gui/wxpython/psmap/frame.py @@ -2716,11 +2716,8 @@ def UpdateLabel(self, itype, id): def OnSize(self, event): """Init image size to match window size""" # not zoom all when notebook page is changed - if ( - self.preview - and self.parent.currentPage == 1 - or not self.preview - and self.parent.currentPage == 0 + if (self.preview and self.parent.currentPage == 1) or ( + not self.preview and self.parent.currentPage == 0 ): self.ZoomAll() self.OnIdle(None) diff --git a/gui/wxpython/psmap/instructions.py b/gui/wxpython/psmap/instructions.py index 5a61a4d4bf2..8b71c3d2df9 100644 --- a/gui/wxpython/psmap/instructions.py +++ b/gui/wxpython/psmap/instructions.py @@ -474,11 +474,8 @@ def SendToRead(self, instruction, text, **kwargs): for line in text: if line.find("# north arrow") >= 0: commentFound = True - if ( - i == "image" - and commentFound - or i == "northArrow" - and not commentFound + if (i == "image" and commentFound) or ( + i == "northArrow" and not commentFound ): continue newInstr = myInstrDict[i](id, settings=self, env=self.env) diff --git a/gui/wxpython/vdigit/toolbars.py b/gui/wxpython/vdigit/toolbars.py index 1e8d394bddc..6c5c8b21212 100644 --- a/gui/wxpython/vdigit/toolbars.py +++ b/gui/wxpython/vdigit/toolbars.py @@ -565,9 +565,9 @@ def OnAddAreaMenu(self, event): menuItems = [] if not self.tools or "addArea" in self.tools: menuItems.append((self.icons["addArea"], self.OnAddArea)) - if not self.fType and not self.tools or "addBoundary" in self.tools: + if (not self.fType and not self.tools) or "addBoundary" in self.tools: menuItems.append((self.icons["addBoundary"], self.OnAddBoundary)) - if not self.fType and not self.tools or "addCentroid" in self.tools: + if (not self.fType and not self.tools) or "addCentroid" in self.tools: menuItems.append((self.icons["addCentroid"], self.OnAddCentroid)) self._onMenu(menuItems) diff --git a/gui/wxpython/web_services/widgets.py b/gui/wxpython/web_services/widgets.py index 67d000d75df..21308bf037c 100644 --- a/gui/wxpython/web_services/widgets.py +++ b/gui/wxpython/web_services/widgets.py @@ -1096,11 +1096,8 @@ def compare(item, l_name, st_name) -> bool: return bool( it_l_name == l_name and ( - not it_st - and not st_name - or it_st - and it_st["name"] == st_name - and it_type == "style" + (not it_st and not st_name) + or (it_st and it_st["name"] == st_name and it_type == "style") ) ) diff --git a/man/build_html.py b/man/build_html.py index e075ebaafc1..daa38b557f4 100644 --- a/man/build_html.py +++ b/man/build_html.py @@ -440,7 +440,7 @@ def html_files(cls=None, ignore_gui=True): and (cls != "*" or len(cmd.split(".")) >= 3) and cmd not in {"full_index.html", "index.html"} and cmd not in exclude_mods - and (ignore_gui and not cmd.startswith("wxGUI.") or not ignore_gui) + and ((ignore_gui and not cmd.startswith("wxGUI.")) or not ignore_gui) ): yield cmd diff --git a/pyproject.toml b/pyproject.toml index ea6c188fc7f..09f17caef7c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -239,7 +239,6 @@ ignore = [ "RUF013", # unnecessary-iterable-allocation-for-first-element "RUF015", # unnecessary-iterable-allocation-for-first-element "RUF019", # unnecessary-key-check - "RUF021", # parenthesize-chained-operators "RUF027", # missing-f-string-syntax "RUF100", # unused-noqa "S101", #assert diff --git a/python/grass/gunittest/runner.py b/python/grass/gunittest/runner.py index f1758e2e08b..b99c6704c89 100644 --- a/python/grass/gunittest/runner.py +++ b/python/grass/gunittest/runner.py @@ -164,7 +164,7 @@ def stopTestRun(self): self.printErrors() self.stream.writeln(self.separator2) run = self.testsRun - self.stream.write("Ran %d test%s" % (run, run != 1 and "s" or "")) + self.stream.write("Ran %d test%s" % (run, (run != 1 and "s") or "")) if self.time_taken: self.stream.write(" in %.3fs" % (self.time_taken)) self.stream.writeln() diff --git a/python/grass/pygrass/modules/grid/grid.py b/python/grass/pygrass/modules/grid/grid.py index 959e6a3b144..135daee7dc6 100644 --- a/python/grass/pygrass/modules/grid/grid.py +++ b/python/grass/pygrass/modules/grid/grid.py @@ -42,7 +42,7 @@ def select(parms, ptype): """ for k in parms: par = parms[k] - if par.type == ptype or par.typedesc == ptype and par.value: + if par.type == ptype or (par.typedesc == ptype and par.value): if par.multiple: yield from par.value else: diff --git a/scripts/r.in.wms/srs.py b/scripts/r.in.wms/srs.py index 460e3c14fc1..8d996fcde94 100644 --- a/scripts/r.in.wms/srs.py +++ b/scripts/r.in.wms/srs.py @@ -105,7 +105,7 @@ def getcodeurn(self): """ return "urn:%s:def:crs:%s:%s:%s" % ( - (self.naming_authority and self.naming_authority or "ogc"), + ((self.naming_authority and self.naming_authority) or "ogc"), (self.authority or ""), (self.version or ""), (self.code or ""), From d3fa0fbfad86984d51ba49861b0bcaa023e55b04 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 7 Aug 2024 22:46:14 +0000 Subject: [PATCH 104/471] CI(deps): Update docker/build-push-action action to v6.6.1 (#4152) --- .github/workflows/docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 823dbc95a5c..9040030669c 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -76,7 +76,7 @@ jobs: password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build and push id: docker_build - uses: docker/build-push-action@4f7cdeb0f05278b464e71357394bf2c61f94138e # v6.6.0 + uses: docker/build-push-action@16ebe778df0e7752d2cfcbd924afdbbd89c1a755 # v6.6.1 with: push: true pull: true From 8c22a55f046a2d40d29d28ec95be7c4cc1ea4311 Mon Sep 17 00:00:00 2001 From: Riya Saxena <77328768+29riyasaxena@users.noreply.github.com> Date: Thu, 8 Aug 2024 09:26:26 +0530 Subject: [PATCH 105/471] grass.jupyter: Allow users to draw simple geometries and save it as vector (#4003) Co-authored-by: Anna Petrasova --- python/grass/jupyter/interactivemap.py | 406 +++++++++++++++++++------ python/grass/jupyter/utils.py | 20 ++ 2 files changed, 326 insertions(+), 100 deletions(-) diff --git a/python/grass/jupyter/interactivemap.py b/python/grass/jupyter/interactivemap.py index 561b2cdfbb2..8ae79023fe7 100644 --- a/python/grass/jupyter/interactivemap.py +++ b/python/grass/jupyter/interactivemap.py @@ -22,6 +22,7 @@ reproject_region, update_region, get_location_proj_string, + save_vector, ) @@ -245,6 +246,7 @@ def __init__( """ self._ipyleaflet = None self._folium = None + self._ipywidgets = None def _import_folium(error): try: @@ -282,17 +284,20 @@ def _import_ipyleaflet(error): if self._ipyleaflet: import ipywidgets as widgets # pylint: disable=import-outside-toplevel + + self._ipywidgets = widgets import xyzservices # pylint: disable=import-outside-toplevel # Store height and width self.width = width self.height = height + self._controllers = {} if self._ipyleaflet: basemap = xyzservices.providers.query_name(tiles) if API_key and basemap.get("accessToken"): basemap["accessToken"] = API_key - layout = widgets.Layout(width=f"{width}px", height=f"{height}px") + layout = self._ipywidgets.Layout(width=f"{width}px", height=f"{height}px") self.map = self._ipyleaflet.Map( basemap=basemap, layout=layout, scroll_wheel_zoom=True ) @@ -353,111 +358,62 @@ def add_layer_control(self, **kwargs): else: self.layer_control_object = self._ipyleaflet.LayersControl(**kwargs) - def draw_computational_region(self): - """ - Allow users to draw the computational region and modify it. - """ - import ipywidgets as widgets # pylint: disable=import-outside-toplevel + def setup_drawing_interface(self): + """Sets up the drawing interface for users + to interactively draw and manage geometries on the map. - region_mode_button = widgets.ToggleButton( - icon="square-o", - description="", - value=False, - tooltip="Click to show and edit computational region", - layout=widgets.Layout(width="40px", margin="0px 0px 0px 0px"), + This includes creating a toggle button to activate the drawing mode, and + instantiating an InteractiveDrawController to handle the drawing functionality. + """ + return self._create_toggle_button( + icon="pencil", + tooltip=_("Click to draw geometries"), + controller_class=InteractiveDrawController, ) - save_button = widgets.Button( - description="Update region", - tooltip="Click to update region", - disabled=True, - ) - bottom_output_widget = widgets.Output( - layout={ - "width": "100%", - "max_height": "300px", - "overflow": "auto", - "display": "none", - } - ) + def setup_computational_region_interface(self): + """Sets up the interface for users to draw and + modify the computational region on the map. - changed_region = {} - save_button_control = None - - def update_output(region): - with bottom_output_widget: - bottom_output_widget.clear_output() - print( - _( - "Region changed to: n={n}, s={s}, e={e}, w={w} " - "nsres={nsres} ewres={ewres}" - ).format(**region) - ) - - def on_rectangle_change(value): - save_button.disabled = False - bottom_output_widget.layout.display = "none" - latlon_bounds = value["new"][0] - changed_region["north"] = latlon_bounds[2]["lat"] - changed_region["south"] = latlon_bounds[0]["lat"] - changed_region["east"] = latlon_bounds[2]["lng"] - changed_region["west"] = latlon_bounds[0]["lng"] - - def toggle_region_mode(change): - nonlocal save_button_control - - if change["new"]: - region_bounds = get_region_bounds_latlon() - self.region_rectangle = self._ipyleaflet.Rectangle( - bounds=region_bounds, - color="red", - fill_color="red", - fill_opacity=0.5, - draggable=True, - transform=True, - rotation=False, - name="Computational region", - ) - self.region_rectangle.observe(on_rectangle_change, names="locations") - self.map.fit_bounds(region_bounds) - self.map.add(self.region_rectangle) - - save_button_control = self._ipyleaflet.WidgetControl( - widget=save_button, position="topright" - ) - self.map.add(save_button_control) - else: - if self.region_rectangle: - self.region_rectangle.transform = False - self.map.remove(self.region_rectangle) - self.region_rectangle = None - - save_button.disabled = True - - if save_button_control: - self.map.remove(save_button_control) - bottom_output_widget.layout.display = "none" - - def save_region(_change): - from_proj = "+proj=longlat +datum=WGS84 +no_defs" - to_proj = get_location_proj_string() - reprojected_region = reproject_region(changed_region, from_proj, to_proj) - new = update_region(reprojected_region) - bottom_output_widget.layout.display = "block" - update_output(new) - - region_mode_button.observe(toggle_region_mode, names="value") - save_button.on_click(save_region) - - region_mode_control = self._ipyleaflet.WidgetControl( - widget=region_mode_button, position="topright" + This includes creating a toggle button to activate the + region editing mode, and instantiating an InteractiveRegionController to + handle the region selection and modification functionality. + """ + return self._create_toggle_button( + icon="square-o", + tooltip=_("Click to show and edit computational region"), + controller_class=InteractiveRegionController, ) - self.map.add(region_mode_control) - output_control = self._ipyleaflet.WidgetControl( - widget=bottom_output_widget, position="bottomleft" + def _create_toggle_button(self, icon, tooltip, controller_class): + button = self._ipywidgets.ToggleButton( + icon=icon, + value=False, + tooltip=tooltip, + description="", + layout=self._ipywidgets.Layout( + width="43px", margin="0px", border="2px solid darkgrey" + ), ) - self.map.add(output_control) + controller = controller_class( + map_object=self.map, + ipyleaflet=self._ipyleaflet, + ipywidgets=self._ipywidgets, + toggle_button=button, + ) + self._controllers[button] = controller + button.observe(self._toggle_mode, names="value") + return button + + def _toggle_mode(self, change): + if change["new"]: + for button, controller in self._controllers.items(): + if button is not change["owner"]: + button.value = False + controller.deactivate() + self._controllers[change["owner"]].activate() + else: + self._controllers[change["owner"]].deactivate() def show(self): """This function returns a folium figure or ipyleaflet map object @@ -466,7 +422,17 @@ def show(self): If map has layer control enabled, additional layers cannot be added after calling show().""" if self._ipyleaflet: - self.draw_computational_region() + toggle_buttons = [ + self.setup_computational_region_interface(), + self.setup_drawing_interface(), + ] + button_box = self._ipywidgets.HBox( + toggle_buttons, layout=self._ipywidgets.Layout(align_items="flex-start") + ) + self.map.add( + self._ipyleaflet.WidgetControl(widget=button_box, position="topright") + ) + self.map.fit_bounds(self._renderer.get_bbox()) if not self.layer_control_object: @@ -490,3 +456,243 @@ def save(self, filename): :param str filename: name of html file """ self.map.save(filename) + + +class InteractiveRegionController: + """A controller for interactive region selection on a map. + + Attributes: + map: The ipyleaflet.Map object. + region_rectangle: The rectangle representing the selected region. + _ipyleaflet: The ipyleaflet module. + _ipywidgets: The ipywidgets module. + save_button: The button to save the selected region. + bottom_output_widget: The output widget to display the selected region. + changed_region (dict): The dictionary to store the changed region. + """ + + def __init__( + self, map_object, ipyleaflet, ipywidgets, **kwargs + ): # pylint: disable=unused-argument + """Initializes the InteractiveRegionController. + + :param ipyleaflet.Map map_object: The map object. + :param ipyleaflet: The ipyleaflet module. + :param ipywidgets: The ipywidgets module. + """ + self.map = map_object + self.region_rectangle = None + self._ipyleaflet = ipyleaflet + self._ipywidgets = ipywidgets + + self.save_button = self._ipywidgets.Button( + description="Update region", + tooltip="Click to update region", + disabled=True, + ) + self.bottom_output_widget = self._ipywidgets.Output( + layout={ + "width": "100%", + "max_height": "300px", + "overflow": "auto", + "display": "none", + } + ) + self.changed_region = {} + self.save_button_control = None + self.save_button.on_click(self._save_region) + + output_control = self._ipyleaflet.WidgetControl( + widget=self.bottom_output_widget, position="bottomleft" + ) + self.map.add(output_control) + + def _update_output(self, region): + """Updates the output widget with the selected region. + + :param dict region: The selected region. + """ + with self.bottom_output_widget: + self.bottom_output_widget.clear_output() + print( + _( + "Region changed to: n={n}, s={s}, e={e}, w={w} " + "nsres={nsres} ewres={ewres}" + ).format(**region) + ) + + def _on_rectangle_change(self, value): + """Handles the change event of the rectangle. + + :param dict value: The changed value. + """ + self.save_button.disabled = False + self.bottom_output_widget.layout.display = "none" + latlon_bounds = value["new"][0] + self.changed_region["north"] = latlon_bounds[2]["lat"] + self.changed_region["south"] = latlon_bounds[0]["lat"] + self.changed_region["east"] = latlon_bounds[2]["lng"] + self.changed_region["west"] = latlon_bounds[0]["lng"] + + def activate(self): + """Activates the interactive region selection.""" + region_bounds = get_region_bounds_latlon() + self.region_rectangle = self._ipyleaflet.Rectangle( + bounds=region_bounds, + color="red", + fill_color="red", + fill_opacity=0.5, + draggable=True, + transform=True, + rotation=False, + name="Computational region", + ) + self.region_rectangle.observe(self._on_rectangle_change, names="locations") + self.map.fit_bounds(region_bounds) + self.map.add(self.region_rectangle) + + self.save_button_control = self._ipyleaflet.WidgetControl( + widget=self.save_button, position="topright" + ) + self.map.add(self.save_button_control) + + def deactivate(self): + """Deactivates the interactive region selection.""" + if self.region_rectangle: + self.region_rectangle.transform = False + self.map.remove(self.region_rectangle) + self.region_rectangle = None + + if ( + hasattr(self, "save_button_control") + and self.save_button_control in self.map.controls + ): + self.map.remove(self.save_button_control) + + self.save_button.disabled = True + self.bottom_output_widget.layout.display = "none" + + def _save_region(self, _change): + """Saves the selected region. + + :param _change:Not used. + """ + from_proj = "+proj=longlat +datum=WGS84 +no_defs" + to_proj = get_location_proj_string() + reprojected_region = reproject_region(self.changed_region, from_proj, to_proj) + new = update_region(reprojected_region) + self.bottom_output_widget.layout.display = "block" + self._update_output(new) + + +class InteractiveDrawController: + """A controller for interactive drawing on a map. + + Attributes: + map: The ipyleaflet.Map object. + _ipyleaflet: The ipyleaflet module. + draw_control: The draw control. + drawn_geometries: The list of drawn geometries. + geo_json_layers: The dictionary of GeoJSON layers. + save_button_control: The save button control. + toggle_button: The toggle button activating/deactivating drawing. + """ + + def __init__(self, map_object, ipyleaflet, ipywidgets, toggle_button): + """Initializes the InteractiveDrawController. + + :param ipyleaflet.Map map_object: The map object. + :param ipyleaflet: The ipyleaflet module. + :param ipywidgets: The ipywidgets module. + :param toggle_button: The toggle button activating/deactivating drawing. + """ + self.map = map_object + self._ipyleaflet = ipyleaflet + self._ipywidgets = ipywidgets + self.toggle_button = toggle_button + self.draw_control = self._ipyleaflet.DrawControl(edit=False, remove=False) + self.drawn_geometries = [] + self.geo_json_layers = {} + self.save_button_control = None + + self.name_input = self._ipywidgets.Text( + description=_("New vector map name:"), + style={"description_width": "initial"}, + layout=self._ipywidgets.Layout(width="80%", margin="1px 1px 1px 1px"), + ) + + self.save_button = self._ipywidgets.Button( + description=_("Save"), + layout=self._ipywidgets.Layout(width="20%", margin="1px 1px 1px 1px"), + ) + + self.save_button.on_click(self._save_geometries) + + def activate(self): + """Activates the interactive drawing.""" + self.map.add_control(self.draw_control) + self.draw_control.on_draw(self._handle_draw) + self._show_interface() + + def deactivate(self): + """Deactivates the interactive drawing.""" + if self.draw_control in self.map.controls: + self.map.remove(self.draw_control) + self.draw_control.clear() + self.drawn_geometries.clear() + self._hide_interface() + + def _handle_draw(self, _, action, geo_json): + """Handles the draw event. + + :param str action: The action type. + :param dict geo_json: The GeoJSON data. + """ + if action == "created": + self.drawn_geometries.append(geo_json) + print(f"Geometry created: {geo_json}") + + def _show_interface(self): + """Shows the interface for saving the drawn geometries.""" + hbox_layout = self._ipywidgets.Layout( + display="flex", + flex_flow="row", + align_items="stretch", + width="300px", + justify_content="space-between", + ) + self.name_input.value = "" + self.save_button_control = self._ipyleaflet.WidgetControl( + widget=self._ipywidgets.HBox( + [self.name_input, self.save_button], layout=hbox_layout + ), + position="topright", + ) + + self.map.add_control(self.save_button_control) + + def _hide_interface(self): + """Hides the interface for saving the drawn geometries.""" + if self.save_button_control: + self.map.remove_control(self.save_button_control) + self.save_button_control = None + + def _save_geometries(self, _b): + """Saves the drawn geometries. + + :param _b: Not used. + """ + name = self.name_input.value + if name and self.drawn_geometries: + for geometry in self.drawn_geometries: + geometry["properties"]["name"] = name + geo_json = { + "type": "FeatureCollection", + "features": self.drawn_geometries, + } + save_vector(name, geo_json) + geo_json_layer = self._ipyleaflet.GeoJSON(data=geo_json, name=name) + self.geo_json_layers[name] = geo_json_layer + self.map.add_layer(geo_json_layer) + self.deactivate() + self.toggle_button.value = False diff --git a/python/grass/jupyter/utils.py b/python/grass/jupyter/utils.py index 64ffb51b850..366fedf7c06 100644 --- a/python/grass/jupyter/utils.py +++ b/python/grass/jupyter/utils.py @@ -10,6 +10,8 @@ # for details. """Utility functions warpping existing processes in a suitable way""" +import tempfile +import json import os import multiprocessing @@ -204,6 +206,24 @@ def get_rendering_size(region, width, height, default_width=600, default_height= return (default_width, round(default_width * region_height / region_width)) +def save_vector(name, geo_json): + """ + Saves the user drawn vector. + + :param geo_json: name of the geojson file to be saved + :param name: name with which vector should be saved + """ + with tempfile.NamedTemporaryFile( + suffix=".geojson", delete=False, mode="w" + ) as temp_file: + temp_filename = temp_file.name + for each in geo_json["features"]: + each["properties"].clear() + json.dump(geo_json, temp_file) + gs.run_command("v.import", input=temp_filename, output=name) + os.remove(temp_filename) + + def get_number_of_cores(requested, env=None): """Get the number of cores to use for multiprocessing.""" nprocs = gs.gisenv(env).get("NPROCS") From fa781d79b77bd4383625c579aa514e8dda4c62c8 Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Thu, 8 Aug 2024 02:27:13 -0400 Subject: [PATCH 106/471] r.out.mpeg: Fix potential buffer overflow issue (#4088) --- raster/r.out.mpeg/main.c | 6 +++++- raster/r.out.mpeg/write.c | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/raster/r.out.mpeg/main.c b/raster/r.out.mpeg/main.c index e92675f48d5..7782033afec 100644 --- a/raster/r.out.mpeg/main.c +++ b/raster/r.out.mpeg/main.c @@ -95,6 +95,7 @@ int main(int argc, char **argv) struct Flag *conv; int i; int *sdimp, longdim, r_out; + size_t len; G_gisinit(argv[0]); @@ -144,7 +145,10 @@ int main(int argc, char **argv) parse_command(viewopts, vfiles, &numviews, &frames); /* output file */ - strcpy(outfile, out->answer); + len = G_strlcpy(outfile, out->answer, sizeof(outfile)); + if (len >= sizeof(outfile)) { + G_fatal_error(_("Name <%s> is too long"), out->answer); + } r_out = 0; if (conv->answer) diff --git a/raster/r.out.mpeg/write.c b/raster/r.out.mpeg/write.c index f2152b3e7f6..ccb2c896562 100644 --- a/raster/r.out.mpeg/write.c +++ b/raster/r.out.mpeg/write.c @@ -197,12 +197,16 @@ void write_params(char *mpfilename, char *yfiles[], char *outfile, int frames, FILE *fp; char dir[1000], *enddir; int i, dirlen = 0; + size_t len; if (NULL == (fp = fopen(mpfilename, "w"))) G_fatal_error(_("Unable to create temporary files.")); if (!fly) { - strcpy(dir, yfiles[0]); + len = G_strlcpy(dir, yfiles[0], sizeof(dir)); + if (len >= sizeof(dir)) { + G_fatal_error(_("Directory <%s> too long"), yfiles[0]); + } enddir = strrchr(dir, '/'); if (enddir) { From c132d689b9ad8df37b1e9c7ff8014a6457797f00 Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Thu, 8 Aug 2024 04:19:14 -0400 Subject: [PATCH 107/471] lib/vector: fix potential buffer overflow (#4149) --- lib/vector/diglib/frmt.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/vector/diglib/frmt.c b/lib/vector/diglib/frmt.c index 8e3612a8dfd..66d107c6a5e 100644 --- a/lib/vector/diglib/frmt.c +++ b/lib/vector/diglib/frmt.c @@ -19,6 +19,7 @@ #include #include +#include /*! \brief Read external vector format file @@ -34,6 +35,7 @@ int dig_read_frmt_ascii(FILE *dascii, struct Format_info *finfo) char buff[2001], buf1[2001]; char *ptr; int frmt = -1; + size_t len; G_debug(3, "dig_read_frmt_ascii()"); @@ -46,7 +48,11 @@ int dig_read_frmt_ascii(FILE *dascii, struct Format_info *finfo) return -1; } - strcpy(buf1, buff); + len = G_strlcpy(buf1, buff, sizeof(buf1)); + if (len >= sizeof(buf1)) { + G_warning(_("Line <%s> is too long"), buff); + return -1; + } buf1[ptr - buff] = '\0'; ptr++; /* Search for the start of text */ @@ -98,7 +104,11 @@ int dig_read_frmt_ascii(FILE *dascii, struct Format_info *finfo) continue; } - strcpy(buf1, buff); + len = G_strlcpy(buf1, buff, sizeof(buf1)); + if (len >= sizeof(buf1)) { + G_warning(_("Line <%s> is too long"), buff); + return -1; + } buf1[ptr - buff] = '\0'; ptr++; /* Search for the start of text */ From ff83f69307d04125f23c4bbd9a91e20c38762408 Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Thu, 8 Aug 2024 15:57:09 -0400 Subject: [PATCH 108/471] r.in.xyz: Fix uninitialized variables (#4150) --- raster/r.in.xyz/main.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/raster/r.in.xyz/main.c b/raster/r.in.xyz/main.c index 0257f22a97d..a4c7dfde6ed 100644 --- a/raster/r.in.xyz/main.c +++ b/raster/r.in.xyz/main.c @@ -101,7 +101,7 @@ int main(int argc, char *argv[]) double zrange_min, zrange_max, vrange_min, vrange_max, d_tmp; char *fs; /* field delim */ off_t filesize; - int linesize; + int linesize = 0; unsigned long estimated_lines, line; int from_stdin; int can_seek; @@ -1213,7 +1213,14 @@ int scan_bounds(FILE *fp, int xcol, int ycol, int zcol, int vcol, char *fs, unsigned long line; int first, max_col; char buff[BUFFSIZE]; - double min_x, max_x, min_y, max_y, min_z, max_z, min_v, max_v; + double min_x = 0.0; + double max_x = 0.0; + double min_y = 0.0; + double max_y = 0.0; + double min_z = 0.0; + double max_z = 0.0; + double min_v = 0.0; + double max_v = 0.0; char **tokens; int ntokens; /* number of tokens */ double x, y, z, v; From 3cc689771bca57d43781c229a651d3a522fc37db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Thu, 8 Aug 2024 18:02:58 -0400 Subject: [PATCH 109/471] t.rast.accumulate: Remove check of non-existing `range` option (#4154) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit t.rast.accumulate: Remove check of non-existing ˋrangeˋ option Seems like a copy-paste artifact from another module like t.rast.accdetect Co-authored-by: Edouard Choinière --- temporal/t.rast.accumulate/t.rast.accumulate.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/temporal/t.rast.accumulate/t.rast.accumulate.py b/temporal/t.rast.accumulate/t.rast.accumulate.py index 9b9262a852f..46ea4d7c326 100644 --- a/temporal/t.rast.accumulate/t.rast.accumulate.py +++ b/temporal/t.rast.accumulate/t.rast.accumulate.py @@ -244,14 +244,6 @@ def main(): # The lower threshold space time raster dataset if lower: - if not range: - dbif.close() - gs.fatal( - _( - "You need to set the range to compute the occurrence" - " space time raster dataset" - ) - ) if lower.find("@") >= 0: lower_id = lower From 16c5bf97b2f0a1623a7b538815927b7d09975744 Mon Sep 17 00:00:00 2001 From: Riya Saxena <77328768+29riyasaxena@users.noreply.github.com> Date: Sat, 10 Aug 2024 02:21:31 +0530 Subject: [PATCH 110/471] grass.jupyter: Add Query Button to InteractiveMap (#3793) Co-authored-by: Anna Petrasova --- python/grass/jupyter/interactivemap.py | 173 +++++++++++++-- .../jupyter/testsuite/interactivemap_test.py | 16 ++ python/grass/jupyter/utils.py | 210 ++++++++++++++++++ 3 files changed, 385 insertions(+), 14 deletions(-) diff --git a/python/grass/jupyter/interactivemap.py b/python/grass/jupyter/interactivemap.py index 8ae79023fe7..c3cbb752fec 100644 --- a/python/grass/jupyter/interactivemap.py +++ b/python/grass/jupyter/interactivemap.py @@ -12,17 +12,22 @@ # for details. """Interactive visualizations map with folium or ipyleaflet""" - +import os import base64 import json from pathlib import Path from .reprojection_renderer import ReprojectionRenderer + from .utils import ( get_region_bounds_latlon, reproject_region, update_region, get_location_proj_string, save_vector, + get_region, + query_raster, + query_vector, + reproject_latlon, ) @@ -293,6 +298,13 @@ def _import_ipyleaflet(error): self.height = height self._controllers = {} + # Store vector and raster name + self.raster_name = [] + self.vector_name = [] + + # Store Region + self.region = None + if self._ipyleaflet: basemap = xyzservices.providers.query_name(tiles) if API_key and basemap.get("accessToken"): @@ -326,6 +338,7 @@ def add_vector(self, name, title=None, **kwargs): :param str title: vector name for layer control :**kwargs: keyword arguments passed to GeoJSON overlay """ + self.vector_name.append(name) Vector(name, title=title, renderer=self._renderer, **kwargs).add_to(self.map) def add_raster(self, name, title=None, **kwargs): @@ -344,6 +357,7 @@ def add_raster(self, name, title=None, **kwargs): :param str title: raster name for layer control :**kwargs: keyword arguments passed to image overlay """ + self.raster_name.append(name) Raster(name, title=title, renderer=self._renderer, **kwargs).add_to(self.map) def add_layer_control(self, **kwargs): @@ -385,21 +399,37 @@ def setup_computational_region_interface(self): controller_class=InteractiveRegionController, ) + def setup_query_interface(self): + """Sets up the query button interface. + + This includes creating a toggle button to activate the + query mode, and instantiating an InteractiveQueryController to + handle the user query. + """ + return self._create_toggle_button( + icon="info", + tooltip=_("Click to query raster and vector maps"), + controller_class=InteractiveQueryController, + ) + def _create_toggle_button(self, icon, tooltip, controller_class): button = self._ipywidgets.ToggleButton( icon=icon, value=False, tooltip=tooltip, description="", - layout=self._ipywidgets.Layout( - width="43px", margin="0px", border="2px solid darkgrey" - ), + # layout=self._ipywidgets.Layout( + # width="43px", margin="0px", #border="2px solid darkgrey" + # ), ) controller = controller_class( map_object=self.map, ipyleaflet=self._ipyleaflet, ipywidgets=self._ipywidgets, toggle_button=button, + rasters=self.raster_name, + vectors=self.vector_name, + width=self.width, ) self._controllers[button] = controller button.observe(self._toggle_mode, names="value") @@ -423,11 +453,12 @@ def show(self): added after calling show().""" if self._ipyleaflet: toggle_buttons = [ + self.setup_query_interface(), self.setup_computational_region_interface(), self.setup_drawing_interface(), ] button_box = self._ipywidgets.HBox( - toggle_buttons, layout=self._ipywidgets.Layout(align_items="flex-start") + toggle_buttons, layout=self._ipywidgets.Layout(width="150px") ) self.map.add( self._ipyleaflet.WidgetControl(widget=button_box, position="topright") @@ -462,7 +493,7 @@ class InteractiveRegionController: """A controller for interactive region selection on a map. Attributes: - map: The ipyleaflet.Map object. + map: The map object. region_rectangle: The rectangle representing the selected region. _ipyleaflet: The ipyleaflet module. _ipywidgets: The ipywidgets module. @@ -476,7 +507,7 @@ def __init__( ): # pylint: disable=unused-argument """Initializes the InteractiveRegionController. - :param ipyleaflet.Map map_object: The map object. + :param map_object: The map object. :param ipyleaflet: The ipyleaflet module. :param ipywidgets: The ipywidgets module. """ @@ -540,8 +571,8 @@ def activate(self): self.region_rectangle = self._ipyleaflet.Rectangle( bounds=region_bounds, color="red", - fill_color="red", - fill_opacity=0.5, + fill_opacity=0, + opacity=0.5, draggable=True, transform=True, rotation=False, @@ -589,27 +620,32 @@ class InteractiveDrawController: """A controller for interactive drawing on a map. Attributes: - map: The ipyleaflet.Map object. + map: The map object. _ipyleaflet: The ipyleaflet module. draw_control: The draw control. drawn_geometries: The list of drawn geometries. + self.vector_layers: List of vector layers geo_json_layers: The dictionary of GeoJSON layers. save_button_control: The save button control. toggle_button: The toggle button activating/deactivating drawing. """ - def __init__(self, map_object, ipyleaflet, ipywidgets, toggle_button): + def __init__( + self, map_object, ipyleaflet, ipywidgets, toggle_button, vectors, **kwargs + ): # pylint: disable=unused-argument """Initializes the InteractiveDrawController. - :param ipyleaflet.Map map_object: The map object. + :param map_object: The map object. :param ipyleaflet: The ipyleaflet module. :param ipywidgets: The ipywidgets module. :param toggle_button: The toggle button activating/deactivating drawing. + :param vectors: List of vector layers. """ self.map = map_object self._ipyleaflet = ipyleaflet self._ipywidgets = ipywidgets self.toggle_button = toggle_button + self.vector_layers = vectors self.draw_control = self._ipyleaflet.DrawControl(edit=False, remove=False) self.drawn_geometries = [] self.geo_json_layers = {} @@ -618,7 +654,7 @@ def __init__(self, map_object, ipyleaflet, ipywidgets, toggle_button): self.name_input = self._ipywidgets.Text( description=_("New vector map name:"), style={"description_width": "initial"}, - layout=self._ipywidgets.Layout(width="80%", margin="1px 1px 1px 1px"), + layout=self._ipywidgets.Layout(width="80%", margin="1px 1px 1px 5px"), ) self.save_button = self._ipywidgets.Button( @@ -636,9 +672,9 @@ def activate(self): def deactivate(self): """Deactivates the interactive drawing.""" + self.draw_control.clear() if self.draw_control in self.map.controls: self.map.remove(self.draw_control) - self.draw_control.clear() self.drawn_geometries.clear() self._hide_interface() @@ -693,6 +729,115 @@ def _save_geometries(self, _b): save_vector(name, geo_json) geo_json_layer = self._ipyleaflet.GeoJSON(data=geo_json, name=name) self.geo_json_layers[name] = geo_json_layer + self.vector_layers.append(name) self.map.add_layer(geo_json_layer) self.deactivate() self.toggle_button.value = False + + +class InteractiveQueryController: + """A controller for interactive querying on a map. + + Attributes: + map: The ipyleaflet.Map object. + _ipyleaflet: The ipyleaflet module. + _ipywidgets: The ipywidgets module. + raster_name: The name of the raster layer. + vector_name: The name of the vector layer. + width: The width of the map. + query_control: The query control. + + """ + + def __init__( + self, map_object, ipyleaflet, ipywidgets, rasters, vectors, width, **kwargs + ): # pylint: disable=unused-argument + """Initializes the InteractiveQueryController. + + :param map: The map object. + :param ipyleaflet: The ipyleaflet module. + :param ipywidgets: The ipywidgets module. + """ + self.map = map_object + self._ipyleaflet = ipyleaflet + self._ipywidgets = ipywidgets + self.raster_name = rasters + self.vector_name = vectors + self.width = width + self.query_control = None + + def activate(self): + """Activates the interactive querying.""" + self.map.on_interaction(self.handle_interaction) + self.map.default_style = {"cursor": "crosshair"} + + def deactivate(self): + """Deactivates the interactive querying.""" + self.map.default_style = {"cursor": "default"} + self.map.on_interaction(self.handle_interaction, remove=True) + self.clear_popups() + + def handle_interaction(self, **kwargs): + """Handles the map interaction event. + + :param kwargs: The event arguments. + """ + if kwargs.get("type") != "click": + return + + lonlat = kwargs.get("coordinates") + reprojected_coordinates = reproject_latlon(lonlat) + raster_output = self.query_raster(reprojected_coordinates) + vector_output = self.query_vector(reprojected_coordinates) + self.show_popup(lonlat, raster_output + vector_output) + + def query_raster(self, coordinates): + """Queries the raster layer. + + :param coordinates: The coordinates. + :return: The raster output. + """ + return query_raster(coordinates, self.raster_name) + + def query_vector(self, coordinates): + """Queries the vector layer. + + :param coordinates: The coordinates. + :return: The vector output. + """ + region = get_region(env=os.environ.copy()) + return query_vector( + coordinates, + self.vector_name, + 10.0 * ((region["east"] - region["west"]) / self.width), + ) + + def show_popup(self, lonlat, message_content): + """Shows a popup with the query result. + + :param lonlat: The latitude and longitude coordinates. + :param message_content: The message content. + """ + scrollable_container = self._ipywidgets.HTML( + value=( + "
          " + f"{message_content}" + "
          " + ) + ) + + popup = self._ipyleaflet.Popup( + location=lonlat, + child=scrollable_container, + close_button=False, + auto_close=True, + close_on_escape_key=False, + ) + self.map.add(popup) + + def clear_popups(self): + """Clears the popups.""" + for item in reversed(list(self.map.layers)): + if isinstance(item, self._ipyleaflet.Popup): + self.map.remove(item) diff --git a/python/grass/jupyter/testsuite/interactivemap_test.py b/python/grass/jupyter/testsuite/interactivemap_test.py index 80836cdf31e..6b8a7379548 100644 --- a/python/grass/jupyter/testsuite/interactivemap_test.py +++ b/python/grass/jupyter/testsuite/interactivemap_test.py @@ -98,6 +98,22 @@ def test_save_as_html(self): interactive_map.save(filename) self.assertFileExists(filename) + @unittest.skipIf(not can_import_ipyleaflet(), "Cannot import ipyleaflet") + def test_query_button(self): + # Create InteractiveMap with ipyleaflet backend + interactive_map = gj.InteractiveMap(map_backend="ipyleaflet") + interactive_map.add_raster("elevation") + interactive_map.add_vector("roadsmajor") + interactive_map.add_query_button() + self.assertIsNotNone(interactive_map.map) + self.assertTrue(interactive_map.query_mode is False) + # Toggle query button to activate + interactive_map.query_mode = True + self.assertTrue(interactive_map.query_mode) + # Toggle query button to deactivate + interactive_map.query_mode = False + self.assertFalse(interactive_map.query_mode) + @unittest.skipIf(not can_import_ipyleaflet(), "Cannot import ipyleaflet") def test_draw_computational_region(self): """Test the draw_computational_region method.""" diff --git a/python/grass/jupyter/utils.py b/python/grass/jupyter/utils.py index 366fedf7c06..1c1c3b1036d 100644 --- a/python/grass/jupyter/utils.py +++ b/python/grass/jupyter/utils.py @@ -92,6 +92,216 @@ def reproject_region(region, from_proj, to_proj): return region +def reproject_latlon(coord): + """Reproject coordinates + + :param coord: coordinates given as tuple (latitude, longitude) + :return: reprojected coordinates (returned as tuple) + """ + # Prepare the input coordinate string + coord_str = f"{coord[1]} {coord[0]}\n" + + # Start the m.proj command + proc = gs.start_command( + "m.proj", + input="-", + flags="i", + separator=",", + stdin=gs.PIPE, + stdout=gs.PIPE, + stderr=gs.PIPE, + ) + + proc.stdin.write(gs.encode(coord_str)) + proc.stdin.close() + proc.stdin = None + proj_output, _ = proc.communicate() + + output = gs.decode(proj_output).splitlines() + east, north, elev = map(float, output[0].split(",")) + + return east, north, elev + + +def _style_table(html_content): + """ + Use to style table displayed in popup. + + :param html_content: HTML content to be displayed + + :return str: formatted HTML content + """ + css = """ + + """ + return f"{css}{html_content}" + + +def _format_nested_table(attributes): + """ + Format nested attributes into an HTML table row. + + :param attributes: Dictionary of nested attributes to format. + + :return: str: HTML formatted string containing rows for each non-empty attribute. + """ + nested_table = "" + for sub_key, sub_value in attributes.items(): + if sub_value: + nested_table += f""" +
+ + + + """ + return nested_table + + +def _format_regular_output(items): + """ + Format attributes into an HTML table. + + :param items: List of key-value pairs (tuples) to process. + + :return: str: HTML formatted string containing rows for specified attributes. + """ + regular_output = "" + for key, value in items: + if key in {"Category", "Layer"}: + regular_output += f""" + + + + + """ + return regular_output + + +def query_raster(coord, raster_list): + """ + Queries raster data at specified coordinates. + + :param coord: Coordinates given as a tuple (latitude, longitude). + :param list raster_list: List of raster names to query. + + :return: str: HTML formatted string containing the results of the raster queries. + """ + output_list = ["""
{d}{status}{nfiles}{sfiles}{pfiles}
{sub_key}{sub_value}
{key}{value}
"""] + + for raster in raster_list: + raster_output = gs.raster.raster_what(map=raster, coord=coord) + + output = f""" + """ + + if raster in raster_output[0]: + if "value" in raster_output[0][raster]: + value = raster_output[0][raster]["value"] + output += f""" + + + + + """ + items = raster_output[0][raster].items() + formatted_output = _format_regular_output(items) + output += formatted_output + + output_list.append(output) + + if len(output_list) == 1: + return "" + + output_list.extend(("
Raster: {raster}
Value{value}
", "
")) + final_output = "".join(output_list) + return _style_table(final_output) + + +def _process_vector_output(vector, coord, distance): + """ + Process the output of a vector query. + + :param vector: Name of the vector map to query. + :param coord: Coordinates given as a tuple for querying. + :param distance: Distance within which to query the vector attributes. + + :return: str: HTML formatted string containing the vector output, including regular + attributes and any nested attribute tables. + """ + vector_output = gs.vector.vector_what(map=vector, coord=coord, distance=distance) + if len(vector_output[0]) <= 2: + return "" + + items = list(vector_output[0].items()) + attributes_output = "" + regular_output = _format_regular_output(items) + + for key, value in items: + if key == "Attributes" and isinstance(value, dict): + attributes_output = _format_nested_table(value) + + vector_html = f""" + + + Vector: {vector} + + + """ + return vector_html + attributes_output + regular_output + + +def query_vector(coord, vector_list, distance): + """ + Queries vector data at specified coordinates. + + :param coord: Coordinates given as a tuple (latitude, longitude). + :param list vector_list: List of vector names to query. + :param distance: Distance within which to query the vector attributes. + + :return: str: HTML formatted string containing the results of the vector queries. + """ + output_list = [""] + + for vector in vector_list: + vector_html = _process_vector_output(vector, coord, distance) + if vector_html: + output_list.append(vector_html) + + if len(output_list) == 1: + return "" + + output_list.extend(("
", "
")) + final_output = "".join(output_list) + return _style_table(final_output) + + def estimate_resolution(raster, mapset, location, dbase, env): """Estimates resolution of reprojected raster. From 19539757c088a494366eb6381afc699f1064e2fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Fri, 9 Aug 2024 18:15:59 -0400 Subject: [PATCH 111/471] style: Fix self-assigning-variable (PLW0127) (#4137) * style: Fix self-assigning-variable (PLW0127) * PLW0127: python/grass/temporal/core.py: The last change on this line was removing decode(mapset) for python 3. * temporal/t.rast.accumulate/t.rast.accumulate.py:283:13: PLW0127 Self-assignment of variable `upper` Following the patterns from the same file, it seems that it is the map id that is wanted * temporal/t.rast.accdetect/t.rast.accdetect.py:241:13: PLW0127 Self-assignment of variable `indicator` Usage seemed to mean to set the map id instead. * gui/wxpython/psmap/instructions.py: PLW0127 Self-assignment of variables in EstimateWidth, EstimateHeight, and EstimateSize * gui/wxpython/psmap/frame.py:2234:17: PLW0127 Self-assignment of variable `zoomFactor` * Change `not` usage to `<=` * gui/wxpython/animation/temporal_manager.py:292:21: PLW0127 Self-assignment of variable `end` Removed assignment * Apply ruff suggestion for collapsible-else-if (PLR5501) --- gui/wxpython/animation/temporal_manager.py | 35 +++++++++---------- gui/wxpython/psmap/frame.py | 4 +-- gui/wxpython/psmap/instructions.py | 28 ++++----------- pyproject.toml | 1 - python/grass/temporal/core.py | 1 - temporal/t.rast.accdetect/t.rast.accdetect.py | 2 +- .../t.rast.accumulate/t.rast.accumulate.py | 2 +- 7 files changed, 26 insertions(+), 47 deletions(-) diff --git a/gui/wxpython/animation/temporal_manager.py b/gui/wxpython/animation/temporal_manager.py index 2273ccfd9d6..9f9eafd41d0 100644 --- a/gui/wxpython/animation/temporal_manager.py +++ b/gui/wxpython/animation/temporal_manager.py @@ -288,26 +288,23 @@ def _getLabelsAndMaps(self, timeseries): followsPoint = True lastTimeseries = series end = None - else: - end = end - # interval data - if series: - # map exists, stop point mode - listOfMaps.append(series) - afterPoint = False - elif afterPoint: - # check point mode - if followsPoint: - # skip this one, already there - followsPoint = False - continue - else: - # append the last one (of point time) - listOfMaps.append(lastTimeseries) - end = None + elif series: + # map exists, stop point mode + listOfMaps.append(series) + afterPoint = False + elif afterPoint: + # check point mode + if followsPoint: + # skip this one, already there + followsPoint = False + continue else: - # append series which is None - listOfMaps.append(series) + # append the last one (of point time) + listOfMaps.append(lastTimeseries) + end = None + else: + # append series which is None + listOfMaps.append(series) timeLabels.append((start, end, unit)) return timeLabels, listOfMaps diff --git a/gui/wxpython/psmap/frame.py b/gui/wxpython/psmap/frame.py index 2334dc0acab..3cb9a9f48ae 100644 --- a/gui/wxpython/psmap/frame.py +++ b/gui/wxpython/psmap/frame.py @@ -2230,9 +2230,7 @@ def ComputeZoom(self, rect): zoomFactor = 1 # when zooming to full extent, in some cases, there was zoom # 1.01..., which causes problem - if abs(zoomFactor - 1) > 0.01: - zoomFactor = zoomFactor - else: + if abs(zoomFactor - 1) <= 0.01: zoomFactor = 1.0 if self.mouse["use"] == "zoomout": diff --git a/gui/wxpython/psmap/instructions.py b/gui/wxpython/psmap/instructions.py index 8b71c3d2df9..2ae68a91069 100644 --- a/gui/wxpython/psmap/instructions.py +++ b/gui/wxpython/psmap/instructions.py @@ -1733,17 +1733,13 @@ def Read(self, instruction, text, **kwargs): def EstimateHeight(self, raster, discrete, fontsize, cols=None, height=None): """Estimate height to draw raster legend""" if discrete == "n": - if height: - height = height - else: + if not height: height = self.unitConv.convert( value=fontsize * 10, fromUnit="point", toUnit="inch" ) if discrete == "y": - if cols: - cols = cols - else: + if not cols: cols = 1 rinfo = gs.raster_info(raster) @@ -1772,9 +1768,7 @@ def EstimateWidth( if discrete == "n": rinfo = gs.raster_info(raster) minim, maxim = rinfo["min"], rinfo["max"] - if width: - width = width - else: + if not width: width = self.unitConv.convert( value=fontsize * 2, fromUnit="point", toUnit="inch" ) @@ -1785,14 +1779,10 @@ def EstimateWidth( width += textPart elif discrete == "y": - if cols: - cols = cols - else: + if not cols: cols = 1 - if width: - width = width - else: + if not width: paperWidth = ( paperInstr["Width"] - paperInstr["Right"] - paperInstr["Left"] ) @@ -1872,14 +1862,10 @@ def Read(self, instruction, text, **kwargs): def EstimateSize(self, vectorInstr, fontsize, width=None, cols=None): """Estimate size to draw vector legend""" - if width: - width = width - else: + if not width: width = fontsize / 24.0 - if cols: - cols = cols - else: + if not cols: cols = 1 vectors = vectorInstr["list"] diff --git a/pyproject.toml b/pyproject.toml index 09f17caef7c..2b7f41dd947 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -183,7 +183,6 @@ ignore = [ "PLR6104", # non-augmented-assignment "PLR6201", # literal-membership "PLR6301", # no-self-use - "PLW0127", # self-assigning-variable "PLW0406", # import-self "PLW0602", # global-variable-not-assigned "PLW0603", # global-statement diff --git a/python/grass/temporal/core.py b/python/grass/temporal/core.py index 0df95106e0f..60479cf4e08 100644 --- a/python/grass/temporal/core.py +++ b/python/grass/temporal/core.py @@ -479,7 +479,6 @@ def get_available_temporal_mapsets(): tgis_mapsets = {} for mapset in mapsets: - mapset = mapset driver = c_library_interface.get_driver_name(mapset) database = c_library_interface.get_database_name(mapset) diff --git a/temporal/t.rast.accdetect/t.rast.accdetect.py b/temporal/t.rast.accdetect/t.rast.accdetect.py index 49953246037..887616f7a1c 100644 --- a/temporal/t.rast.accdetect/t.rast.accdetect.py +++ b/temporal/t.rast.accdetect/t.rast.accdetect.py @@ -238,7 +238,7 @@ def main(): ) ) if indicator.find("@") >= 0: - indicator = indicator + indicator_id = indicator else: indicator_id = indicator + "@" + mapset diff --git a/temporal/t.rast.accumulate/t.rast.accumulate.py b/temporal/t.rast.accumulate/t.rast.accumulate.py index 46ea4d7c326..9f711667000 100644 --- a/temporal/t.rast.accumulate/t.rast.accumulate.py +++ b/temporal/t.rast.accumulate/t.rast.accumulate.py @@ -272,7 +272,7 @@ def main(): ) if upper.find("@") >= 0: - upper = upper + upper_id = upper else: upper_id = upper + "@" + mapset From f2c8cd9666a6921d490663d6d175479f7e9d56fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Sat, 10 Aug 2024 11:58:25 -0400 Subject: [PATCH 112/471] style: Fix non-augmented-assignment (PLR6104) (#4134) --- gui/wxpython/dbmgr/base.py | 2 +- gui/wxpython/gui_core/gselect.py | 2 +- gui/wxpython/gui_core/prompt.py | 4 ++-- gui/wxpython/gui_core/pystc.py | 8 ++++---- gui/wxpython/gui_core/widgets.py | 2 +- gui/wxpython/location_wizard/dialogs.py | 8 ++++---- gui/wxpython/location_wizard/wizard.py | 2 +- gui/wxpython/mapdisp/gprint.py | 4 ++-- gui/wxpython/mapdisp/toolbars.py | 2 +- gui/wxpython/mapwin/analysis.py | 2 +- gui/wxpython/nviz/mapwindow.py | 4 ++-- gui/wxpython/nviz/tools.py | 4 ++-- gui/wxpython/psmap/frame.py | 14 ++++++------- gui/wxpython/rlisetup/sampling_frame.py | 2 +- gui/wxpython/rlisetup/wizard.py | 6 +++--- gui/wxpython/vdigit/dialogs.py | 2 +- gui/wxpython/web_services/widgets.py | 4 ++-- imagery/i.atcorr/create_iwave.py | 8 ++++---- pyproject.toml | 1 - python/grass/grassdb/manage.py | 2 +- python/grass/gunittest/reporters.py | 2 +- python/grass/imaging/images2gif.py | 2 +- python/grass/imaging/images2swf.py | 11 +++++----- python/grass/pygrass/modules/grid/grid.py | 2 +- .../raster/testsuite/test_raster_img.py | 10 +++++----- python/grass/pygrass/vector/basic.py | 3 +-- python/grass/script/core.py | 2 +- .../temporal/abstract_space_time_dataset.py | 10 +++++----- python/grass/temporal/datetime_math.py | 20 +++++++++---------- python/grass/temporal/register.py | 2 +- python/grass/temporal/temporal_algebra.py | 2 +- python/grass/temporal/temporal_granularity.py | 2 +- python/grass/temporal/temporal_operator.py | 4 ++-- .../temporal/temporal_raster_base_algebra.py | 2 +- .../grass/temporal/temporal_vector_algebra.py | 4 ++-- scripts/g.extension/g.extension.py | 10 +++++----- scripts/i.oif/i.oif.py | 2 +- scripts/i.spectral/i.spectral.py | 2 +- scripts/r.fillnulls/r.fillnulls.py | 2 +- scripts/r.grow/r.grow.py | 2 +- scripts/r.import/r.import.py | 2 +- scripts/r.in.wms/wms_drv.py | 6 +++--- scripts/r.reclass.area/r.reclass.area.py | 2 +- scripts/v.db.univar/v.db.univar.py | 2 +- .../t.rast.accumulate/t.rast.accumulate.py | 2 +- temporal/t.rast.what/t.rast.what.py | 4 ++-- .../t.vect.what.strds/t.vect.what.strds.py | 2 +- utils/gitlog2changelog.py | 2 +- 48 files changed, 100 insertions(+), 101 deletions(-) diff --git a/gui/wxpython/dbmgr/base.py b/gui/wxpython/dbmgr/base.py index 231a2a10202..1c4349bdd8c 100644 --- a/gui/wxpython/dbmgr/base.py +++ b/gui/wxpython/dbmgr/base.py @@ -3018,7 +3018,7 @@ def Populate(self, update=False): str(self.table[column]["type"]), int(self.table[column]["length"]), ) - i = i + 1 + i += 1 self.SendSizeEvent() diff --git a/gui/wxpython/gui_core/gselect.py b/gui/wxpython/gui_core/gselect.py index 215a7bba23d..7dc51b75935 100644 --- a/gui/wxpython/gui_core/gselect.py +++ b/gui/wxpython/gui_core/gselect.py @@ -2903,7 +2903,7 @@ def _draw(self, delay): coords = self._getCoords() if coords is not None: for i in range(len(coords) // 2): - i = i * 2 + i *= 2 self.pointsToDraw.AddItem(coords=(coords[i], coords[i + 1])) self._giface.updateMap.emit(render=False, renderVector=False, delay=delay) diff --git a/gui/wxpython/gui_core/prompt.py b/gui/wxpython/gui_core/prompt.py index 088465a976e..6ac9219f827 100644 --- a/gui/wxpython/gui_core/prompt.py +++ b/gui/wxpython/gui_core/prompt.py @@ -477,9 +477,9 @@ def OnKeyPressed(self, event): # move through command history list index values if event.GetKeyCode() == wx.WXK_UP: - self.cmdindex = self.cmdindex - 1 + self.cmdindex -= 1 if event.GetKeyCode() == wx.WXK_DOWN: - self.cmdindex = self.cmdindex + 1 + self.cmdindex += 1 self.cmdindex = max(self.cmdindex, 0) self.cmdindex = min(self.cmdindex, len(self.cmdbuffer) - 1) diff --git a/gui/wxpython/gui_core/pystc.py b/gui/wxpython/gui_core/pystc.py index 00db4d8a0c8..a1dfc543320 100644 --- a/gui/wxpython/gui_core/pystc.py +++ b/gui/wxpython/gui_core/pystc.py @@ -353,7 +353,7 @@ def FoldAll(self): if expanding: self.SetFoldExpanded(lineNum, True) lineNum = self.Expand(lineNum, True) - lineNum = lineNum - 1 + lineNum -= 1 else: lastChild = self.GetLastChild(lineNum, -1) self.SetFoldExpanded(lineNum, False) @@ -361,11 +361,11 @@ def FoldAll(self): if lastChild > lineNum: self.HideLines(lineNum + 1, lastChild) - lineNum = lineNum + 1 + lineNum += 1 def Expand(self, line, doExpand, force=False, visLevels=0, level=-1): lastChild = self.GetLastChild(line, level) - line = line + 1 + line += 1 while line <= lastChild: if force: @@ -392,6 +392,6 @@ def Expand(self, line, doExpand, force=False, visLevels=0, level=-1): else: line = self.Expand(line, False, force, visLevels - 1) else: - line = line + 1 + line += 1 return line diff --git a/gui/wxpython/gui_core/widgets.py b/gui/wxpython/gui_core/widgets.py index 45393f2eb1d..035c70e6fbd 100644 --- a/gui/wxpython/gui_core/widgets.py +++ b/gui/wxpython/gui_core/widgets.py @@ -1798,7 +1798,7 @@ def OnLeftDown(self, event): colLocs = [0] loc = 0 for n in range(self.GetColumnCount()): - loc = loc + self.GetColumnWidth(n) + loc += self.GetColumnWidth(n) colLocs.append(loc) col = bisect(colLocs, x + self.GetScrollPos(wx.HORIZONTAL)) - 1 diff --git a/gui/wxpython/location_wizard/dialogs.py b/gui/wxpython/location_wizard/dialogs.py index aa3954dab25..16325ddfbe1 100644 --- a/gui/wxpython/location_wizard/dialogs.py +++ b/gui/wxpython/location_wizard/dialogs.py @@ -702,9 +702,9 @@ def __init__( height += h width = max(width, w) - height = height + 5 + height += 5 height = min(height, 400) - width = width + 5 + width += 5 width = min(width, 400) # @@ -746,12 +746,12 @@ def __init__( def ClickTrans(self, event): """Get the number of the datum transform to use in g.proj""" self.transnum = event.GetSelection() - self.transnum = self.transnum - 1 + self.transnum -= 1 def GetTransform(self): """Get the number of the datum transform to use in g.proj""" self.transnum = self.translist.GetSelection() - self.transnum = self.transnum - 1 + self.transnum -= 1 return self.transnum diff --git a/gui/wxpython/location_wizard/wizard.py b/gui/wxpython/location_wizard/wizard.py index 5a206fafa52..155346b3467 100644 --- a/gui/wxpython/location_wizard/wizard.py +++ b/gui/wxpython/location_wizard/wizard.py @@ -2332,7 +2332,7 @@ def OnEnterPage(self, event): finishButton = wx.FindWindowById(wx.ID_FORWARD) if ret == 0: if datum != "": - projlabel = projlabel + "+datum=%s" % datum + projlabel += "+datum=%s" % datum self.lproj4string.SetLabel(projlabel.replace(" +", os.linesep + "+")) finishButton.Enable(True) else: diff --git a/gui/wxpython/mapdisp/gprint.py b/gui/wxpython/mapdisp/gprint.py index b72a265f035..3172f58eab5 100644 --- a/gui/wxpython/mapdisp/gprint.py +++ b/gui/wxpython/mapdisp/gprint.py @@ -59,8 +59,8 @@ def OnPrintPage(self, page): marginY = 10 # Add the margin to the graphic size - maxX = maxX + (2 * marginX) - maxY = maxY + (2 * marginY) + maxX += 2 * marginX + maxY += 2 * marginY # Get the size of the DC in pixels (w, h) = dc.GetSizeTuple() diff --git a/gui/wxpython/mapdisp/toolbars.py b/gui/wxpython/mapdisp/toolbars.py index 3589c7fd5de..4eb49846d4f 100644 --- a/gui/wxpython/mapdisp/toolbars.py +++ b/gui/wxpython/mapdisp/toolbars.py @@ -252,7 +252,7 @@ def _toolbarData(self): ), ) if self.parent.IsDockable(): - data = data + ( + data += ( ( ("docking", BaseIcons["docking"].label), BaseIcons["docking"], diff --git a/gui/wxpython/mapwin/analysis.py b/gui/wxpython/mapwin/analysis.py index 907817335f6..490a9d7e866 100644 --- a/gui/wxpython/mapwin/analysis.py +++ b/gui/wxpython/mapwin/analysis.py @@ -297,7 +297,7 @@ def MeasureDist(self, beginpt, endpt): # the mathematical theta convention (CCW from +x axis) # angle = 90 - angle if angle < 0: - angle = 360 + angle + angle += 360 mstring = "%s = %s %s\n%s = %s %s\n%s = %d %s\n%s" % ( _("segment"), diff --git a/gui/wxpython/nviz/mapwindow.py b/gui/wxpython/nviz/mapwindow.py index 398beb2b738..eaf0d266d4e 100644 --- a/gui/wxpython/nviz/mapwindow.py +++ b/gui/wxpython/nviz/mapwindow.py @@ -308,8 +308,8 @@ def ComputeMxMy(self, x, y): else: my = 0.0 - mx = mx / (1.0 - dx) - my = my / (1.0 - dy) + mx /= 1.0 - dx + my /= 1.0 - dy # Quadratic seems smoother mx *= abs(mx) diff --git a/gui/wxpython/nviz/tools.py b/gui/wxpython/nviz/tools.py index 02341420cc3..a1b0a9fcb1b 100644 --- a/gui/wxpython/nviz/tools.py +++ b/gui/wxpython/nviz/tools.py @@ -5860,8 +5860,8 @@ def Draw(self, pos, scale=False): w, h = self.GetClientSize() x, y = pos if scale: - x = x * w - y = y * h + x *= w + y *= h self.pdc.Clear() self.pdc.BeginDrawing() self.pdc.DrawLine(w // 2, h // 2, int(x), int(y)) diff --git a/gui/wxpython/psmap/frame.py b/gui/wxpython/psmap/frame.py index 3cb9a9f48ae..3effc33cd4d 100644 --- a/gui/wxpython/psmap/frame.py +++ b/gui/wxpython/psmap/frame.py @@ -569,7 +569,7 @@ def getFile(self, wildcard): filename = dlg.GetPath() suffix = suffix[dlg.GetFilterIndex()] if not os.path.splitext(filename)[1]: - filename = filename + suffix + filename += suffix elif suffix not in {os.path.splitext(filename)[1], ""}: filename = os.path.splitext(filename)[0] + suffix @@ -1509,8 +1509,8 @@ def SetPage(self): if self.currScale is None: self.currScale = min(cW / pW, cH / pH) - pW = pW * self.currScale - pH = pH * self.currScale + pW *= self.currScale + pH *= self.currScale x = cW / 2 - pW / 2 y = cH / 2 - pH / 2 @@ -2257,10 +2257,10 @@ def Zoom(self, zoomFactor, view): """Zoom to specified region, scroll view, redraw""" if not self.currScale: return - self.currScale = self.currScale * zoomFactor + self.currScale *= zoomFactor if self.currScale > 10 or self.currScale < 0.1: - self.currScale = self.currScale / zoomFactor + self.currScale /= zoomFactor return if not self.preview: # redraw paper @@ -2606,8 +2606,8 @@ def ImageRect(self): iW, iH = img.GetWidth(), img.GetHeight() self.currScale = min(float(cW) / iW, float(cH) / iH) - iW = iW * self.currScale - iH = iH * self.currScale + iW *= self.currScale + iH *= self.currScale x = cW / 2 - iW / 2 y = cH / 2 - iH / 2 return Rect(int(x), int(y), int(iW), int(iH)) diff --git a/gui/wxpython/rlisetup/sampling_frame.py b/gui/wxpython/rlisetup/sampling_frame.py index dcd8398d049..860c0b91d8f 100644 --- a/gui/wxpython/rlisetup/sampling_frame.py +++ b/gui/wxpython/rlisetup/sampling_frame.py @@ -264,7 +264,7 @@ def writeArea(self, coords, rasterName): catbuf = "=%d a\n" % self.catId polyfile.write(catbuf) - self.catId = self.catId + 1 + self.catId += 1 polyfile.close() region_settings = grass.parse_command("g.region", flags="p", delimiter=":") diff --git a/gui/wxpython/rlisetup/wizard.py b/gui/wxpython/rlisetup/wizard.py index e485d3596ba..54c6dbcd78a 100644 --- a/gui/wxpython/rlisetup/wizard.py +++ b/gui/wxpython/rlisetup/wizard.py @@ -1217,7 +1217,7 @@ def afterRegionDrawn(self, marea): if marea: self.parent.msAreaList.append(marea) - self.regioncount = self.regioncount + 1 + self.regioncount += 1 numregions = int(self.parent.samplingareapage.numregions) if self.regioncount > numregions: @@ -1715,7 +1715,7 @@ def SampleFrameChanged(self, region): # region = self.GetSampleUnitRegion() if region: self.parent.msAreaList.append(region) - self.regioncount = self.regioncount + 1 + self.regioncount += 1 drawtype = self.parent.drawunits.drawtype if self.regioncount > self.numregions: @@ -1813,7 +1813,7 @@ def __init__(self, wizard, parent): def afterRegionDrawn(self): """Function to update the title and the number of selected area""" - self.areascount = self.areascount + 1 + self.areascount += 1 if self.areascount == self.areanum: wx.FindWindowById(wx.ID_FORWARD).Enable(True) self.areaOK.Enable(False) diff --git a/gui/wxpython/vdigit/dialogs.py b/gui/wxpython/vdigit/dialogs.py index be775c5e28b..43b277b2ca7 100644 --- a/gui/wxpython/vdigit/dialogs.py +++ b/gui/wxpython/vdigit/dialogs.py @@ -591,7 +591,7 @@ def Populate(self, cats, update=False): self.SetItem(index, 1, str(cat)) self.SetItemData(index, i) itemData[i] = (str(layer), str(cat)) - i = i + 1 + i += 1 if not update: self.SetColumnWidth(0, 100) diff --git a/gui/wxpython/web_services/widgets.py b/gui/wxpython/web_services/widgets.py index 21308bf037c..83fe7a5706c 100644 --- a/gui/wxpython/web_services/widgets.py +++ b/gui/wxpython/web_services/widgets.py @@ -171,9 +171,9 @@ def _requestPage(self): style = wx.TR_DEFAULT_STYLE | wx.TR_HAS_BUTTONS | wx.TR_FULL_ROW_HIGHLIGHT if self.drv_props["req_multiple_layers"]: - style = style | wx.TR_MULTIPLE + style |= wx.TR_MULTIPLE if "WMS" not in self.ws: - style = style | wx.TR_HIDE_ROOT + style |= wx.TR_HIDE_ROOT self.list = LayersList( parent=self.req_page_panel, web_service=self.ws, style=style diff --git a/imagery/i.atcorr/create_iwave.py b/imagery/i.atcorr/create_iwave.py index 243e7a22542..022fdf8d12e 100644 --- a/imagery/i.atcorr/create_iwave.py +++ b/imagery/i.atcorr/create_iwave.py @@ -217,12 +217,12 @@ def write_cpp(bands, values, sensor, folder): # Get minimum wavelength with spectral response c = maxresponse_idx while c > 0 and fi[c - 1] > rthresh: - c = c - 1 + c -= 1 min_wavelength = np.ceil(li[0] * 1000 + (2.5 * c)) # Get maximum wavelength with spectral response c = maxresponse_idx while c < len(fi) - 1 and fi[c + 1] > rthresh: - c = c + 1 + c += 1 max_wavelength = np.floor(li[0] * 1000 + (2.5 * c)) print(" %s (%inm - %inm)" % (bands[b], min_wavelength, max_wavelength)) @@ -239,12 +239,12 @@ def write_cpp(bands, values, sensor, folder): # Get minimum wavelength with spectral response c = maxresponse_idx while c > 0 and fi[c - 1] > rthresh: - c = c - 1 + c -= 1 min_wavelength = np.ceil(li[0] * 1000 + (2.5 * c)) # Get maximum wavelength with spectral response c = maxresponse_idx while c < len(fi) - 1 and fi[c + 1] > rthresh: - c = c + 1 + c += 1 max_wavelength = np.floor(li[0] * 1000 + (2.5 * c)) print(" %s (%inm - %inm)" % (bands[b], min_wavelength, max_wavelength)) diff --git a/pyproject.toml b/pyproject.toml index 2b7f41dd947..b9f4b73bccd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -180,7 +180,6 @@ ignore = [ "PLR1704", # redefined-argument-from-local "PLR1733", # unnecessary-dict-index-lookup "PLR2004", # magic-value-comparison - "PLR6104", # non-augmented-assignment "PLR6201", # literal-membership "PLR6301", # no-self-use "PLW0406", # import-self diff --git a/python/grass/grassdb/manage.py b/python/grass/grassdb/manage.py index 28c4a87555b..77ac52e0823 100644 --- a/python/grass/grassdb/manage.py +++ b/python/grass/grassdb/manage.py @@ -173,7 +173,7 @@ def resolve_mapset_path(path, location=None, mapset=None) -> MapsetPath: from grass.grassdb.checks import is_mapset_valid if not is_mapset_valid(path) and is_mapset_valid(path / default_mapset): - path = path / default_mapset + path /= default_mapset parts = path.parts if len(parts) < 3: raise ValueError( diff --git a/python/grass/gunittest/reporters.py b/python/grass/gunittest/reporters.py index 45b1c386b27..bf82c768509 100644 --- a/python/grass/gunittest/reporters.py +++ b/python/grass/gunittest/reporters.py @@ -40,7 +40,7 @@ def keyvalue_to_text(keyvalue, sep="=", vsep="\n", isep=",", last_vertical=None) items.append("{key}{sep}{value}".format(key=key, sep=sep, value=value)) text = vsep.join(items) if last_vertical: - text = text + vsep + text += vsep return text diff --git a/python/grass/imaging/images2gif.py b/python/grass/imaging/images2gif.py index ba681dca77d..3741525a822 100644 --- a/python/grass/imaging/images2gif.py +++ b/python/grass/imaging/images2gif.py @@ -471,7 +471,7 @@ def writeGifToFile(self, fp, images, durations, loops, xys, disposes): fp.write(d) # Prepare for next round - frames = frames + 1 + frames += 1 fp.write(";") # end gif return frames diff --git a/python/grass/imaging/images2swf.py b/python/grass/imaging/images2swf.py index e98d567d83c..0a08ed9d46d 100644 --- a/python/grass/imaging/images2swf.py +++ b/python/grass/imaging/images2swf.py @@ -65,6 +65,7 @@ of a watermark in the upper left corner. """ +from __future__ import annotations import os import zlib @@ -224,7 +225,7 @@ def intToUint8(i): return int(i).to_bytes(1, "little") -def intToBits(i, n=None): +def intToBits(i: int, n: int | None = None) -> BitArray: """convert int to a string of bits (0's and 1's in a string), pad to n elements. Convert back using int(ss,2).""" ii = i @@ -233,7 +234,7 @@ def intToBits(i, n=None): bb = BitArray() while ii > 0: bb += str(ii % 2) - ii = ii >> 1 + ii >>= 1 bb.Reverse() # justify @@ -295,7 +296,7 @@ def getTypeAndLen(bb): return type, L, L2 -def signedIntToBits(i, n=None): +def signedIntToBits(i: int, n: int | None = None) -> BitArray: """convert signed int to a string of bits (0's and 1's in a string), pad to n elements. Negative numbers are stored in 2's complement bit patterns, thus positive numbers always start with a 0. @@ -311,7 +312,7 @@ def signedIntToBits(i, n=None): bb = BitArray() while ii > 0: bb += str(ii % 2) - ii = ii >> 1 + ii >>= 1 bb.Reverse() # justify @@ -489,7 +490,7 @@ def ProcessTag(self): for i in range(3): clr = self.rgb[i] if isinstance(clr, float): - clr = clr * 255 + clr *= 255 bb += intToUint8(clr) self.bytes = bb diff --git a/python/grass/pygrass/modules/grid/grid.py b/python/grass/pygrass/modules/grid/grid.py index 135daee7dc6..fb338ca973a 100644 --- a/python/grass/pygrass/modules/grid/grid.py +++ b/python/grass/pygrass/modules/grid/grid.py @@ -628,7 +628,7 @@ def define_mapset_inputs(self): if inm.type in {"raster", "vector"} and inm.value: if "@" not in inm.value: mset = get_mapset_raster(inm.value) - inm.value = inm.value + "@%s" % mset + inm.value += "@%s" % mset def run(self, patch=True, clean=True): """Run the GRASS command diff --git a/python/grass/pygrass/raster/testsuite/test_raster_img.py b/python/grass/pygrass/raster/testsuite/test_raster_img.py index c7885c8fcb8..bbc1cb2dabd 100644 --- a/python/grass/pygrass/raster/testsuite/test_raster_img.py +++ b/python/grass/pygrass/raster/testsuite/test_raster_img.py @@ -49,7 +49,7 @@ def test_resampling_to_QImg_1(self): region.adjust() tmpfile = tempfile(False) - tmpfile = tmpfile + ".png" + tmpfile += ".png" a = raster2numpy_img(self.name, region) @@ -67,7 +67,7 @@ def test_resampling_to_QImg_2(self): region.adjust() tmpfile = tempfile(False) - tmpfile = tmpfile + ".png" + tmpfile += ".png" # With array as argument array = np.ndarray((region.rows * region.cols * 4), np.uint8) @@ -88,7 +88,7 @@ def test_resampling_to_QImg_large(self): region.adjust() tmpfile = tempfile(False) - tmpfile = tmpfile + ".png" + tmpfile += ".png" # With array as argument array = np.ndarray((region.rows * region.cols * 4), np.uint8) @@ -109,7 +109,7 @@ def test_resampling_to_QImg_3(self): region.adjust() tmpfile = tempfile(False) - tmpfile = tmpfile + ".png" + tmpfile += ".png" # With array as argument array = np.ndarray((region.rows * region.cols * 4), np.uint8) @@ -130,7 +130,7 @@ def test_resampling_to_QImg_4(self): region.adjust() tmpfile = tempfile(False) - tmpfile = tmpfile + ".png" + tmpfile += ".png" array = raster2numpy_img(rastname=self.name, region=region, color="RGB") diff --git a/python/grass/pygrass/vector/basic.py b/python/grass/pygrass/vector/basic.py index 73eb5bb5557..1ce2dccedd8 100644 --- a/python/grass/pygrass/vector/basic.py +++ b/python/grass/pygrass/vector/basic.py @@ -461,8 +461,7 @@ def delete(self, cat=None, layer=1): """ if cat: self.n_del = libvect.Vect_field_cat_del(self.c_cats, layer, cat) - err_msg = "Layer(%d)/category(%d) number does not exist" - err_msg = err_msg % (layer, cat) + err_msg = "Layer(%d)/category(%d) number does not exist" % (layer, cat) else: self.n_del = libvect.Vect_cat_del(self.c_cats, layer) err_msg = "Layer: %r does not exist" % layer diff --git a/python/grass/script/core.py b/python/grass/script/core.py index d46c35d3352..a5e14aa92db 100644 --- a/python/grass/script/core.py +++ b/python/grass/script/core.py @@ -178,7 +178,7 @@ def scan(gisbase, directory): gui_path = os.path.join(gisbase, "etc", "gui", "scripts") if os.path.exists(gui_path): os.environ["PATH"] = os.getenv("PATH") + os.pathsep + gui_path - cmd = cmd + os.listdir(gui_path) + cmd += os.listdir(gui_path) return set(cmd), scripts diff --git a/python/grass/temporal/abstract_space_time_dataset.py b/python/grass/temporal/abstract_space_time_dataset.py index 890061ee64d..929f74e4421 100644 --- a/python/grass/temporal/abstract_space_time_dataset.py +++ b/python/grass/temporal/abstract_space_time_dataset.py @@ -1134,7 +1134,7 @@ def get_registered_maps_as_objects_by_granularity(self, gran=None, dbif=None): if self.is_time_absolute(): end = increment_datetime_by_string(end, gran) else: - end = end + gran + end += gran maplist = AbstractSpaceTimeDataset.resample_maplist_by_granularity( maps, start, end, gran @@ -1997,9 +1997,9 @@ def shift_map_list(maps, gran): end = increment_datetime_by_string(end, gran) map.set_absolute_time(start, end) elif map.is_time_relative(): - start = start + int(gran) + start += int(gran) if end is not None: - end = end + int(gran) + end += int(gran) map.set_relative_time(start, end, map.get_relative_time_unit()) return maps @@ -2048,9 +2048,9 @@ def shift(self, gran, dbif=None): if end is not None: end = increment_datetime_by_string(end, gran) elif self.is_time_relative(): - start = start + int(gran) + start += int(gran) if end is not None: - end = end + int(gran) + end += int(gran) date_list.append((start, end)) diff --git a/python/grass/temporal/datetime_math.py b/python/grass/temporal/datetime_math.py index 3d4b2869854..980c773bf87 100644 --- a/python/grass/temporal/datetime_math.py +++ b/python/grass/temporal/datetime_math.py @@ -483,9 +483,9 @@ def adjust_datetime_to_granularity(mydate, granularity): minutes = 0 hours = 0 if days > weekday: - days = days - weekday # this needs to be fixed + days -= weekday # this needs to be fixed else: - days = days + weekday # this needs to be fixed + days += weekday # this needs to be fixed elif has_months: # Start at the first day of the month at 00:00:00 seconds = 0 minutes = 0 @@ -660,7 +660,7 @@ def compute_datetime_delta(start, end): elif start.day == 1 and end.day == 1: d = end.month - start.month if d < 0: - d = d + 12 * comp["year"] + d += 12 * comp["year"] elif d == 0: d = 12 * comp["year"] comp["month"] = d @@ -678,9 +678,9 @@ def compute_datetime_delta(start, end): else: d = end.hour - start.hour if d < 0: - d = d + 24 + 24 * day_diff + d += 24 + 24 * day_diff else: - d = d + 24 * day_diff + d += 24 * day_diff comp["hour"] = d # Minutes @@ -690,9 +690,9 @@ def compute_datetime_delta(start, end): d = end.minute - start.minute if d != 0: if comp["hour"]: - d = d + 60 * comp["hour"] + d += 60 * comp["hour"] else: - d = d + 24 * 60 * day_diff + d += 24 * 60 * day_diff elif d == 0: if comp["hour"]: d = 60 * comp["hour"] @@ -708,11 +708,11 @@ def compute_datetime_delta(start, end): d = end.second - start.second if d != 0: if comp["minute"]: - d = d + 60 * comp["minute"] + d += 60 * comp["minute"] elif comp["hour"]: - d = d + 3600 * comp["hour"] + d += 3600 * comp["hour"] else: - d = d + 24 * 60 * 60 * day_diff + d += 24 * 60 * 60 * day_diff elif d == 0: if comp["minute"]: d = 60 * comp["minute"] diff --git a/python/grass/temporal/register.py b/python/grass/temporal/register.py index 8133eca734f..0cd339946b9 100644 --- a/python/grass/temporal/register.py +++ b/python/grass/temporal/register.py @@ -557,7 +557,7 @@ def assign_valid_time_to_map( end_time = int(end) if increment: - start_time = start_time + mult * int(increment) + start_time += mult * int(increment) if interval: end_time = start_time + int(increment) diff --git a/python/grass/temporal/temporal_algebra.py b/python/grass/temporal/temporal_algebra.py index 3792402e5ca..4d48b7119b0 100644 --- a/python/grass/temporal/temporal_algebra.py +++ b/python/grass/temporal/temporal_algebra.py @@ -1731,7 +1731,7 @@ def compare_bool_value( if count > 0: condition_value_list.append(aggregate) condition_value_list.append(boolean) - count = count + 1 + count += 1 if self.debug: print( diff --git a/python/grass/temporal/temporal_granularity.py b/python/grass/temporal/temporal_granularity.py index 9e4ab653bb1..3022c27ea77 100644 --- a/python/grass/temporal/temporal_granularity.py +++ b/python/grass/temporal/temporal_granularity.py @@ -1251,7 +1251,7 @@ def _return(output, tounit, shell): return _return(output, tounit, shell) if k == myunit: num, myunit = v.split(" ") - output = output * ast.literal_eval(num) + output *= ast.literal_eval(num) if tounit == "second" and myunit == tounit: return _return(output, tounit, shell) print(_("Probably you need to invert 'from_gran' and 'to_gran'")) diff --git a/python/grass/temporal/temporal_operator.py b/python/grass/temporal/temporal_operator.py index 572e1559611..7d91ed0f65c 100644 --- a/python/grass/temporal/temporal_operator.py +++ b/python/grass/temporal/temporal_operator.py @@ -199,7 +199,7 @@ class TemporalOperatorLexer: ) # Build the token list - tokens = tokens + tuple(relations.values()) + tokens += tuple(relations.values()) # Regular expression rules for simple tokens t_T_SELECT = r":" @@ -647,7 +647,7 @@ def p_relationlist(self, t): rel_list = [] rel_list.append(t[1]) if isinstance(t[3], list): - rel_list = rel_list + t[3] + rel_list += t[3] else: rel_list.append(t[3]) t[0] = rel_list diff --git a/python/grass/temporal/temporal_raster_base_algebra.py b/python/grass/temporal/temporal_raster_base_algebra.py index 02e28e0e435..8333fbb698e 100644 --- a/python/grass/temporal/temporal_raster_base_algebra.py +++ b/python/grass/temporal/temporal_raster_base_algebra.py @@ -478,7 +478,7 @@ def compare_cmd_value( if count > 0: cmd_value_list.append(aggregate + aggregate) cmd_value_list.append(relationmap.cmd_list) - count = count + 1 + count += 1 if self.debug: print( "compare_cmd_value", diff --git a/python/grass/temporal/temporal_vector_algebra.py b/python/grass/temporal/temporal_vector_algebra.py index d8e8474f26e..4a005a7baeb 100644 --- a/python/grass/temporal/temporal_vector_algebra.py +++ b/python/grass/temporal/temporal_vector_algebra.py @@ -314,14 +314,14 @@ def overlay_cmd_value(self, map_i, tbrelations, function, topolist=["EQUAL"]): mapainput = map_i.get_id() # Append command list of given map to result command list. if "cmd_list" in dir(map_i): - resultlist = resultlist + map_i.cmd_list + resultlist += map_i.cmd_list for topo in topolist: if topo.upper() in tbrelations.keys(): relationmaplist = tbrelations[topo.upper()] for relationmap in relationmaplist: # Append command list of given map to result command list. if "cmd_list" in dir(relationmap): - resultlist = resultlist + relationmap.cmd_list + resultlist += relationmap.cmd_list # Generate an intermediate name name = self.generate_map_name() # Put it into the removalbe map list diff --git a/scripts/g.extension/g.extension.py b/scripts/g.extension/g.extension.py index 14844a8af76..5077f85f4f4 100644 --- a/scripts/g.extension/g.extension.py +++ b/scripts/g.extension/g.extension.py @@ -766,7 +766,7 @@ def list_available_extensions(url): def get_available_toolboxes(url): """Return toolboxes available in the repository""" tdict = {} - url = url + "toolboxes.xml" + url += "toolboxes.xml" try: tree = etree_fromurl(url) for tnode in tree.findall("toolbox"): @@ -798,7 +798,7 @@ def get_toolbox_extensions(url, name): # dictionary of extensions edict = {} - url = url + "toolboxes.xml" + url += "toolboxes.xml" try: tree = etree_fromurl(url) @@ -1275,7 +1275,7 @@ def get_toolboxes_metadata(url): def install_toolbox_xml(url, name): """Update local toolboxes metadata file""" # read metadata from remote server (toolboxes) - url = url + "toolboxes.xml" + url += "toolboxes.xml" data = get_toolboxes_metadata(url) if not data: gs.warning(_("No addons metadata available")) @@ -2487,7 +2487,7 @@ def resolve_install_prefix(path, to_system): # ensure dir sep at the end for cases where path is used as URL and pasted # together with file names if not path.endswith(os.path.sep): - path = path + os.path.sep + path += os.path.sep os.environ["GRASS_PREFIX_ADDON_BASE"] = os.path.abspath( path ) # make likes absolute paths @@ -2516,7 +2516,7 @@ def resolve_xmlurl_prefix(url, source=None): # the exact action depends on subsequent code (somewhere) if not url.endswith("/"): - url = url + "/" + url += "/" return url diff --git a/scripts/i.oif/i.oif.py b/scripts/i.oif/i.oif.py index 4f03da94db8..cfee7890120 100755 --- a/scripts/i.oif/i.oif.py +++ b/scripts/i.oif/i.oif.py @@ -107,7 +107,7 @@ def main(): if not proc[bandp].stdout.closed: pout[bandp] = proc[bandp].communicate()[0] proc[bandp].wait() - n = n + 1 + n += 1 # wait for jobs to finish, collect the output for band in bands: diff --git a/scripts/i.spectral/i.spectral.py b/scripts/i.spectral/i.spectral.py index 9ac8cd09bf3..d40c1076169 100755 --- a/scripts/i.spectral/i.spectral.py +++ b/scripts/i.spectral/i.spectral.py @@ -92,7 +92,7 @@ def write2textf(what, output): outf = open(output, "w") i = 0 for row in enumerate(what): - i = i + 1 + i += 1 outf.write("%d, %s\n" % (i, row)) outf.close() diff --git a/scripts/r.fillnulls/r.fillnulls.py b/scripts/r.fillnulls/r.fillnulls.py index 2627bca9ffe..a53d19a8a72 100755 --- a/scripts/r.fillnulls/r.fillnulls.py +++ b/scripts/r.fillnulls/r.fillnulls.py @@ -320,7 +320,7 @@ def main(): holename = prefix + "hole_" + cat # GTC Hole is a NULL area in a raster map gs.message(_("Filling hole %s of %s") % (hole_n, len(cat_list))) - hole_n = hole_n + 1 + hole_n += 1 # cut out only CAT hole for processing try: gs.run_command( diff --git a/scripts/r.grow/r.grow.py b/scripts/r.grow/r.grow.py index 86fdfd9ebc3..dce83b35f39 100755 --- a/scripts/r.grow/r.grow.py +++ b/scripts/r.grow/r.grow.py @@ -110,7 +110,7 @@ def main(): if metric == "euclidean": metric = "squared" - radius = radius * radius + radius *= radius # check if input file exists if not gs.find_file(input)["file"]: diff --git a/scripts/r.import/r.import.py b/scripts/r.import/r.import.py index a3f952c037b..7f13e79c336 100644 --- a/scripts/r.import/r.import.py +++ b/scripts/r.import/r.import.py @@ -289,7 +289,7 @@ def main(): env=src_env, ) gs.run_command("g.region", vector=tgtregion, env=src_env) - parameters["flags"] = parameters["flags"] + region_flag + parameters["flags"] += region_flag try: gs.run_command("r.in.gdal", env=src_env, **parameters) except CalledModuleError: diff --git a/scripts/r.in.wms/wms_drv.py b/scripts/r.in.wms/wms_drv.py index 4628c60b8ef..1e7a90e9938 100644 --- a/scripts/r.in.wms/wms_drv.py +++ b/scripts/r.in.wms/wms_drv.py @@ -494,7 +494,7 @@ def __init__(self, params, bbox, region, tile_size, proj_srs, cap_file=None): self.last_tile_x = False if self.last_tile_x_size != 0: self.last_tile_x = True - self.num_tiles_x = self.num_tiles_x + 1 + self.num_tiles_x += 1 self.num_tiles_y = rows // self.tile_rows self.last_tile_y_size = rows % self.tile_rows @@ -507,7 +507,7 @@ def __init__(self, params, bbox, region, tile_size, proj_srs, cap_file=None): self.last_tile_y = False if self.last_tile_y_size != 0: self.last_tile_y = True - self.num_tiles_y = self.num_tiles_y + 1 + self.num_tiles_y += 1 self.tile_bbox = dict(self.bbox) self.tile_bbox["maxx"] = self.bbox["minx"] + self.tile_length_x @@ -816,7 +816,7 @@ def _getMatSize(self, tile_mat, mat_set_link): mat_num_bbox[i[0]] = int(i_tag.text) if i[0] in {"max_row", "max_col"}: - mat_num_bbox[i[0]] = mat_num_bbox[i[0]] - 1 + mat_num_bbox[i[0]] -= 1 break return mat_num_bbox diff --git a/scripts/r.reclass.area/r.reclass.area.py b/scripts/r.reclass.area/r.reclass.area.py index 1fa8a868973..5d6fc1292bc 100755 --- a/scripts/r.reclass.area/r.reclass.area.py +++ b/scripts/r.reclass.area/r.reclass.area.py @@ -184,7 +184,7 @@ def rmarea(infile, outfile, thresh, coef): # transform user input from hectares to meters because currently v.clean # rmarea accept only meters as threshold - thresh = thresh * 10000.0 + thresh *= 10000.0 vectfile = "%s_vect_%s" % (infile.split("@")[0], outfile) TMPRAST.append(vectfile) gs.run_command("r.to.vect", input=infile, output=vectfile, type="area") diff --git a/scripts/v.db.univar/v.db.univar.py b/scripts/v.db.univar/v.db.univar.py index ab604db2b71..996c965b4fb 100755 --- a/scripts/v.db.univar/v.db.univar.py +++ b/scripts/v.db.univar/v.db.univar.py @@ -94,7 +94,7 @@ def main(): if not passflags: passflags = "g" else: - passflags = passflags + "g" + passflags += "g" output_format = options["format"] try: diff --git a/temporal/t.rast.accumulate/t.rast.accumulate.py b/temporal/t.rast.accumulate/t.rast.accumulate.py index 9f711667000..3ad82fe9ed3 100644 --- a/temporal/t.rast.accumulate/t.rast.accumulate.py +++ b/temporal/t.rast.accumulate/t.rast.accumulate.py @@ -344,7 +344,7 @@ def main(): map.set_relative_time( gran_start, gran_end, input_strds.get_relative_time_unit() ) - gran_start = gran_start + granularity + gran_start += granularity gran_list.append(copy(map)) gran_list_low.append(copy(map)) gran_list_up.append(copy(map)) diff --git a/temporal/t.rast.what/t.rast.what.py b/temporal/t.rast.what/t.rast.what.py index 18a25a61080..9fa4ad5d793 100755 --- a/temporal/t.rast.what/t.rast.what.py +++ b/temporal/t.rast.what/t.rast.what.py @@ -597,9 +597,9 @@ def one_point_per_timerow_output( matrix.append(cols[:2]) if vcat: - matrix[i] = matrix[i] + cols[4:] + matrix[i] += cols[4:] else: - matrix[i] = matrix[i] + cols[3:] + matrix[i] += cols[3:] first = False diff --git a/temporal/t.vect.what.strds/t.vect.what.strds.py b/temporal/t.vect.what.strds/t.vect.what.strds.py index 5d29ea3b5fb..6ea36e79579 100755 --- a/temporal/t.vect.what.strds/t.vect.what.strds.py +++ b/temporal/t.vect.what.strds/t.vect.what.strds.py @@ -151,7 +151,7 @@ def main(): False, dbif, ) - aggreagated_map_name = aggreagated_map_name + "_0" + aggreagated_map_name += "_0" if new_map is None: continue # We overwrite the raster_maps list diff --git a/utils/gitlog2changelog.py b/utils/gitlog2changelog.py index e9627567e05..2626321d6fb 100755 --- a/utils/gitlog2changelog.py +++ b/utils/gitlog2changelog.py @@ -98,7 +98,7 @@ elif len(line) == 4: messageFound = True elif len(message) == 0: - message = message + line.strip() + message += line.strip() else: message = message + " " + line.strip() # If this line is hit all of the files have been stored for this commit From 71f8a8141106a9f94b539d4882b2afb00394f1bc Mon Sep 17 00:00:00 2001 From: Kriti Birda <164247895+kritibirda26@users.noreply.github.com> Date: Sun, 11 Aug 2024 23:00:33 +0530 Subject: [PATCH 113/471] g.region: Add JSON support (#3941) * g.region: add JSON support * add test and documentation * add gmt, wms to output * fix CI build issues * separate projection code and name fields --- general/g.region/Makefile | 2 +- general/g.region/g.region.html | 53 +++++ general/g.region/local_proto.h | 7 +- general/g.region/main.c | 44 +++- general/g.region/printwindow.c | 220 ++++++++++++++---- general/g.region/testsuite/test_g_region.py | 70 ++++++ .../grass/pygrass/modules/interface/module.py | 7 +- 7 files changed, 357 insertions(+), 46 deletions(-) diff --git a/general/g.region/Makefile b/general/g.region/Makefile index a288c856113..9d6c871c9f4 100644 --- a/general/g.region/Makefile +++ b/general/g.region/Makefile @@ -2,7 +2,7 @@ MODULE_TOPDIR = ../.. PGM = g.region -LIBES = $(GPROJLIB) $(VECTORLIB) $(DIG2LIB) $(RASTER3DLIB) $(RASTERLIB) $(GISLIB) $(MATHLIB) $(PROJLIB) +LIBES = $(GPROJLIB) $(VECTORLIB) $(DIG2LIB) $(RASTER3DLIB) $(RASTERLIB) $(GISLIB) $(MATHLIB) $(PROJLIB) $(PARSONLIB) DEPENDENCIES = $(GPROJDEP) $(VECTORDEP) $(DIG2DEP) $(RASTER3DDEP) $(RASTERDEP) $(GISDEP) EXTRA_INC = $(VECT_INC) $(PROJINC) EXTRA_CFLAGS = $(VECT_CFLAGS) diff --git a/general/g.region/g.region.html b/general/g.region/g.region.html index 4716aee2e38..85d75a058df 100644 --- a/general/g.region/g.region.html +++ b/general/g.region/g.region.html @@ -457,6 +457,59 @@

Using g.region in a shell in combination with GDAL

Here the input raster map does not have to match the project's coordinate reference system since it is reprojected on the fly. +

JSON Output

+
+g.region -p format=json
+
+
+{
+    "projection": "99 (Lambert Conformal Conic)",
+    "zone": 0,
+    "datum": "nad83",
+    "ellipsoid": "a=6378137 es=0.006694380022900787",
+    "region": {
+        "north": 320000,
+        "south": 10000,
+        "west": 120000,
+        "east": 935000,
+        "ns-res": 500,
+        "ns-res3": 1000,
+        "ew-res": 500,
+        "ew-res3": 1000
+    },
+    "top": 500,
+    "bottom": -500,
+    "tbres": 100,
+    "rows": 620,
+    "rows3": 310,
+    "cols": 1630,
+    "cols3": 815,
+    "depths": 10,
+    "cells": 1010600,
+    "cells3": 2526500
+}
+
+ +
+g.region -l format=json
+
+
+{
+    "nw_long": -78.688888505507336,
+    "nw_lat": 35.743893244701788,
+    "ne_long": -78.669097826118957,
+    "ne_lat": 35.743841072010554,
+    "se_long": -78.669158624787542,
+    "se_lat": 35.728968779193615,
+    "sw_long": -78.688945667963168,
+    "sw_lat": 35.729020942542441,
+    "center_long": -78.679022655614958,
+    "center_lat": 35.736431420327719,
+    "rows": 165,
+    "cols": 179
+}
+
+

SEE ALSO

diff --git a/general/g.region/local_proto.h b/general/g.region/local_proto.h index 781fce01e81..31c1bd21e65 100644 --- a/general/g.region/local_proto.h +++ b/general/g.region/local_proto.h @@ -13,10 +13,15 @@ #define PRINT_GMT 0x200 #define PRINT_WMS 0x400 +#include + +enum OutputFormat { PLAIN, SHELL, JSON }; + /* zoom.c */ int zoom(struct Cell_head *, const char *, const char *); /* printwindow.c */ -void print_window(struct Cell_head *, int, int); +void print_window(struct Cell_head *, int, int, enum OutputFormat, + JSON_Object *); #endif diff --git a/general/g.region/main.c b/general/g.region/main.c index 06c33a2e8ce..c36eabe4c36 100644 --- a/general/g.region/main.c +++ b/general/g.region/main.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include "local_proto.h" @@ -41,6 +42,9 @@ int main(int argc, char *argv[]) char **rast_ptr, **vect_ptr; int pix; bool update_file = false; + enum OutputFormat format; + JSON_Value *root_value; + JSON_Object *root_object; struct GModule *module; struct { @@ -51,7 +55,7 @@ int main(int argc, char *argv[]) struct { struct Option *north, *south, *east, *west, *top, *bottom, *res, *nsres, *ewres, *res3, *tbres, *rows, *cols, *save, *region, *raster, - *raster3d, *align, *zoom, *vect, *grow; + *raster3d, *align, *zoom, *vect, *grow, *format; } parm; G_gisinit(argv[0]); @@ -358,6 +362,13 @@ int main(int argc, char *argv[]) parm.save->gisprompt = "new,windows,region"; parm.save->guisection = _("Effects"); + parm.format = G_define_standard_option(G_OPT_F_FORMAT); + parm.format->options = "plain,shell,json"; + parm.format->descriptions = _("plain;Plain text output;" + "shell;shell script style output;" + "json;JSON (JavaScript Object Notation);"); + parm.format->guisection = _("Print"); + G_option_required( flag.dflt, flag.savedefault, flag.print, flag.lprint, flag.eprint, flag.center, flag.gmt_style, flag.wms_style, flag.dist_res, flag.nangle, @@ -421,6 +432,24 @@ int main(int argc, char *argv[]) print_flag |= PRINT_REG; } + if (strcmp(parm.format->answer, "json") == 0) { + format = JSON; + root_value = json_value_init_object(); + if (root_value == NULL) { + G_fatal_error( + _("Failed to initialize JSON object. Out of memory?")); + } + root_object = json_object(root_value); + } + else if (strcmp(parm.format->answer, "shell") == 0 || + (print_flag & PRINT_SH)) { + format = SHELL; + print_flag |= PRINT_SH; + } + else { + format = PLAIN; + } + if (flag.dflt->answer) update_file = true; else @@ -903,7 +932,18 @@ int main(int argc, char *argv[]) } /* / flag.savedefault->answer */ if (print_flag) - print_window(&window, print_flag, flat_flag); + print_window(&window, print_flag, flat_flag, format, root_object); + + if (format == JSON) { + char *serialized_string = NULL; + serialized_string = json_serialize_to_string_pretty(root_value); + if (serialized_string == NULL) { + G_fatal_error(_("Failed to initialize pretty JSON string.")); + } + puts(serialized_string); + json_free_serialized_string(serialized_string); + json_value_free(root_value); + } exit(EXIT_SUCCESS); } diff --git a/general/g.region/printwindow.c b/general/g.region/printwindow.c index 9d8d91cb447..c36600864ff 100644 --- a/general/g.region/printwindow.c +++ b/general/g.region/printwindow.c @@ -21,7 +21,8 @@ static double get_shift(double east) return shift; } -void print_window(struct Cell_head *window, int print_flag, int flat_flag) +void print_window(struct Cell_head *window, int print_flag, int flat_flag, + enum OutputFormat format, JSON_Object *root_object) { const char *prj, *datum, *ellps; int x, width = 11; @@ -31,9 +32,13 @@ void print_window(struct Cell_head *window, int print_flag, int flat_flag) char buf[50]; char *sep = "\n"; + double d_nsres, d_ewres, d_nsres3, d_ewres3, d_tbres; double ew_dist1, ew_dist2, ns_dist1, ns_dist2; double longitude, latitude; + JSON_Value *region_value; + JSON_Object *region; + if (print_flag & PRINT_SH) { x = G_projection() == PROJECTION_LL ? -1 : 0; if (flat_flag) @@ -46,6 +51,12 @@ void print_window(struct Cell_head *window, int print_flag, int flat_flag) G_format_northing(window->south, south, x); G_format_easting(window->east, east, x); G_format_easting(window->west, west, x); + + d_ewres = window->ew_res; + d_ewres3 = window->ew_res3; + d_nsres = window->ns_res; + d_nsres3 = window->ns_res3; + d_tbres = window->tb_res; G_format_resolution(window->ew_res, ewres, x); G_format_resolution(window->ew_res3, ewres3, x); G_format_resolution(window->ns_res, nsres, x); @@ -81,15 +92,20 @@ void print_window(struct Cell_head *window, int print_flag, int flat_flag) /* flag.dist_res */ if (print_flag & PRINT_METERS) { - sprintf(ewres, "%.8f", ((ew_dist1 + ew_dist2) / 2) / window->cols); + d_ewres = ((ew_dist1 + ew_dist2) / 2) / window->cols; + sprintf(ewres, "%.8f", d_ewres); G_trim_decimal(ewres); - sprintf(ewres3, "%.8f", ((ew_dist1 + ew_dist2) / 2) / window->cols3); + d_ewres3 = ((ew_dist1 + ew_dist2) / 2) / window->cols3; + sprintf(ewres3, "%.8f", d_ewres3); G_trim_decimal(ewres3); - sprintf(nsres, "%.8f", ((ns_dist1 + ns_dist2) / 2) / window->rows); + d_nsres = ((ns_dist1 + ns_dist2) / 2) / window->rows; + sprintf(nsres, "%.8f", d_nsres); G_trim_decimal(nsres); - sprintf(nsres3, "%.8f", ((ns_dist1 + ns_dist2) / 2) / window->rows3); + d_nsres3 = ((ns_dist1 + ns_dist2) / 2) / window->rows3; + sprintf(nsres3, "%.8f", d_nsres3); G_trim_decimal(nsres3); - sprintf(tbres, "%.8f", (window->top - window->bottom) / window->depths); + d_tbres = (window->top - window->bottom) / window->depths; + sprintf(tbres, "%.8f", d_tbres); G_trim_decimal(tbres); } @@ -99,14 +115,22 @@ void print_window(struct Cell_head *window, int print_flag, int flat_flag) if (!prj) prj = "** unknown **"; - if (print_flag & PRINT_SH) { + switch (format) { + case SHELL: fprintf(stdout, "projection=%d%s", window->proj, sep); fprintf(stdout, "zone=%d%s", window->zone, sep); - } - else { + break; + case PLAIN: fprintf(stdout, "%-*s %d (%s)\n", width, "projection:", window->proj, prj); fprintf(stdout, "%-*s %d\n", width, "zone:", window->zone); + break; + case JSON: + json_object_dotset_number(root_object, "projection.code", + window->proj); + json_object_dotset_string(root_object, "projection.name", prj); + json_object_set_number(root_object, "zone", window->zone); + break; } /* don't print datum/ellipsoid in XY-Locations */ @@ -140,13 +164,22 @@ void print_window(struct Cell_head *window, int print_flag, int flat_flag) } */ - if (!(print_flag & PRINT_SH)) { - fprintf(stdout, "%-*s %s\n", width, "datum:", datum); - fprintf(stdout, "%-*s %s\n", width, "ellipsoid:", ellps); + switch (format) { + case JSON: + json_object_set_string(root_object, "datum", datum); + json_object_set_string(root_object, "ellipsoid", ellps); + break; + default: + if (!(print_flag & PRINT_SH)) { + fprintf(stdout, "%-*s %s\n", width, "datum:", datum); + fprintf(stdout, "%-*s %s\n", width, "ellipsoid:", ellps); + } + break; } } - if (print_flag & PRINT_SH) { + switch (format) { + case SHELL: fprintf(stdout, "n=%s%s", north, sep); fprintf(stdout, "s=%s%s", south, sep); fprintf(stdout, "w=%s%s", west, sep); @@ -188,8 +221,8 @@ void print_window(struct Cell_head *window, int print_flag, int flat_flag) (long)window->rows3 * window->cols3 * window->depths, sep); #endif - } - else { + break; + case PLAIN: fprintf(stdout, "%-*s %s\n", width, "north:", north); fprintf(stdout, "%-*s %s\n", width, "south:", south); fprintf(stdout, "%-*s %s\n", width, "west:", west); @@ -232,6 +265,42 @@ void print_window(struct Cell_head *window, int print_flag, int flat_flag) fprintf(stdout, "%-*s %ld\n", width, "cells3:", (long)window->rows3 * window->cols3 * window->depths); #endif + break; + case JSON: + region_value = json_value_init_object(); + region = json_object(region_value); + json_object_set_number(region, "north", window->north); + json_object_set_number(region, "south", window->south); + json_object_set_number(region, "west", window->west); + json_object_set_number(region, "east", window->east); + json_object_set_number(region, "ns-res", d_nsres); + json_object_set_number(region, "ns-res3", d_nsres3); + json_object_set_number(region, "ew-res", d_ewres); + json_object_set_number(region, "ew-res3", d_ewres3); + json_object_set_value(root_object, "region", region_value); + json_object_set_number(root_object, "top", window->top); + json_object_set_number(root_object, "bottom", window->bottom); + json_object_set_number(root_object, "tbres", d_tbres); + json_object_set_number(root_object, "rows", window->rows); + json_object_set_number(root_object, "rows3", window->rows3); + json_object_set_number(root_object, "cols", window->cols); + json_object_set_number(root_object, "cols3", window->cols3); + json_object_set_number(root_object, "depths", window->depths); + +#ifdef HAVE_LONG_LONG_INT + json_object_set_number(root_object, "cells", + (long long)window->rows * window->cols); + json_object_set_number(root_object, "cells3", + (long long)window->rows3 * window->cols3 * + window->depths); +#else + json_object_set_number(root_object, "cells", + (long)window->rows * window->cols); + json_object_set_number(root_object, "cells3", + (long)window->rows3 * window->cols3 * + window->depths); +#endif + break; } } @@ -337,7 +406,8 @@ void print_window(struct Cell_head *window, int print_flag, int flat_flag) loc = longitude; lac = latitude; - if (print_flag & PRINT_SH) { + switch (format) { + case SHELL: fprintf(stdout, "nw_long=%.8f%snw_lat=%.8f%s", lo1, sep, la1, sep); fprintf(stdout, "ne_long=%.8f%sne_lat=%.8f%s", lo2, sep, la2, @@ -348,8 +418,8 @@ void print_window(struct Cell_head *window, int print_flag, int flat_flag) sep); fprintf(stdout, "center_long=%.8f%s", loc, sep); fprintf(stdout, "center_lat=%.8f%s", lac, sep); - } - else { + break; + case PLAIN: G_format_easting(lo1, buf, PROJECTION_LL); fprintf(stdout, "%-*s long: %s ", width, "north-west corner:", buf); @@ -379,16 +449,35 @@ void print_window(struct Cell_head *window, int print_flag, int flat_flag) G_format_northing(lac, buf, PROJECTION_LL); fprintf(stdout, "%-*s %11s\n", width, "center latitude:", buf); + break; + case JSON: + json_object_set_number(root_object, "nw_long", lo1); + json_object_set_number(root_object, "nw_lat", la1); + json_object_set_number(root_object, "ne_long", lo2); + json_object_set_number(root_object, "ne_lat", la2); + json_object_set_number(root_object, "se_long", lo3); + json_object_set_number(root_object, "se_lat", la3); + json_object_set_number(root_object, "sw_long", lo4); + json_object_set_number(root_object, "sw_lat", la4); + json_object_set_number(root_object, "center_long", loc); + json_object_set_number(root_object, "center_lat", lac); + break; } if (!(print_flag & PRINT_REG)) { - if (print_flag & PRINT_SH) { + switch (format) { + case SHELL: fprintf(stdout, "rows=%d%s", window->rows, sep); fprintf(stdout, "cols=%d%s", window->cols, sep); - } - else { + break; + case PLAIN: fprintf(stdout, "%-*s %d\n", width, "rows:", window->rows); fprintf(stdout, "%-*s %d\n", width, "cols:", window->cols); + break; + case JSON: + json_object_set_number(root_object, "rows", window->rows); + json_object_set_number(root_object, "cols", window->cols); + break; } } } @@ -406,12 +495,13 @@ void print_window(struct Cell_head *window, int print_flag, int flat_flag) /* flag.eprint */ if (print_flag & PRINT_EXTENT) { - if (print_flag & PRINT_SH) { + switch (format) { + case SHELL: fprintf(stdout, "ns_extent=%f%s", window->north - window->south, sep); fprintf(stdout, "ew_extent=%f%s", window->east - window->west, sep); - } - else { + break; + case PLAIN: if (G_projection() != PROJECTION_LL) { fprintf(stdout, "%-*s %f\n", width, "north-south extent:", window->north - window->south); @@ -426,18 +516,26 @@ void print_window(struct Cell_head *window, int print_flag, int flat_flag) PROJECTION_LL); fprintf(stdout, "%-*s %s\n", width, "east-west extent:", buf); } + break; + case JSON: + json_object_set_number(root_object, "ns_extent", + window->north - window->south); + json_object_set_number(root_object, "ew_extent", + window->east - window->west); + break; } } /* flag.center */ if (print_flag & PRINT_CENTER) { - if (print_flag & PRINT_SH) { + switch (format) { + case SHELL: fprintf(stdout, "center_easting=%f%s", (window->west + window->east) / 2., sep); fprintf(stdout, "center_northing=%f%s", (window->north + window->south) / 2., sep); - } - else { + break; + case PLAIN: if (G_projection() != PROJECTION_LL) { fprintf(stdout, "%-*s %f\n", width, "center easting:", (window->west + window->east) / 2.); @@ -452,20 +550,47 @@ void print_window(struct Cell_head *window, int print_flag, int flat_flag) PROJECTION_LL); fprintf(stdout, "%-*s %s\n", width, "east-west center:", buf); } + break; + case JSON: + json_object_set_number(root_object, "center_easting", + (window->west + window->east) / 2.); + json_object_set_number(root_object, "center_northing", + (window->north + window->south) / 2.); + break; } } /* flag.gmt_style */ - if (print_flag & PRINT_GMT) - fprintf(stdout, "%s/%s/%s/%s\n", west, east, south, north); + if (print_flag & PRINT_GMT) { + char gmt[120]; + switch (format) { + case JSON: + snprintf(gmt, 120, "%s/%s/%s/%s", west, east, south, north); + json_object_set_string(root_object, "GMT", gmt); + break; + default: + fprintf(stdout, "%s/%s/%s/%s\n", west, east, south, north); + break; + } + } /* flag.wms_style */ if (print_flag & PRINT_WMS) { - G_format_northing(window->north, north, -1); - G_format_northing(window->south, south, -1); - G_format_easting(window->east, east, -1); - G_format_easting(window->west, west, -1); - fprintf(stdout, "bbox=%s,%s,%s,%s%s", west, south, east, north, sep); + char wms[150]; + switch (format) { + case JSON: + snprintf(wms, 150, "bbox=%s,%s,%s,%s", west, south, east, north); + json_object_set_string(root_object, "WMS", wms); + break; + default: + G_format_northing(window->north, north, -1); + G_format_northing(window->south, south, -1); + G_format_easting(window->east, east, -1); + G_format_easting(window->west, west, -1); + fprintf(stdout, "bbox=%s,%s,%s,%s%s", west, south, east, north, + sep); + break; + } } /* flag.nangle */ @@ -540,11 +665,17 @@ void print_window(struct Cell_head *window, int print_flag, int flat_flag) #endif } - if (print_flag & PRINT_SH) + switch (format) { + case SHELL: fprintf(stdout, "converge_angle=%f%s", convergence, sep); - else + break; + case PLAIN: fprintf(stdout, "%-*s %f\n", width, "convergence angle:", convergence); + break; + case JSON: + json_object_set_number(root_object, "converge_angle", convergence); + } } /* flag.bbox @@ -722,7 +853,8 @@ void print_window(struct Cell_head *window, int print_flag, int flat_flag) sh_ll_e += get_shift(sh_ll_e); /* print the largest bounding box */ - if (print_flag & PRINT_SH) { + switch (format) { + case SHELL: fprintf(stdout, "ll_n=%.8f%s", sh_ll_n, sep); fprintf(stdout, "ll_s=%.8f%s", sh_ll_s, sep); fprintf(stdout, "ll_w=%.8f%s", sh_ll_w, sep); @@ -731,8 +863,8 @@ void print_window(struct Cell_head *window, int print_flag, int flat_flag) fprintf(stdout, "ll_clon=%.8f%s", loc, sep); fprintf(stdout, "ll_clat=%.8f%s", (sh_ll_n + sh_ll_s) / 2., sep); - } - else { + break; + case PLAIN: G_format_northing(sh_ll_n, buf, PROJECTION_LL); fprintf(stdout, "%-*s %s\n", width, "north latitude:", buf); G_format_northing(sh_ll_s, buf, PROJECTION_LL); @@ -746,6 +878,16 @@ void print_window(struct Cell_head *window, int print_flag, int flat_flag) fprintf(stdout, "%-*s %s\n", width, "center longitude:", buf); G_format_northing((sh_ll_n + sh_ll_s) / 2., buf, PROJECTION_LL); fprintf(stdout, "%-*s %s\n", width, "center latitude:", buf); + break; + case JSON: + json_object_set_number(root_object, "ll_n", sh_ll_n); + json_object_set_number(root_object, "ll_s", sh_ll_s); + json_object_set_number(root_object, "ll_w", sh_ll_w); + json_object_set_number(root_object, "ll_e", sh_ll_e); + /* center of the largest bounding box */ + json_object_set_number(root_object, "ll_clon", loc); + json_object_set_number(root_object, "ll_clat", + (sh_ll_n + sh_ll_s) / 2.); } /*It should be calculated which number of rows and cols we have in diff --git a/general/g.region/testsuite/test_g_region.py b/general/g.region/testsuite/test_g_region.py index 7362c062f1d..f174c19af67 100644 --- a/general/g.region/testsuite/test_g_region.py +++ b/general/g.region/testsuite/test_g_region.py @@ -3,6 +3,8 @@ @author Anna Petrasova """ +import json + from grass.gunittest.case import TestCase from grass.gunittest.gmodules import call_module import grass.script as gs @@ -46,6 +48,74 @@ def test_f_flag(self): line = call_module("g.region", flags="fglecn3", capture_stdout=True) self.assertEqual(1, len(line.splitlines())) + def test_format_json(self): + """Test json format""" + expected = { + "projection": {"code": 99, "name": "Lambert Conformal Conic"}, + "zone": 0, + "datum": "nad83", + "ellipsoid": "a=6378137 es=0.006694380022900787", + "region": { + "north": 320000, + "south": 10000, + "west": 120000, + "east": 935000, + "ns-res": 500, + "ns-res3": 1000, + "ew-res": 500, + "ew-res3": 1000, + }, + "top": 500, + "bottom": -500, + "tbres": 100, + "rows": 620, + "rows3": 310, + "cols": 1630, + "cols3": 815, + "depths": 10, + "cells": 1010600, + "cells3": 2526500, + "GMT": "120000/935000/10000/320000", + "WMS": "bbox=120000,10000,935000,320000", + "se_lat": 33.78822598716895, + "se_long": -75.48643633119754, + "sw_lat": 33.722662075471355, + "sw_long": -84.28378827453474, + "ew_extent": 815000, + "ll_clat": 35.17852919352316, + "ll_clon": -79.91588285974797, + "ll_e": -75.36388301356145, + "ll_n": 36.634396311574974, + "ll_s": 33.722662075471355, + "ll_w": -84.46788270593447, + "ne_lat": 36.58069555564894, + "ne_long": -75.36388301356145, + "ns_extent": 310000, + "nw_lat": 36.51287343603797, + "nw_long": -84.46788270593447, + "center_easting": 527500, + "center_lat": 35.23406270825775, + "center_long": -79.90206638014922, + "center_northing": 165000, + "converge_angle": -0.5206458828734528, + } + + output = call_module("g.region", flags="plectwmn3b", format="json") + output_json = json.loads(output) + + expected_ellps = expected.pop("ellipsoid").split(" ") + received_ellps = output_json.pop("ellipsoid").split(" ") + self.assertEqual(expected_ellps[0], received_ellps[0]) + self.assertAlmostEqual( + float(expected_ellps[1][3:]), float(received_ellps[1][3:]), places=6 + ) + self.assertCountEqual(list(expected.keys()), list(output_json.keys())) + for key in expected: + if isinstance(expected[key], float): + self.assertAlmostEqual(expected[key], output_json[key], places=6) + else: + self.assertEqual(expected[key], output_json[key]) + if __name__ == "__main__": from grass.gunittest.main import test diff --git a/python/grass/pygrass/modules/interface/module.py b/python/grass/pygrass/modules/interface/module.py index b9c298b4a6f..c8a1e723a05 100644 --- a/python/grass/pygrass/modules/interface/module.py +++ b/python/grass/pygrass/modules/interface/module.py @@ -334,11 +334,11 @@ class Module: >>> region.flags.u = True >>> region.flags["3"].value = True # set numeric flags >>> region.get_bash() - 'g.region -p -3 -u' + 'g.region format=plain -p -3 -u' >>> new_region = copy.deepcopy(region) >>> new_region.inputs.res = "10" >>> new_region.get_bash() - 'g.region res=10 -p -3 -u' + 'g.region res=10 format=plain -p -3 -u' >>> neighbors = Module("r.neighbors") >>> neighbors.inputs.input = "mapA" @@ -952,7 +952,8 @@ class MultiModule: ... set_temp_region=True, ... ) >>> str(mm) - 'g.region -p ; g.region -p ; g.region -p ; g.region -p ; g.region -p' + 'g.region format=plain -p ; g.region format=plain -p ; g.region format=plain -p ; \ +g.region format=plain -p ; g.region format=plain -p' >>> t = mm.run() >>> isinstance(t, Process) True From 8fa7eb860b9d96dd261337de942fef3ce3b2b48f Mon Sep 17 00:00:00 2001 From: Kriti Birda <164247895+kritibirda26@users.noreply.github.com> Date: Mon, 12 Aug 2024 19:52:27 +0530 Subject: [PATCH 114/471] g.region: fix ruff lint error in tests (#4167) --- general/g.region/testsuite/test_g_region.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/general/g.region/testsuite/test_g_region.py b/general/g.region/testsuite/test_g_region.py index f174c19af67..a5cb0e5dc3b 100644 --- a/general/g.region/testsuite/test_g_region.py +++ b/general/g.region/testsuite/test_g_region.py @@ -110,11 +110,11 @@ def test_format_json(self): float(expected_ellps[1][3:]), float(received_ellps[1][3:]), places=6 ) self.assertCountEqual(list(expected.keys()), list(output_json.keys())) - for key in expected: - if isinstance(expected[key], float): - self.assertAlmostEqual(expected[key], output_json[key], places=6) + for key, value in expected.items(): + if isinstance(value, float): + self.assertAlmostEqual(value, output_json[key], places=6) else: - self.assertEqual(expected[key], output_json[key]) + self.assertEqual(value, output_json[key]) if __name__ == "__main__": From 0fb66119dbb3a5438629d6a7a4731c6d74f13eea Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 13 Aug 2024 02:06:17 +0000 Subject: [PATCH 115/471] CI(deps): Update ruff to v0.5.7 (#4156) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * CI(deps): Update ruff to v0.5.7 * Fix new RUF031 rule violations (incorrectly-parenthesized-tuple-in-subscript) * Fix new ruff errors for C409: Unnecessary list comprehension passed to `tuple()` (rewrite as a generator) --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Edouard Choinière <27212526+echoix@users.noreply.github.com> --- .github/workflows/python-code-quality.yml | 2 +- .pre-commit-config.yaml | 2 +- gui/wxpython/timeline/frame.py | 6 +++--- python/grass/imaging/images2gif.py | 4 ++-- python/grass/pygrass/raster/buffer.py | 6 +++--- python/grass/pygrass/raster/category.py | 2 +- python/grass/pygrass/vector/geometry.py | 2 +- python/grass/pygrass/vector/testsuite/test_vector.py | 4 ++-- scripts/d.rast.leg/d.rast.leg.py | 2 +- scripts/v.in.wfs/v.in.wfs.py | 2 +- 10 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index 2cf7390084b..ed26d501a6a 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -36,7 +36,7 @@ jobs: # renovate: datasource=pypi depName=bandit BANDIT_VERSION: "1.7.9" # renovate: datasource=pypi depName=ruff - RUFF_VERSION: "0.5.6" + RUFF_VERSION: "0.5.7" runs-on: ${{ matrix.os }} permissions: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 035239f08f2..2dc9dc65f35 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -37,7 +37,7 @@ repos: ) - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.5.6 + rev: v0.5.7 hooks: # Run the linter. - id: ruff diff --git a/gui/wxpython/timeline/frame.py b/gui/wxpython/timeline/frame.py index bba9b7a3717..f69dcc89aef 100644 --- a/gui/wxpython/timeline/frame.py +++ b/gui/wxpython/timeline/frame.py @@ -589,11 +589,11 @@ def AddDataset(self, type_, yrange, xranges, datasetName): if type_ == "bar": self.data[yrange] = {"name": datasetName} for i, (start, end) in enumerate(xranges): - self.data[yrange][(start, end)] = i + self.data[yrange][start, end] = i elif type_ == "point": - self.data[(yrange, yrange)] = {"name": datasetName} + self.data[yrange, yrange] = {"name": datasetName} for i, start in enumerate(xranges): - self.data[(yrange, yrange)][(start, start)] = i + self.data[yrange, yrange][start, start] = i def GetInformation(self, x, y): keys = None diff --git a/python/grass/imaging/images2gif.py b/python/grass/imaging/images2gif.py index 3741525a822..a4dc2dc0824 100644 --- a/python/grass/imaging/images2gif.py +++ b/python/grass/imaging/images2gif.py @@ -857,14 +857,14 @@ def altersingle(self, alpha, i, b, g, r): def geta(self, alpha, rad): try: - return self.a_s[(alpha, rad)] + return self.a_s[alpha, rad] except KeyError: length = rad * 2 - 1 mid = length / 2 q = np.array(list(range(mid - 1, -1, -1)) + list(range(-1, mid))) a = alpha * (rad * rad - q * q) / (rad * rad) a[mid] = 0 - self.a_s[(alpha, rad)] = a + self.a_s[alpha, rad] = a return a def alterneigh(self, alpha, rad, i, b, g, r): diff --git a/python/grass/pygrass/raster/buffer.py b/python/grass/pygrass/raster/buffer.py index 1a2325e2f4c..51420f16610 100644 --- a/python/grass/pygrass/raster/buffer.py +++ b/python/grass/pygrass/raster/buffer.py @@ -4,11 +4,11 @@ _CELL = ("int", "intp", "int8", "int16", "int32", "int64") -CELL = tuple([getattr(np, attr) for attr in _CELL if hasattr(np, attr)]) +CELL = tuple(getattr(np, attr) for attr in _CELL if hasattr(np, attr)) _FCELL = "float", "float16", "float32" -FCELL = tuple([getattr(np, attr) for attr in _FCELL if hasattr(np, attr)]) +FCELL = tuple(getattr(np, attr) for attr in _FCELL if hasattr(np, attr)) _DCELL = "float64", "float128" -DCELL = tuple([getattr(np, attr) for attr in _DCELL if hasattr(np, attr)]) +DCELL = tuple(getattr(np, attr) for attr in _DCELL if hasattr(np, attr)) class Buffer(np.ndarray): diff --git a/python/grass/pygrass/raster/category.py b/python/grass/pygrass/raster/category.py index 31ef5f3ed76..0a5e42093ae 100644 --- a/python/grass/pygrass/raster/category.py +++ b/python/grass/pygrass/raster/category.py @@ -97,7 +97,7 @@ def __dict__(self): diz = {} for cat in self.__iter__(): label, min_cat, max_cat = cat - diz[(min_cat, max_cat)] = label + diz[min_cat, max_cat] = label return diz def __repr__(self): diff --git a/python/grass/pygrass/vector/geometry.py b/python/grass/pygrass/vector/geometry.py index 99d7e2249cb..bc6ad32f10a 100644 --- a/python/grass/pygrass/vector/geometry.py +++ b/python/grass/pygrass/vector/geometry.py @@ -1137,7 +1137,7 @@ def from_wkt(self, wkt): if match: self.reset() for coord in match.groups()[0].strip().split(","): - self.append(tuple([float(e) for e in coord.split(" ")])) + self.append(tuple(float(e) for e in coord.split(" "))) else: return None diff --git a/python/grass/pygrass/vector/testsuite/test_vector.py b/python/grass/pygrass/vector/testsuite/test_vector.py index a57856eca45..5d16333c31f 100644 --- a/python/grass/pygrass/vector/testsuite/test_vector.py +++ b/python/grass/pygrass/vector/testsuite/test_vector.py @@ -37,9 +37,9 @@ def test_getitem_slice(self): """Test that getitem handle correctly the slice starting from 1""" vcoords = ((10.0, 6.0), (12.0, 6.0)) with VectorTopo(self.tmpname, mode="r") as vect: - coords = tuple([pnt.coords() for pnt in vect[:3]]) + coords = tuple(pnt.coords() for pnt in vect[:3]) self.assertTupleEqual(vcoords, coords) - coords = tuple([pnt.coords() for pnt in vect[1:3]]) + coords = tuple(pnt.coords() for pnt in vect[1:3]) self.assertTupleEqual(vcoords, coords) self.vect.close() diff --git a/scripts/d.rast.leg/d.rast.leg.py b/scripts/d.rast.leg/d.rast.leg.py index 46c46928d9b..85cb423f9e9 100755 --- a/scripts/d.rast.leg/d.rast.leg.py +++ b/scripts/d.rast.leg/d.rast.leg.py @@ -104,7 +104,7 @@ def main(): # fixes trunk r64459 s = s.split(":")[1] - f = tuple([float(x) for x in s.split()]) + f = tuple(float(x) for x in s.split()) gs.run_command("d.erase") os.environ["GRASS_RENDER_FILE_READ"] = "TRUE" diff --git a/scripts/v.in.wfs/v.in.wfs.py b/scripts/v.in.wfs/v.in.wfs.py index bd127c3bea0..9f53840edcf 100755 --- a/scripts/v.in.wfs/v.in.wfs.py +++ b/scripts/v.in.wfs/v.in.wfs.py @@ -127,7 +127,7 @@ def main(): wfs_url += request_base if options["name"]: - if tuple([int(x) for x in version_num.split(".")]) >= (2, 0, 0): + if tuple(int(x) for x in version_num.split(".")) >= (2, 0, 0): wfs_url += "&TYPENAMES=" + options["name"] else: wfs_url += "&TYPENAME=" + options["name"] From cb013cccc40978c7db9f1c4ace7924263d00a6db Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 12 Aug 2024 22:33:46 -0400 Subject: [PATCH 116/471] CI(deps): Update actions/upload-artifact action to v4.3.6 (#4143) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/actions/create-upload-suggestions/action.yml | 4 ++-- .github/workflows/macos.yml | 2 +- .github/workflows/osgeo4w.yml | 2 +- .github/workflows/pytest.yml | 2 +- .github/workflows/python-code-quality.yml | 4 ++-- .github/workflows/ubuntu.yml | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/actions/create-upload-suggestions/action.yml b/.github/actions/create-upload-suggestions/action.yml index 201d566bffc..6a4cf1201dd 100644 --- a/.github/actions/create-upload-suggestions/action.yml +++ b/.github/actions/create-upload-suggestions/action.yml @@ -177,7 +177,7 @@ runs: echo "diff-file-name=${INPUT_DIFF_FILE_NAME}" >> "${GITHUB_OUTPUT}" env: INPUT_DIFF_FILE_NAME: ${{ steps.tool-name-safe.outputs.diff-file-name }} - - uses: actions/upload-artifact@89ef406dd8d7e03cfd12d9e0a4a378f454709029 # v4.3.5 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 id: upload-diff if: >- ${{ (steps.files_changed.outputs.files_changed == 'true') && @@ -200,7 +200,7 @@ runs: echo 'Suggestions can only be added near to lines changed in this PR.' echo 'If any fixes can be added as code suggestions, they will be added shortly from another workflow.' } >> "${GITHUB_STEP_SUMMARY}" - - uses: actions/upload-artifact@89ef406dd8d7e03cfd12d9e0a4a378f454709029 # v4.3.5 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 id: upload-changes if: >- ${{ always() && diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 34397e88873..6fd2442659c 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -72,7 +72,7 @@ jobs: nc_spm_full_v2alpha2.tar.gz" - name: Make HTML test report available if: ${{ always() }} - uses: actions/upload-artifact@89ef406dd8d7e03cfd12d9e0a4a378f454709029 # v4.3.5 + uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 with: name: testreport-macOS path: testreport diff --git a/.github/workflows/osgeo4w.yml b/.github/workflows/osgeo4w.yml index 5ad5a4723f9..ba1217620c3 100644 --- a/.github/workflows/osgeo4w.yml +++ b/.github/workflows/osgeo4w.yml @@ -83,7 +83,7 @@ jobs: - name: Make HTML test report available if: ${{ always() }} - uses: actions/upload-artifact@89ef406dd8d7e03cfd12d9e0a4a378f454709029 # v4.3.5 + uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 with: name: testreport-${{ matrix.os }} path: testreport diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 6707b1be4d3..17b6203ffe1 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -114,7 +114,7 @@ jobs: token: ${{ secrets.CODECOV_TOKEN }} - name: Make python-only code coverage test report available if: ${{ !cancelled() }} - uses: actions/upload-artifact@89ef406dd8d7e03cfd12d9e0a4a378f454709029 # v4.3.5 + uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 with: name: python-codecoverage-report-${{ matrix.os }}-${{ matrix.python-version }} path: coverage_html_report diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index ed26d501a6a..42c7913bdb1 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -129,7 +129,7 @@ jobs: bandit -c pyproject.toml -iii -r . -f sarif -o bandit.sarif --exit-zero - name: Upload Bandit Scan Results - uses: actions/upload-artifact@89ef406dd8d7e03cfd12d9e0a4a378f454709029 # v4.3.5 + uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 with: name: bandit.sarif path: bandit.sarif @@ -201,7 +201,7 @@ jobs: cp -rp dist.$ARCH/docs/html/libpython sphinx-grass - name: Make Sphinx documentation available - uses: actions/upload-artifact@89ef406dd8d7e03cfd12d9e0a4a378f454709029 # v4.3.5 + uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 with: name: sphinx-grass path: sphinx-grass diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index a858f8984a9..4f70486e05e 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -147,7 +147,7 @@ jobs: - name: Make HTML test report available if: ${{ always() }} - uses: actions/upload-artifact@89ef406dd8d7e03cfd12d9e0a4a378f454709029 # v4.3.5 + uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 with: name: testreport-${{ matrix.os }}-${{ matrix.config }}-${{ matrix.extra-include }} path: testreport From d8dd58938fffdd002a63e62b054dfcbf69b525b9 Mon Sep 17 00:00:00 2001 From: Riya Saxena <77328768+29riyasaxena@users.noreply.github.com> Date: Tue, 13 Aug 2024 19:37:23 +0530 Subject: [PATCH 117/471] grass.jupyter: add authorship and add missing docstrings (#4159) --- python/grass/jupyter/baseseriesmap.py | 15 +++++++++++++++ python/grass/jupyter/interactivemap.py | 1 + python/grass/jupyter/seriesmap.py | 3 ++- python/grass/jupyter/timeseriesmap.py | 3 ++- python/grass/jupyter/utils.py | 24 +++++++++++++++++------- 5 files changed, 37 insertions(+), 9 deletions(-) diff --git a/python/grass/jupyter/baseseriesmap.py b/python/grass/jupyter/baseseriesmap.py index e75a0ea1637..634e172f3c5 100644 --- a/python/grass/jupyter/baseseriesmap.py +++ b/python/grass/jupyter/baseseriesmap.py @@ -1,3 +1,18 @@ +# +# AUTHOR(S): Riya Saxena <29riyasaxena AT gmail> +# +# PURPOSE: This module provides the base class for interactive visualizations +# used by `TimeSeriesMap` and `SeriesMap` in Jupyter Notebooks. It +# includes methods for rendering visualizations and creating interactive +# sliders to navigate through time-series or series data, while reducing +# redundancy and enhancing functionality. +# +# COPYRIGHT: (C) 2024 Riya Saxena, and by the GRASS Development Team +# +# This program is free software under the GNU General Public +# License (>=v2). Read the file COPYING that comes with GRASS +# for details. + """Base class for SeriesMap and TimeSeriesMap""" import os diff --git a/python/grass/jupyter/interactivemap.py b/python/grass/jupyter/interactivemap.py index c3cbb752fec..b6a5ccfb3e8 100644 --- a/python/grass/jupyter/interactivemap.py +++ b/python/grass/jupyter/interactivemap.py @@ -1,6 +1,7 @@ # # AUTHOR(S): Caitlin Haedrich # Anna Petrasova +# Riya Saxena <29riyasaxena AT gmail> # # PURPOSE: This module contains functions for interactive visualizations # in Jupyter Notebooks. diff --git a/python/grass/jupyter/seriesmap.py b/python/grass/jupyter/seriesmap.py index 86d46018864..8615ad488fb 100644 --- a/python/grass/jupyter/seriesmap.py +++ b/python/grass/jupyter/seriesmap.py @@ -1,11 +1,12 @@ # MODULE: grass.jupyter.seriesmap # # AUTHOR(S): Caitlin Haedrich +# Riya Saxena <29riyasaxena AT gmail> # # PURPOSE: This module contains functions for visualizing series of rasters in # Jupyter Notebooks # -# COPYRIGHT: (C) 2022 Caitlin Haedrich, and by the GRASS Development Team +# COPYRIGHT: (C) 2022-2024 Caitlin Haedrich, and by the GRASS Development Team # # This program is free software under the GNU General Public # License (>=v2). Read the file COPYING that comes with GRASS diff --git a/python/grass/jupyter/timeseriesmap.py b/python/grass/jupyter/timeseriesmap.py index 0c7f11a562f..3ac94bea93e 100644 --- a/python/grass/jupyter/timeseriesmap.py +++ b/python/grass/jupyter/timeseriesmap.py @@ -1,11 +1,12 @@ # MODULE: grass.jupyter.timeseriesmap # # AUTHOR(S): Caitlin Haedrich +# Riya Saxena <29riyasaxena AT gmail> # # PURPOSE: This module contains functions for visualizing raster and vector # space-time datasets in Jupyter Notebooks # -# COPYRIGHT: (C) 2022 Caitlin Haedrich, and by the GRASS Development Team +# COPYRIGHT: (C) 2022-2024 Caitlin Haedrich, and by the GRASS Development Team # # This program is free software under the GNU General Public # License (>=v2). Read the file COPYING that comes with GRASS diff --git a/python/grass/jupyter/utils.py b/python/grass/jupyter/utils.py index 1c1c3b1036d..b06553d2d3b 100644 --- a/python/grass/jupyter/utils.py +++ b/python/grass/jupyter/utils.py @@ -1,9 +1,10 @@ # # AUTHOR(S): Caitlin Haedrich +# Riya Saxena <29riyasaxena AT gmail> # # PURPOSE: This module contains utility functions for InteractiveMap. # -# COPYRIGHT: (C) 2021-2022 Caitlin Haedrich, and by the GRASS Development Team +# COPYRIGHT: (C) 2021-2024 Caitlin Haedrich, and by the GRASS Development Team # # This program is free software under the GNU General Public # License (>=v2). Read the file COPYING that comes with GRASS @@ -417,8 +418,7 @@ def get_rendering_size(region, width, height, default_width=600, default_height= def save_vector(name, geo_json): - """ - Saves the user drawn vector. + """Saves the user drawn vector. :param geo_json: name of the geojson file to be saved :param name: name with which vector should be saved @@ -435,7 +435,13 @@ def save_vector(name, geo_json): def get_number_of_cores(requested, env=None): - """Get the number of cores to use for multiprocessing.""" + """Get the number of cores to use for multiprocessing. + + :param int requested: Desired number of cores. + :param dict env: Optional process environment. + + :return int: Number of cores to use, constrained by system availability. + """ nprocs = gs.gisenv(env).get("NPROCS") if nprocs is not None: return int(nprocs) @@ -448,7 +454,11 @@ def get_number_of_cores(requested, env=None): def get_region_bounds_latlon(): - """Gets the current computational region bounds in latlon.""" + """Gets the current computational region bounds in latlon. + + :return list of tuples: represent the southwest and northeast + corners of the region in (latitude, longitude) format. + """ region = gs.parse_command("g.region", flags="gbp") return [ (float(region["ll_s"]), float(region["ll_w"])), @@ -459,6 +469,7 @@ def get_region_bounds_latlon(): def update_region(region): """Updates the computational region bounds. + :param dict region: region dictionary :return: the new region """ current = gs.region() @@ -484,8 +495,7 @@ def save_gif( text_size=12, text_color="gray", ): - """ - Creates a GIF animation + """Creates a GIF animation param list input_files: list of paths to source param str output_filename: destination gif filename From 9df9f2da4c0ab8144aeb2e705e1174a1e90e7c43 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 13 Aug 2024 17:28:24 +0000 Subject: [PATCH 118/471] CI(deps): Update docker/build-push-action action to v6.7.0 (#4173) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 9040030669c..1f60ed4c6d7 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -76,7 +76,7 @@ jobs: password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build and push id: docker_build - uses: docker/build-push-action@16ebe778df0e7752d2cfcbd924afdbbd89c1a755 # v6.6.1 + uses: docker/build-push-action@5cd11c3a4ced054e52742c5fd54dca954e0edd85 # v6.7.0 with: push: true pull: true From d6729290d7c0bd6bbd8d81088c57d17e482c0f39 Mon Sep 17 00:00:00 2001 From: Riya Saxena <77328768+29riyasaxena@users.noreply.github.com> Date: Wed, 14 Aug 2024 18:32:51 +0530 Subject: [PATCH 119/471] grass.jupyter: Add documentation of InteractiveMap Features and Parallelization in jupyter_tutorial.ipynb (#4164) Co-authored-by: Anna Petrasova --- doc/notebooks/jupyter_tutorial.ipynb | 32 +++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/doc/notebooks/jupyter_tutorial.ipynb b/doc/notebooks/jupyter_tutorial.ipynb index 34d39c29737..66bbabef1b3 100644 --- a/doc/notebooks/jupyter_tutorial.ipynb +++ b/doc/notebooks/jupyter_tutorial.ipynb @@ -30,6 +30,7 @@ "source": [ "# Import Python standard library and IPython packages we need.\n", "import subprocess\n", + "import time\n", "import sys\n", "\n", "# Ask GRASS GIS where its Python packages are.\n", @@ -42,7 +43,7 @@ "import grass.jupyter as gj\n", "\n", "# Start GRASS Session\n", - "session = gj.init(\"~/data/grassdata\", \"nc_basic_spm_grass7\", \"user1\")\n", + "session = gj.init(\"../../../grassdata\", \"nc_basic_spm_grass7\", \"user1\")\n", "\n", "# Set computational region to the elevation raster.\n", "gs.run_command(\"g.region\", raster=\"elevation\")" @@ -213,6 +214,17 @@ "The `InteractiveMap` class displays *GRASS GIS* rasters and vectors with [*folium*](http://python-visualization.github.io/folium/) or [*ipyleaflet*](https://ipyleaflet.readthedocs.io/en/latest/)." ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "When using ipyleaflet, the map display includes a button for querying data (ℹ️), a button for displaying and editing computational region (◻️), and a button for drawing simple vector geometries (🖉) that can be saved as a GRASS native vector map.\n", + "\n", + "1. Query raster/vector layers: Click the **info** button (ℹ️) to enable query mode, then select a point on the map to retrieve information. Toggle the button off when finished.\n", + "2. Draw and save geometries: Click the **pencil** button (🖉) to draw shapes on the map, name the vector map, and save it. The geometry will be added as a new layer and the drawing tool will close automatically.\n", + "3. View and edit the computational region: Click the **computational region** button (◻️) to display and optionally adjust the region. Update it by clicking **Update region**, then toggle the button off when done.\n" + ] + }, { "cell_type": "code", "execution_count": null, @@ -445,6 +457,20 @@ "!r.slope.aspect elevation=elevation slope=slope" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Managing Core Usage with SeriesMap\n", + "\n", + "The `SeriesMap` module automatically uses multiple CPU cores to improve rendering performance. If you want to control the number of cores used, you can set the `NPROCS` [variable](https://grass.osgeo.org/grass83/manuals/variables.html#list-of-selected-grass-gisenv-variables).\n", + "To specify the number of cores for rendering, set the `NPROCS` variable to the desired number of cores before running `gj.SeriesMap`. For example, to use 4 cores, execute:\n", + "\n", + " ```\n", + " gs.run_command(\"g.gisenv\", set=\"NPROCS=4\")\n", + " ```" + ] + }, { "cell_type": "code", "execution_count": null, @@ -453,7 +479,7 @@ "source": [ "series = gj.SeriesMap(height=500)\n", "series.add_rasters([\"elevation\", \"elevation_shade\", \"slope\"])\n", - "# series.add_vectors([\"streams\", \"streets\", \"viewpoints\"])\n", + "series.add_vectors([\"streams\", \"streets\", \"viewpoints\"])\n", "series.d_vect(map=\"streets\")\n", "series.d_barscale()\n", "series.show() # Create Slider" @@ -528,7 +554,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.10" + "version": "3.10.12" } }, "nbformat": 4, From 2ec5af9658516b4b4a4cf776051cdeb38007de9f Mon Sep 17 00:00:00 2001 From: Linda Karlovska <49241681+lindakarlovska@users.noreply.github.com> Date: Wed, 14 Aug 2024 17:27:26 +0200 Subject: [PATCH 120/471] wxGUI/history: Order commands by time and optimize node refresh (#3896) New command ordering, node refresh optimization, and refactoring of the history tree. Co-authored-by: Anna Petrasova --- gui/wxpython/history/tree.py | 349 +++++++++++++++++++++++------------ 1 file changed, 232 insertions(+), 117 deletions(-) diff --git a/gui/wxpython/history/tree.py b/gui/wxpython/history/tree.py index 9119473d9ff..b616ef44698 100644 --- a/gui/wxpython/history/tree.py +++ b/gui/wxpython/history/tree.py @@ -65,6 +65,14 @@ def label(self): else: return self.data["name"] + @property + def time_sort(self): + if "day" in self.data.keys(): + return self.data["day"] + if "timestamp" in self.data.keys(): + return self.data["timestamp"] + return None + def dayToLabel(self, day): """ Convert day (midnight timestamp) to a day node label. @@ -93,9 +101,39 @@ def dayToLabel(self, day): return _("{base_date}, {year}").format(base_date=base_date, year=day.year) +class HistoryTreeModel(TreeModel): + """Customized tree model defined in core/treemodel.py.""" + + def __init__(self, nodeClass): + """Constructor creates root node. + + :param nodeClass: class which is used for creating nodes + """ + super().__init__(nodeClass=nodeClass) + + def SortChildren(self, node): + """ + Sort children chronologically based on time_sort property. + Leave out day nodes that include commands with missing info. + + :param node: node whose children are sorted + """ + if node.children and node.time_sort != OLD_DATE: + node.children.sort(key=lambda node: node.time_sort, reverse=True) + + def UpdateNode(self, node, **kwargs): + """Update node attributes. + + :param node: node to be updated + :param kwargs: key-value pairs of attributes to update + """ + for key, value in kwargs.items(): + node.data[key] = value + + class HistoryBrowserTree(CTreeView): """Tree structure visualizing and managing history of executed commands. - Uses virtual tree and model defined in core/treemodel.py. + Uses customized virtual tree and model. """ def __init__( @@ -110,7 +148,7 @@ def __init__( | wx.TR_FULL_ROW_HIGHLIGHT, ): """History Browser Tree constructor.""" - self._model = TreeModel(HistoryBrowserNode) + self._model = HistoryTreeModel(HistoryBrowserNode) self._orig_model = self._model super().__init__(parent=parent, model=self._model, id=wx.ID_ANY, style=style) @@ -149,19 +187,6 @@ def __init__( self.itemActivated.connect(self.OnDoubleClick) self.contextMenu.connect(self.OnRightClick) - def _sortDays(self): - """Sort day nodes from earliest to oldest.""" - if self._model.root.children: - self._model.root.children.sort( - key=lambda node: node.data["day"], reverse=True - ) - - def _refreshTree(self): - """Refresh tree models""" - self._sortDays() - self.SetModel(copy.deepcopy(self._model)) - self._orig_model = self._model - def _resetSelectVariables(self): """Reset variables related to item selection.""" self.selected_day = [] @@ -209,92 +234,115 @@ def _popupMenuEmpty(self): self.PopupMenu(menu) menu.Destroy() - def _timestampToDay(self, timestamp=None): + def _timestampToDay(self, datetime_timestamp=None): """ - Convert timestamp to datetime.date object with time set to midnight. - :param str timestamp: Timestamp as a string in ISO format. + Convert a timestamp to datetime.date object with time set to midnight. + + :param datetime.datetime datetime_timestamp: Command timestamp :return datetime.date day_midnight: midnight of a day. """ - if not timestamp: + if not datetime_timestamp: return OLD_DATE - timestamp_datetime = datetime.datetime.fromisoformat(timestamp) return datetime.datetime( - timestamp_datetime.year, timestamp_datetime.month, timestamp_datetime.day + datetime_timestamp.year, datetime_timestamp.month, datetime_timestamp.day ).date() + def _timestampToISO(self, datetime_timestamp=None): + """ + Convert a datetime timestamp to ISO format. + + :param str datetime_timestamp: datetime.datetime object + :return: Command timestamp in ISO format or None if datetime_timestamp is None. + """ + return ( + datetime.datetime.isoformat(datetime_timestamp) + if datetime_timestamp + else None + ) + + def _timestampToDatetime(self, iso_timestamp=None): + """ + Convert an ISO format timestamp string to a datetime object. + + :param str iso_timestamp: Command timestamp in ISO format. + :return: datetime.datetime object or None if iso_timestamp is None. + """ + return datetime.datetime.fromisoformat(iso_timestamp) if iso_timestamp else None + + def _reloadNode(self, node): + """Reload the model of a specific node.""" + self._model.SortChildren(node) + self._orig_model = copy.deepcopy(self._model) + if node == self._model.root: + self.RefreshItems() + else: + self.RefreshNode(node, recursive=True) + + def _populateDayItem(self, day_node, entry): + """ + Populate a day item with a command info node. + + :param entry dict: entry with 'command' and 'command_info' keys + :return: The newly created command node. + """ + # Determine command timestamp + command_info = entry.get("command_info", {}) + timestamp = command_info.get("timestamp") if command_info else None + + # Determine command status + status = ( + command_info.get("status") + if command_info and command_info.get("status") is not None + else Status.UNKNOWN.value + ) + + # Add command node to day node + return self._model.AppendNode( + parent=day_node, + data={ + "type": COMMAND, + "name": entry["command"].strip(), + "timestamp": ( + self._timestampToDatetime(timestamp) if timestamp else None + ), + "status": status, + }, + ) + def _initHistoryModel(self): - """Fill tree history model based on the current history log.""" - content_list = self.ReadFromHistory() - - for entry in content_list: - timestamp = None - if entry["command_info"]: - # Find day node for entries with command info - timestamp = entry["command_info"].get("timestamp") - if timestamp: - day = self._model.SearchNodes( - parent=self._model.root, - day=self._timestampToDay(timestamp), - type=TIME_PERIOD, - ) - else: - # Find day node prepared for entries without any command info - day = self._model.SearchNodes( - parent=self._model.root, - day=self._timestampToDay(), - type=TIME_PERIOD, - ) + """ + Populate the tree history model based on the current history log. + """ + for entry in self.ReadFromHistory(): - if day: - day = day[0] - # Create time period node if not found - elif not entry["command_info"]: - # Prepare it for entries without command info - day = self._model.AppendNode( - parent=self._model.root, - data={"type": TIME_PERIOD, "day": self._timestampToDay()}, - ) - else: - day = self._model.AppendNode( - parent=self._model.root, - data={ - "type": TIME_PERIOD, - "day": self._timestampToDay(entry["command_info"]["timestamp"]), - }, - ) + # Get history day node + day_node = self.GetHistoryNode(entry) - # Determine status and create command node - status = ( - entry["command_info"].get("status") - if entry.get("command_info") - and entry["command_info"].get("status") is not None - else Status.UNKNOWN.value - ) + if not day_node: + # Create the day node if it doesn't exist + day_node = self.InsertDay(entry) - # Add command to time period node - self._model.AppendNode( - parent=day, - data={ - "type": COMMAND, - "name": entry["command"].strip(), - "timestamp": timestamp or None, - "status": status, - }, - ) + # Populate the day node with the command entry + self._populateDayItem(day_node, entry) + + # Sort command nodes inside the day node from newest to oldest + self._model.SortChildren(day_node) # Refresh the tree view - self._refreshTree() + self._reloadNode(self._model.root) def _getIndexFromFile(self, command_node): - """Get index of command node in the corresponding history log file.""" + """ + Get index of command node in the corresponding history log file. + """ if not command_node.data["timestamp"]: return self._model.GetIndexOfNode(command_node)[1] else: return history.filter( json_data=self.ReadFromHistory(), command=command_node.data["name"], - timestamp=command_node.data["timestamp"], + timestamp=self._timestampToISO(command_node.data["timestamp"]), ) def ReadFromHistory(self): @@ -334,6 +382,43 @@ def GetCommandInfo(self, index): GError(str(e)) return command_info + def GetHistoryNode(self, entry, command_index=None): + """ + Get node representing time/command or None if not found. + + :param entry dict: entry with 'command' and 'command_info' keys + :param int command_index: index of the command from the particular day + :return: Node representing the time/command or None if not found. + """ + if entry["command_info"]: + # Find the day node for entries with command info + timestamp = entry["command_info"].get("timestamp") + day = self._timestampToDay(self._timestampToDatetime(timestamp)) + else: + # Find the day node for entries without command info + day = self._timestampToDay() + + # Search for day nodes matching the given day and type + day_nodes = self._model.SearchNodes( + parent=self._model.root, + day=day, + type=TIME_PERIOD, + ) + + if day_nodes: + if command_index is None: + # If no command index is specified, return the first found day node + return day_nodes[0] + else: + # Search for command nodes under the first day node + command_nodes = self._model.SearchNodes( + parent=day_nodes[0], type=COMMAND + ) + if 0 <= command_index < len(command_nodes): + return command_nodes[command_index] + + return None + def DefineItems(self, selected): """Set selected items.""" self._resetSelectVariables() @@ -369,66 +454,95 @@ def UpdateHistoryModelFromScratch(self): self._initHistoryModel() self.infoPanel.hideCommandInfo() - def InsertCommand(self, entry): - """Insert command node to the model and refresh the tree. + def InsertDay(self, entry): + """Insert a node representing the time period into the model. :param entry dict: entry with 'command' and 'command_info' keys + :return: Node representing the day. """ - # Check if today time period node exists or create it - today = self._timestampToDay(entry["command_info"]["timestamp"]) - today_nodes = self._model.SearchNodes( - parent=self._model.root, day=today, type=TIME_PERIOD - ) - if not today_nodes: - today_node = self._model.AppendNode( + if entry.get("command_info"): + # Create time period node + day = self._model.AppendNode( parent=self._model.root, data={ "type": TIME_PERIOD, - "day": today, + "day": self._timestampToDay( + self._timestampToDatetime( + entry["command_info"].get("timestamp") + ) + ), }, ) else: - today_node = today_nodes[0] + # Create time period node for entries with missing timestamp info + day = self._model.AppendNode( + parent=self._model.root, + data={ + "type": TIME_PERIOD, + "day": self._timestampToDay(), + }, + ) - # Create the command node under today time period node - command_node = self._model.AppendNode( - parent=today_node, - data={ - "type": COMMAND, - "name": entry["command"].strip(), - "timestamp": entry["command_info"]["timestamp"], - "status": entry["command_info"].get("status", Status.UNKNOWN.value), - }, - ) + return day + + def InsertCommand(self, entry): + """Insert command node to the model and reload it. + + :param entry dict: Dictionary with 'command' and 'command_info' keys + """ + # Check if time period node exists or create it + today_node = self.GetHistoryNode(entry=entry) + command_info = entry["command_info"] - # Refresh the tree - self._refreshTree() + if not today_node: + today_node = self._model.AppendNode( + parent=self._model.root, + data={ + "type": TIME_PERIOD, + "day": self._timestampToDay( + self._timestampToDatetime(command_info["timestamp"]) + ), + }, + ) + self._model.SortChildren(self._model.root) + + # Populate today's node by executed command + command_node = self._populateDayItem(today_node, entry) + self._reloadNode(today_node) # Select and expand the newly added command node self.Select(command_node) self.ExpandNode(command_node) # Show command info in info panel - self.infoPanel.showCommandInfo(entry["command_info"]) + self.infoPanel.showCommandInfo(command_info) def UpdateCommand(self, entry): - """Update last node in the model and refresh the tree. + """Update first command node in the model and refresh it. :param entry dict: entry with 'command' and 'command_info' keys """ - # Get node of last command - today = self._timestampToDay(entry["command_info"]["timestamp"]) - today_node = self._model.SearchNodes( - parent=self._model.root, day=today, type=TIME_PERIOD - )[0] - command_nodes = self._model.SearchNodes(parent=today_node, type=COMMAND) - last_node = command_nodes[-1] - - # Remove last node - self._model.RemoveNode(last_node) + # Get node of first command + first_node = self.GetHistoryNode(entry=entry, command_index=0) + + # Extract command info + command_info = entry["command_info"] + status = command_info["status"] + timestamp = command_info["timestamp"] + + # Convert timestamp to datetime object + datetime_timestamp = self._timestampToDatetime(timestamp) + + # Update command node + self._model.UpdateNode( + first_node, + status=status, + timestamp=datetime_timestamp, + ) + self._reloadNode(first_node.parent) - # Add new command node to the model - self.InsertCommand(entry) + # Show command info in info panel + self.infoPanel.showCommandInfo(command_info) def Run(self, node=None): """Parse selected history command into list and launch module dialog.""" @@ -508,7 +622,8 @@ def OnRemoveCmd(self, event): if selected_day and len(selected_day.children) == 0: self._model.RemoveNode(selected_day) - self._refreshTree() + # Reload day node + self._reloadNode(selected_day) self.showNotification.emit(message=_("<{}> removed").format(command)) def OnItemSelected(self, node): From 38eb7ba16d7d28fbf5d158843596286eb55471cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Wed, 14 Aug 2024 12:14:54 -0400 Subject: [PATCH 121/471] style: Fix Pylint bad-chained-comparison / W3601 (#4171) * style: Fix Pylint bad-chained-comparison / W3601 Pylint rule: https://pylint.readthedocs.io/en/latest/user_guide/messages/warning/bad-chained-comparison.html * Fix copy and paste typo in comment about Time zones are not supported --- python/grass/temporal/datetime_math.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/grass/temporal/datetime_math.py b/python/grass/temporal/datetime_math.py index 980c773bf87..368836c5b7a 100644 --- a/python/grass/temporal/datetime_math.py +++ b/python/grass/temporal/datetime_math.py @@ -803,10 +803,10 @@ def check_datetime_string(time_string, use_dateutil=True): return time_object # BC is not supported - if "bc" in time_string > 0: + if "bc" in time_string: return _("Dates Before Christ (BC) are not supported") - # BC is not supported + # Time zones are not supported if "+" in time_string: return _("Time zones are not supported") From e7c0f65fdec0b09aac924e757b197d040bb397b7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 14 Aug 2024 12:21:13 -0400 Subject: [PATCH 122/471] CI(deps): Update github/codeql-action action to v3.26.1 (#4177) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 4 ++-- .github/workflows/python-code-quality.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 960941d4ede..1aedeb94364 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -56,7 +56,7 @@ jobs: if: ${{ matrix.language == 'c-cpp' }} - name: Initialize CodeQL - uses: github/codeql-action/init@eb055d739abdc2e8de2e5f4ba1a8b246daa779aa # v3.26.0 + uses: github/codeql-action/init@29d86d22a34ea372b1bbf3b2dced2e25ca6b3384 # v3.26.1 with: languages: ${{ matrix.language }} config-file: ./.github/codeql/codeql-config.yml @@ -81,6 +81,6 @@ jobs: run: .github/workflows/build_ubuntu-22.04.sh "${HOME}/install" - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@eb055d739abdc2e8de2e5f4ba1a8b246daa779aa # v3.26.0 + uses: github/codeql-action/analyze@29d86d22a34ea372b1bbf3b2dced2e25ca6b3384 # v3.26.1 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index 42c7913bdb1..c6ccfd0b45c 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -135,7 +135,7 @@ jobs: path: bandit.sarif - name: Upload SARIF File into Security Tab - uses: github/codeql-action/upload-sarif@eb055d739abdc2e8de2e5f4ba1a8b246daa779aa # v3.26.0 + uses: github/codeql-action/upload-sarif@29d86d22a34ea372b1bbf3b2dced2e25ca6b3384 # v3.26.1 with: sarif_file: bandit.sarif From 90e26858a4bec25bad50d34ff579d9a201999ee9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 14 Aug 2024 16:44:24 +0000 Subject: [PATCH 123/471] CI(deps): Update super-linter/super-linter action to v6.9.0 (#4179) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/super-linter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/super-linter.yml b/.github/workflows/super-linter.yml index 3ed9efc5125..71d2501e573 100644 --- a/.github/workflows/super-linter.yml +++ b/.github/workflows/super-linter.yml @@ -31,7 +31,7 @@ jobs: # list of files that changed across commits fetch-depth: 0 - name: Lint code base - uses: super-linter/super-linter/slim@b4515bd4ad9d0aa4681960e053916ab991bdbe96 # v6.8.0 + uses: super-linter/super-linter/slim@1fa6ba58a88783e9714725cf89ac26d53e80c148 # v6.9.0 env: DEFAULT_BRANCH: main # To report GitHub Actions status checks From fa6d31d11a50fc47f56b4fb32ef2096d697fb255 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Wed, 14 Aug 2024 13:03:21 -0400 Subject: [PATCH 124/471] pytest(macOS): Run pytest tests on macOS (#4163) * CI(macOS): Add pytest dependencies to macOS workflow * CI(macOS): Run pytest tests on macOS * pytest: Use pytest.importorskip for optional imports * Mark unpickable tests with xfail when using spawn start method * grass.script: Example test using functools.partial that works with spawn start method * CI(macOS): Use if: ${{ !cancelled() }} when appropriate * Update macos_dependencies.txt * Apply suggestions from code review Co-authored-by: Nicklas Larsson * Add ipython and pyyaml to macos_dependencies.txt --------- Co-authored-by: Nicklas Larsson --- .github/workflows/macos.yml | 27 ++++++++++-- .github/workflows/macos_dependencies.txt | 7 +++- lib/gis/tests/lib_gis_env_test.py | 10 +++++ python/grass/jupyter/tests/seriesmap_test.py | 18 +++----- .../grass/jupyter/tests/timeseriesmap_test.py | 18 +++----- .../modules/tests/grass_pygrass_grid_test.py | 14 +++++++ .../tests/grass_script_core_location_test.py | 8 ++++ .../script/tests/grass_script_setup_test.py | 41 ++++++++++++++++++- .../t.rast.list/tests/t_rast_list_test.py | 8 +--- 9 files changed, 114 insertions(+), 37 deletions(-) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 6fd2442659c..cf824d5dd8d 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -55,12 +55,31 @@ jobs: - name: Add the bin directory to PATH run: echo "$HOME/install/bin" >> $GITHUB_PATH - name: Check installed version - if: always() + if: ${{ !cancelled() }} shell: bash -l {0} run: source ./.github/workflows/print_versions.sh - - name: Run tests + + - name: Run pytest with multiple workers in parallel + shell: bash -el {0} + run: | + export PYTHONPATH=$(grass --config python_path):$PYTHONPATH + export LD_LIBRARY_PATH=$(grass --config path)/lib:$LD_LIBRARY_PATH + pytest --verbose --color=yes --durations=0 --durations-min=0.5 \ + --numprocesses auto \ + -ra . \ + -m 'not needs_solo_run' + - name: Run pytest with a single worker (for tests marked with needs_solo_run) shell: bash -el {0} - run: > + run: | + export PYTHONPATH=$(grass --config python_path):$PYTHONPATH + export LD_LIBRARY_PATH=$(grass --config path)/lib:$LD_LIBRARY_PATH + pytest --verbose --color=yes --durations=0 --durations-min=0.5 \ + -ra . \ + -m 'needs_solo_run' + + - name: Run gunittest tests + shell: bash -el {0} + run: | grass --tmp-project XY --exec \ g.download.location url=${{ env.SampleData }} path=$HOME grass --tmp-project XY --exec \ @@ -71,7 +90,7 @@ jobs: SampleData: "https://grass.osgeo.org/sampledata/north_carolina/\ nc_spm_full_v2alpha2.tar.gz" - name: Make HTML test report available - if: ${{ always() }} + if: ${{ !cancelled() }} uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 with: name: testreport-macOS diff --git a/.github/workflows/macos_dependencies.txt b/.github/workflows/macos_dependencies.txt index 8f9507c15b0..bc60609f7b5 100644 --- a/.github/workflows/macos_dependencies.txt +++ b/.github/workflows/macos_dependencies.txt @@ -11,7 +11,7 @@ gettext ghostscript giflib git -libjpeg-turbo +ipython krb5 lapack lastools @@ -39,8 +39,13 @@ pkg-config ply postgresql proj +pytest +pytest-github-actions-annotate-failures +pytest-timeout +pytest-xdist python python.app +pyyaml setuptools six sqlite diff --git a/lib/gis/tests/lib_gis_env_test.py b/lib/gis/tests/lib_gis_env_test.py index 5eeffb28ba4..ad05aa4124b 100644 --- a/lib/gis/tests/lib_gis_env_test.py +++ b/lib/gis/tests/lib_gis_env_test.py @@ -2,8 +2,17 @@ import multiprocessing +import pytest + import grass.script as gs +xfail_mp_spawn = pytest.mark.xfail( + multiprocessing.get_start_method() == "spawn", + reason="Multiprocessing using 'spawn' start method requires pickable functions", + raises=AttributeError, + strict=True, +) + def run_in_subprocess(function): """Run function in a separate process @@ -19,6 +28,7 @@ def run_in_subprocess(function): return result +@xfail_mp_spawn def test_reading_respects_change_of_session(tmp_path): """Check new session file path is retrieved and the file is read""" diff --git a/python/grass/jupyter/tests/seriesmap_test.py b/python/grass/jupyter/tests/seriesmap_test.py index 14f79ccb7a8..ead9939fc0b 100644 --- a/python/grass/jupyter/tests/seriesmap_test.py +++ b/python/grass/jupyter/tests/seriesmap_test.py @@ -1,20 +1,16 @@ """Test SeriesMap functions""" from pathlib import Path -import pytest - -try: - import IPython -except ImportError: - IPython = None -try: - import ipywidgets -except ImportError: - ipywidgets = None +import pytest import grass.jupyter as gj +IPython = pytest.importorskip("IPython", reason="IPython package not available") +ipywidgets = pytest.importorskip( + "ipywidgets", reason="ipywidgets package not available" +) + @pytest.mark.needs_solo_run def test_default_init(space_time_raster_dataset): @@ -44,8 +40,6 @@ def test_render_layers(space_time_raster_dataset): @pytest.mark.needs_solo_run -@pytest.mark.skipif(IPython is None, reason="IPython package not available") -@pytest.mark.skipif(ipywidgets is None, reason="ipywidgets package not available") def test_save(space_time_raster_dataset, tmp_path): """Test returns from animate and time_slider are correct object types""" img = gj.SeriesMap() diff --git a/python/grass/jupyter/tests/timeseriesmap_test.py b/python/grass/jupyter/tests/timeseriesmap_test.py index d904830b292..d65f7fcb700 100644 --- a/python/grass/jupyter/tests/timeseriesmap_test.py +++ b/python/grass/jupyter/tests/timeseriesmap_test.py @@ -1,21 +1,17 @@ """Test TimeSeriesMap functions""" from pathlib import Path -import pytest - -try: - import IPython -except ImportError: - IPython = None -try: - import ipywidgets -except ImportError: - ipywidgets = None +import pytest import grass.jupyter as gj from grass.jupyter.timeseriesmap import collect_layers, fill_none_values +IPython = pytest.importorskip("IPython", reason="IPython package not available") +ipywidgets = pytest.importorskip( + "ipywidgets", reason="ipywidgets package not available" +) + def test_fill_none_values(): """Test that fill_none_values replaces None with previous value in list""" @@ -71,8 +67,6 @@ def test_render_layers(space_time_raster_dataset, fill_gaps): @pytest.mark.needs_solo_run -@pytest.mark.skipif(IPython is None, reason="IPython package not available") -@pytest.mark.skipif(ipywidgets is None, reason="ipywidgets package not available") def test_save(space_time_raster_dataset, tmp_path): """Test returns from animate and time_slider are correct object types""" img = gj.TimeSeriesMap() diff --git a/python/grass/pygrass/modules/tests/grass_pygrass_grid_test.py b/python/grass/pygrass/modules/tests/grass_pygrass_grid_test.py index fcdd5ffc67c..6a9915b6a14 100644 --- a/python/grass/pygrass/modules/tests/grass_pygrass_grid_test.py +++ b/python/grass/pygrass/modules/tests/grass_pygrass_grid_test.py @@ -7,6 +7,13 @@ import grass.script as gs from grass.pygrass.modules.grid import GridModule +xfail_mp_spawn = pytest.mark.xfail( + multiprocessing.get_start_method() == "spawn", + reason="Multiprocessing using 'spawn' start method requires pickable functions", + raises=AttributeError, + strict=True, +) + def max_processes(): """Get max useful number of parallel processes to run""" @@ -23,6 +30,7 @@ def run_in_subprocess(function): process.join() +@xfail_mp_spawn @pytest.mark.needs_solo_run @pytest.mark.parametrize("processes", list(range(1, max_processes() + 1)) + [None]) def test_processes(tmp_path, processes): @@ -57,6 +65,7 @@ def run_grid_module(): # @pytest.mark.parametrize("split", [False]) # True does not work. +@xfail_mp_spawn @pytest.mark.parametrize("width", [5, 10, 50]) # None does not work. @pytest.mark.parametrize("height", [5, 10, 50]) def test_tiling_schemes(tmp_path, width, height): @@ -88,6 +97,7 @@ def run_grid_module(): assert info["min"] > 0 +@xfail_mp_spawn @pytest.mark.parametrize("overlap", [0, 1, 2, 5]) def test_overlaps(tmp_path, overlap): """Check that overlap accepts different values""" @@ -117,6 +127,7 @@ def run_grid_module(): assert info["min"] > 0 +@xfail_mp_spawn @pytest.mark.parametrize("clean", [True, False]) @pytest.mark.parametrize("surface", ["surface", "non_exist_surface"]) def test_cleans(tmp_path, clean, surface): @@ -159,6 +170,7 @@ def run_grid_module(): assert prefixed, "Not even one prefixed mapset" +@xfail_mp_spawn @pytest.mark.parametrize("patch_backend", [None, "r.patch", "RasterRow"]) def test_patching_backend(tmp_path, patch_backend): """Check patching backend works""" @@ -196,6 +208,7 @@ def run_grid_module(): assert abs(mean - mean_ref) < 0.0001 +@xfail_mp_spawn @pytest.mark.parametrize( "width, height, processes", [ @@ -233,6 +246,7 @@ def run_grid_module(): assert info["min"] > 0 +@xfail_mp_spawn @pytest.mark.needs_solo_run @pytest.mark.parametrize( "processes, backend", diff --git a/python/grass/script/tests/grass_script_core_location_test.py b/python/grass/script/tests/grass_script_core_location_test.py index b389d41ba8d..e04f65f211c 100644 --- a/python/grass/script/tests/grass_script_core_location_test.py +++ b/python/grass/script/tests/grass_script_core_location_test.py @@ -7,6 +7,13 @@ import grass.script as gs +xfail_mp_spawn = pytest.mark.xfail( + multiprocessing.get_start_method() == "spawn", + reason="Multiprocessing using 'spawn' start method requires pickable functions", + raises=AttributeError, + strict=True, +) + # This is useful when we want to ensure that function like init does # not change the global environment. @@ -50,6 +57,7 @@ def test_with_same_path(tmp_path): assert srid == "EPSG:3358" +@xfail_mp_spawn def test_with_init_in_subprocess(tmp_path): """Check creation when running in a subprocess""" diff --git a/python/grass/script/tests/grass_script_setup_test.py b/python/grass/script/tests/grass_script_setup_test.py index cd0939046ad..62a0ca37b74 100644 --- a/python/grass/script/tests/grass_script_setup_test.py +++ b/python/grass/script/tests/grass_script_setup_test.py @@ -2,11 +2,19 @@ import multiprocessing import os +from functools import partial import pytest import grass.script as gs +xfail_mp_spawn = pytest.mark.xfail( + multiprocessing.get_start_method() == "spawn", + reason="Multiprocessing using 'spawn' start method requires pickable functions", + raises=AttributeError, + strict=True, +) + # This is useful when we want to ensure that function like init does # not change the global environment. @@ -62,6 +70,32 @@ def test_init_finish_global_functions_with_env(tmp_path): assert not os.path.exists(session_file) +def init_finish_global_functions_capture_strerr0_partial(tmp_path, queue): + gs.set_capture_stderr(True) + location = "test" + gs.core._create_location_xy(tmp_path, location) # pylint: disable=protected-access + gs.setup.init(tmp_path / location) + gs.run_command("g.region", flags="p") + runtime_present = bool(os.environ.get("GISBASE")) + queue.put((os.environ["GISRC"], runtime_present)) + gs.setup.finish() + + +def test_init_finish_global_functions_capture_strerr0_partial(tmp_path): + """Check that init and finish global functions work with global env using a partial + function + """ + + init_finish = partial( + init_finish_global_functions_capture_strerr0_partial, tmp_path + ) + session_file, runtime_present = run_in_subprocess(init_finish) + assert session_file, "Expected file name from the subprocess" + assert runtime_present, "Runtime (GISBASE) should be present" + assert not os.path.exists(session_file), "Session file not deleted" + + +@xfail_mp_spawn def test_init_finish_global_functions_capture_strerr0(tmp_path): """Check that init and finish global functions work with global env""" @@ -83,6 +117,7 @@ def init_finish(queue): assert not os.path.exists(session_file), "Session file not deleted" +@xfail_mp_spawn def test_init_finish_global_functions_capture_strerrX(tmp_path): """Check that init and finish global functions work with global env""" @@ -111,6 +146,7 @@ def init_finish(queue): assert runtime_present_after, "Runtime should continue to be present" +@xfail_mp_spawn def test_init_finish_global_functions_isolated(tmp_path): """Check that init and finish global functions work with global env""" @@ -165,15 +201,16 @@ def init_finish(queue): assert not os.path.exists(session_file), "Session file not deleted" +@xfail_mp_spawn @pytest.mark.usefixtures("mock_no_session") def test_init_as_context_manager_env_attribute(tmp_path): """Check that session has global environment as attribute""" def workload(queue): location = "test" - gs.core._create_location_xy( + gs.core._create_location_xy( # pylint: disable=protected-access tmp_path, location - ) # pylint: disable=protected-access + ) with gs.setup.init(tmp_path / location) as session: gs.run_command("g.region", flags="p", env=session.env) session_file = os.environ["GISRC"] diff --git a/temporal/t.rast.list/tests/t_rast_list_test.py b/temporal/t.rast.list/tests/t_rast_list_test.py index ccb8e8cdc29..fbc092175db 100644 --- a/temporal/t.rast.list/tests/t_rast_list_test.py +++ b/temporal/t.rast.list/tests/t_rast_list_test.py @@ -7,13 +7,10 @@ import pytest -try: - import yaml -except ImportError: - yaml = None - import grass.script as gs +yaml = pytest.importorskip("yaml", reason="PyYAML package not available") + @pytest.mark.needs_solo_run def test_defaults(space_time_raster_dataset): @@ -62,7 +59,6 @@ def test_json(space_time_raster_dataset): @pytest.mark.needs_solo_run -@pytest.mark.skipif(yaml is None, reason="PyYAML package not available") def test_yaml(space_time_raster_dataset): """Check JSON can be parsed and contains the right values""" result = yaml.safe_load( From cc0e2ce0747886e276460446b203a695901bd61c Mon Sep 17 00:00:00 2001 From: Markus Neteler Date: Wed, 14 Aug 2024 20:06:41 +0200 Subject: [PATCH 125/471] style: use overwrite and not 'o' flag in Python scripts (#4176) (replaces #4175) Fixes message ```bash WARNING: Please update the usage of : flag has been renamed to <--overwrite> ``` --- scripts/v.db.reconnect.all/v.db.reconnect.all.py | 2 +- scripts/v.unpack/v.unpack.py | 2 +- vector/v.fill.holes/tests/conftest.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/v.db.reconnect.all/v.db.reconnect.all.py b/scripts/v.db.reconnect.all/v.db.reconnect.all.py index 03a98f7bf0e..b42b8006673 100755 --- a/scripts/v.db.reconnect.all/v.db.reconnect.all.py +++ b/scripts/v.db.reconnect.all/v.db.reconnect.all.py @@ -298,7 +298,7 @@ def main(): try: gs.run_command( "v.db.connect", - flags="o", + overwrite=True, quiet=True, map=vect, layer=layer, diff --git a/scripts/v.unpack/v.unpack.py b/scripts/v.unpack/v.unpack.py index bd350676444..af430f98829 100644 --- a/scripts/v.unpack/v.unpack.py +++ b/scripts/v.unpack/v.unpack.py @@ -280,7 +280,7 @@ def main(): try: grass.run_command( "v.db.connect", - flags="o", + overwrite=True, quiet=True, driver=dbconn["driver"], database=todb, diff --git a/vector/v.fill.holes/tests/conftest.py b/vector/v.fill.holes/tests/conftest.py index f2413728e71..885699c0a4b 100644 --- a/vector/v.fill.holes/tests/conftest.py +++ b/vector/v.fill.holes/tests/conftest.py @@ -216,7 +216,7 @@ def import_data(path, areas_name, areas_with_space_in_between, env): "v.db.connect", map=areas_with_space_in_between, table=areas_with_space_in_between, - flags="o", + overwrite=True, env=env, ) From 5182753d14b9ab6415443581ca1f9baf9ae95f1b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 14 Aug 2024 18:33:25 +0000 Subject: [PATCH 126/471] CI(deps): Update github/codeql-action action to v3.26.2 (#4180) --- .github/workflows/codeql-analysis.yml | 4 ++-- .github/workflows/python-code-quality.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 1aedeb94364..e1da939842e 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -56,7 +56,7 @@ jobs: if: ${{ matrix.language == 'c-cpp' }} - name: Initialize CodeQL - uses: github/codeql-action/init@29d86d22a34ea372b1bbf3b2dced2e25ca6b3384 # v3.26.1 + uses: github/codeql-action/init@429e1977040da7a23b6822b13c129cd1ba93dbb2 # v3.26.2 with: languages: ${{ matrix.language }} config-file: ./.github/codeql/codeql-config.yml @@ -81,6 +81,6 @@ jobs: run: .github/workflows/build_ubuntu-22.04.sh "${HOME}/install" - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@29d86d22a34ea372b1bbf3b2dced2e25ca6b3384 # v3.26.1 + uses: github/codeql-action/analyze@429e1977040da7a23b6822b13c129cd1ba93dbb2 # v3.26.2 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index c6ccfd0b45c..f4413f5a885 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -135,7 +135,7 @@ jobs: path: bandit.sarif - name: Upload SARIF File into Security Tab - uses: github/codeql-action/upload-sarif@29d86d22a34ea372b1bbf3b2dced2e25ca6b3384 # v3.26.1 + uses: github/codeql-action/upload-sarif@429e1977040da7a23b6822b13c129cd1ba93dbb2 # v3.26.2 with: sarif_file: bandit.sarif From 5c1992c138136190163a8a83bd6931c0b766dfc2 Mon Sep 17 00:00:00 2001 From: Anna Petrasova Date: Wed, 14 Aug 2024 14:49:39 -0400 Subject: [PATCH 127/471] wxGUI: fix mapCreated signal handling (#4174) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #4166, something must have changed in the pydispatch library, it looks like before it ignored if the handler didn't have all the parameters. Co-authored-by: Edouard Choinière <27212526+echoix@users.noreply.github.com> --- gui/wxpython/gui_core/forms.py | 5 ++++- gui/wxpython/rdigit/g.gui.rdigit.py | 4 +++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/gui/wxpython/gui_core/forms.py b/gui/wxpython/gui_core/forms.py index 848e8e94c4e..d86c7ded554 100644 --- a/gui/wxpython/gui_core/forms.py +++ b/gui/wxpython/gui_core/forms.py @@ -46,6 +46,8 @@ @author Stepan Turek (CoordinatesSelect) """ +from __future__ import annotations + import sys import textwrap import os @@ -806,11 +808,12 @@ def OnDone(self, event): # was closed also when aborted but better is leave it open wx.CallLater(2000, self.Close) - def OnMapCreated(self, name, ltype): + def OnMapCreated(self, name, ltype, add: bool | None = None): """Map created or changed :param name: map name :param ltype: layer type (prompt value) + :param add: whether to display layer or not """ if hasattr(self, "addbox") and self.addbox.IsChecked(): add = True diff --git a/gui/wxpython/rdigit/g.gui.rdigit.py b/gui/wxpython/rdigit/g.gui.rdigit.py index 81f15d15531..d740b1c3e78 100755 --- a/gui/wxpython/rdigit/g.gui.rdigit.py +++ b/gui/wxpython/rdigit/g.gui.rdigit.py @@ -53,6 +53,7 @@ # % required: create, edit # % requires: base, create # %end +from __future__ import annotations import os @@ -163,11 +164,12 @@ def _addLayer(self, name, ltype="raster"): render=True, ) - def OnMapCreated(self, name, ltype): + def OnMapCreated(self, name, ltype, add: bool | None = None): """Add new created raster layer into map :param str name: map name :param str ltype: layer type + :param bool add: unused """ self._mapObj.Clean() self._addLayer(name=name, ltype=ltype) From ba6cb70a519e8ad433c1c755e7d3c558fbaf9c91 Mon Sep 17 00:00:00 2001 From: Nicklas Larsson Date: Wed, 14 Aug 2024 21:53:22 +0200 Subject: [PATCH 128/471] CI: also remove /opt/homebrew from macOS runner (#4178) --- .github/workflows/macos.yml | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index cf824d5dd8d..bf3859334ec 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -28,12 +28,19 @@ jobs: - name: Uninstalling Homebrew run: | echo "Moving directories..." - sudo mkdir /opt/off - /usr/bin/sudo /usr/bin/find /usr/local /opt/homebrew -mindepth 1 -maxdepth 1 \ - -type d -print -exec /bin/mv {} /opt/off/ \; + sudo mkdir /opt/local-off /opt/homebrew-off + test ! -d /usr/local || /usr/bin/sudo /usr/bin/find /usr/local \ + -mindepth 1 -maxdepth 1 -type d -print -exec /bin/mv {} \ + /opt/local-off/ \; + test ! -d /opt/homebrew || /usr/bin/sudo /usr/bin/find /opt/homebrew \ + -mindepth 1 -maxdepth 1 -type d -print -exec /bin/mv {} \ + /opt/homebrew-off/ \; echo "Removing files..." - /usr/bin/sudo /usr/bin/find /usr/local /opt/homebrew -mindepth 1 -maxdepth 1 \ - -type f -print -delete + test ! -d /usr/local || /usr/bin/sudo /usr/bin/find /usr/local \ + -mindepth 1 -maxdepth 1 -type f -print -delete + test ! -d /opt/homebrew || /usr/bin/sudo /usr/bin/find /opt/homebrew \ + -mindepth 1 -maxdepth 1 -type f -print -delete + # Rehash to forget about the deleted files hash -r - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Setup Mamba From 46903c0e45c802f809645b745c59265ebc619c58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Wed, 14 Aug 2024 17:19:01 -0400 Subject: [PATCH 129/471] style: Fix 12 ruff rules with small count of violations (#4139) * style: Fix import-self (PLW0406) Ruff rule: https://docs.astral.sh/ruff/rules/import-self/ 1 instance solved * Solve Pylint W0105: pointless-string-statement for python/grass/pygrass/utils.py * Fix bad name of ruff rule in pyproject.toml * Fix missing space in pyproject.toml ruff rules ignore list * style: Fix native-literals (UP018) * style: Fix unnecessary-dict-index-lookup (PLR1733) * style: Fix no-classmethod-decorator (PLR0202) Ruff rule: https://docs.astral.sh/ruff/rules/no-classmethod-decorator/ 1 instance fixed. * style: Ignore remaining comparison-with-itself (PLR0124) in test_eq Ruff rule: https://docs.astral.sh/ruff/rules/comparison-with-itself/ 1 instance solved * style: Fix ambiguous-unicode-character-docstring (RUF002) Ruff rule: https://docs.astral.sh/ruff/rules/ambiguous-unicode-character-docstring/ 2 instances solved * style: Fix unnecessary-generator-list (C400) Ruff rule: https://docs.astral.sh/ruff/rules/unnecessary-generator-list/ 1 instance solved * style: Fix unnecessary-generator-set (C401) Ruff rule: https://docs.astral.sh/ruff/rules/unnecessary-generator-set/ 2 instances solved * style: Fix unnecessary-list-comprehension-set (C403) Ruff rule: https://docs.astral.sh/ruff/rules/unnecessary-list-comprehension-set/ 3 instances solved * style: Ignore 1 instance of sys-version-info-minor-cmp-int (YTT204) Ruff rule: https://docs.astral.sh/ruff/rules/sys-version-info-minor-cmp-int/ Correctly checks for only Python 3. Using a tuple would not be less messy, as a second comparison for >= Python 3.0 would be needed. * style: Fix unnecessary-literal-set (C405) Ruff rule: https://docs.astral.sh/ruff/rules/unnecessary-literal-set/ 2 instances fixed * Fix copy paste typo in ruff rule comment * style: Fix implicit-optional (RUF013) Ruff rule: https://docs.astral.sh/ruff/rules/implicit-optional/ 2 instances fixed * Update scripts/g.extension/g.extension.py --------- Co-authored-by: Stefan Blumentrath --- gui/wxpython/animation/dialogs.py | 2 +- gui/wxpython/rdigit/toolbars.py | 2 +- gui/wxpython/tplot/frame.py | 4 ++-- gui/wxpython/web_services/dialogs.py | 2 +- man/parser_standard_options.py | 2 +- pyproject.toml | 16 ++-------------- python/grass/gunittest/case.py | 4 ++-- python/grass/jupyter/map3d.py | 5 +++-- python/grass/pydispatch/saferef.py | 3 +-- python/grass/pygrass/utils.py | 9 ++++----- .../pygrass/vector/testsuite/test_geometry.py | 2 +- python/grass/script/core.py | 2 +- python/grass/temporal/datetime_math.py | 2 +- scripts/g.extension/g.extension.py | 4 ++-- .../testsuite/test_addons_download.py | 2 +- 15 files changed, 24 insertions(+), 37 deletions(-) diff --git a/gui/wxpython/animation/dialogs.py b/gui/wxpython/animation/dialogs.py index 91b67429858..154bdc9fce6 100644 --- a/gui/wxpython/animation/dialogs.py +++ b/gui/wxpython/animation/dialogs.py @@ -900,7 +900,7 @@ def GetResult(self): return self.result def OnOk(self, event): - indices = set([anim.windowIndex for anim in self.animationData]) + indices = {anim.windowIndex for anim in self.animationData} if len(indices) != len(self.animationData): GError( parent=self, diff --git a/gui/wxpython/rdigit/toolbars.py b/gui/wxpython/rdigit/toolbars.py index 277e4c11ff6..d3d9aed5eb9 100644 --- a/gui/wxpython/rdigit/toolbars.py +++ b/gui/wxpython/rdigit/toolbars.py @@ -56,7 +56,7 @@ def __init__(self, parent, giface, controller, toolSwitcher): self._color.SetToolTip(_("Set drawing color (not raster cell color)")) self.InsertControl(4, self._color) - self._cellValues = set(["1"]) + self._cellValues = {"1"} # validator does not work with combobox, SetBackgroundColor is not # working self._valueCombo = wx.ComboBox( diff --git a/gui/wxpython/tplot/frame.py b/gui/wxpython/tplot/frame.py index 85028081158..e15e6584808 100755 --- a/gui/wxpython/tplot/frame.py +++ b/gui/wxpython/tplot/frame.py @@ -1410,8 +1410,8 @@ def __init__( artists = [artists] self.artists = artists self.convert = convert - self.axes = tuple(set(art.axes for art in self.artists)) - self.figures = tuple(set(ax.figure for ax in self.axes)) + self.axes = tuple({art.axes for art in self.artists}) + self.figures = tuple({ax.figure for ax in self.axes}) self.annotations = {} for ax in self.axes: diff --git a/gui/wxpython/web_services/dialogs.py b/gui/wxpython/web_services/dialogs.py index 904b2bb81c5..d67a2b34f7d 100644 --- a/gui/wxpython/web_services/dialogs.py +++ b/gui/wxpython/web_services/dialogs.py @@ -681,7 +681,7 @@ def __del__(self): def _setRevertCapFiles(self, ws_cap_files): for ws, f in ws_cap_files.items(): - if os.path.isfile(ws_cap_files[ws]): + if os.path.isfile(f): shutil.copyfile(f, self.revert_ws_cap_files[ws]) else: # delete file content diff --git a/man/parser_standard_options.py b/man/parser_standard_options.py index b6ce30b1bb2..15d1e0fc9ae 100644 --- a/man/parser_standard_options.py +++ b/man/parser_standard_options.py @@ -115,7 +115,7 @@ def clean_value(val): class OptTable: def __init__(self, list_of_dict): self.options = list_of_dict - self.columns = sorted(set([key for _, d in self.options for key in d.keys()])) + self.columns = sorted({key for _, d in self.options for key in d.keys()}) def csv(self, delimiter=";", endline="\n"): """Return a CSV string with the options""" diff --git a/pyproject.toml b/pyproject.toml index b9f4b73bccd..c28c944f345 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -100,11 +100,7 @@ ignore = [ "B904", # raise-without-from-inside-except "B909", # loop-iterator-mutation "BLE001", # blind-except - "C400", # unnecessary-generator-list - "C401", # unnecessary-generator-set - "C403", # unnecessary-list-comprehension-set "C404", # unnecessary-list-comprehension-dict - "C405", # unnecessary-literal-set "C414", # unnecessary-double-cast-or-process "C416", # unnecessary-comprehension "COM812", # missing-trailing-comma @@ -166,8 +162,6 @@ ignore = [ "PLC2701", # import-private-name "PLC2801", # unnecessary-dunder-call "PLE0704", # misplaced-bare-raise - "PLR0124", # comparison-with-itself - "PLR0202", # no-classmethod-decorator "PLR0904", # too-many-public-methods "PLR0911", # too-many-return-statements "PLR0912", # too-many-branches @@ -178,11 +172,9 @@ ignore = [ "PLR0917", # too-many-positional "PLR1702", # too-many-nested-blocks "PLR1704", # redefined-argument-from-local - "PLR1733", # unnecessary-dict-index-lookup "PLR2004", # magic-value-comparison "PLR6201", # literal-membership "PLR6301", # no-self-use - "PLW0406", # import-self "PLW0602", # global-variable-not-assigned "PLW0603", # global-statement "PLW0604", # global-at-module-level @@ -191,7 +183,7 @@ ignore = [ "PLW1514", # unspecified-encoding "PLW1641", # eq-without-hash "PLW2901", # redefined-loop-name - "PLW3201", # nested-min-max + "PLW3201", # bad-dunder-method-name "PT001", # pytest-fixture-incorrect-parentheses-style "PT004", # pytest-missing-fixture-name-underscore "PT006", # pytest-parametrize-names-wrong-type @@ -230,16 +222,14 @@ ignore = [ "RET507", # superfluous-else-continue "RET508", # superfluous-else-break "RSE102", # unnecessary-paren-on-raise-exception - "RUF002", # ambiguous-unicode-character-docstring "RUF003", # ambiguous-unicode-character-comment "RUF005", # collection-literal-concatenation "RUF012", # mutable-class-default - "RUF013", # unnecessary-iterable-allocation-for-first-element "RUF015", # unnecessary-iterable-allocation-for-first-element "RUF019", # unnecessary-key-check "RUF027", # missing-f-string-syntax "RUF100", # unused-noqa - "S101", #assert + "S101", # assert "S108", # hardcoded-temp-file "S110", # try-except-pass "S112", # try-except-continue @@ -282,13 +272,11 @@ ignore = [ "TRY300", # try-consider-else "TRY301", # raise-within-try "UP015", # redundant-open-modes - "UP018", # native-literals "UP030", # format-literals "UP031", # printf-string-formatting "UP032", # f-string "UP036", # outdated-version-block "W605", # invalid-escape-sequence - "YTT204", # sys-version-info-minor-cmp-int ] diff --git a/python/grass/gunittest/case.py b/python/grass/gunittest/case.py index 30eed3e9865..4d1b37bdfed 100644 --- a/python/grass/gunittest/case.py +++ b/python/grass/gunittest/case.py @@ -896,7 +896,7 @@ def assertRastersNoDifference( If statistics is not given ``dict(min=-precision, max=precision)`` is used. - Be ware – comparison is performed on overall statistics and thus + Beware - comparison is performed on overall statistics and thus differences in individual cell values not changing overall statistics might go unnoticed. Use `assertRastersEqual()` for cell to cell equivalence testing. @@ -943,7 +943,7 @@ def assertRastersDifference( This method should not be used to test r.mapcalc or r.univar. - Be ware – comparison is performed on overall statistics and thus + Beware - comparison is performed on overall statistics and thus differences in individual cell values not changing overall statistics might go unnoticed. Use `assertRastersEqual()` for cell to cell equivalence testing. diff --git a/python/grass/jupyter/map3d.py b/python/grass/jupyter/map3d.py index acad4040d31..dd373f315a7 100644 --- a/python/grass/jupyter/map3d.py +++ b/python/grass/jupyter/map3d.py @@ -12,6 +12,7 @@ # for details. """Render 3D visualizations""" +from __future__ import annotations import os import tempfile @@ -48,7 +49,7 @@ def __init__( self, width: int = 600, height: int = 400, - filename: str = None, + filename: str | None = None, mode: str = "fine", resolution_fine: int = 1, screen_backend: str = "auto", @@ -56,7 +57,7 @@ def __init__( text_size: float = 12, renderer2d: str = "cairo", use_region: bool = False, - saved_region: str = None, + saved_region: str | None = None, ): """Checks screen_backend and creates a temporary directory for rendering. diff --git a/python/grass/pydispatch/saferef.py b/python/grass/pydispatch/saferef.py index bf67240d6dd..a802a1982c1 100644 --- a/python/grass/pydispatch/saferef.py +++ b/python/grass/pydispatch/saferef.py @@ -141,6 +141,7 @@ def remove(weak, self=self): self.selfName = getattr(target, im_self).__class__.__name__ self.funcName = str(getattr(target, im_func).__name__) + @classmethod def calculateKey(cls, target): """Calculate the reference key for this reference @@ -149,8 +150,6 @@ def calculateKey(cls, target): """ return (id(getattr(target, im_self)), id(getattr(target, im_func))) - calculateKey = classmethod(calculateKey) - def __str__(self): """Give a friendly representation of the object""" return """%s( %s.%s )""" % ( diff --git a/python/grass/pygrass/utils.py b/python/grass/pygrass/utils.py index d9d17ade0d9..a04e3936a64 100644 --- a/python/grass/pygrass/utils.py +++ b/python/grass/pygrass/utils.py @@ -593,19 +593,18 @@ def create_test_stream_network_map(map_name="streams"): if __name__ == "__main__": import doctest - from grass.pygrass import utils from grass.script.core import run_command - utils.create_test_vector_map(test_vector_name) + create_test_vector_map(test_vector_name) run_command("g.region", n=50, s=0, e=60, w=0, res=1) run_command("r.mapcalc", expression="%s = 1" % (test_raster_name), overwrite=True) doctest.testmod() - """Remove the generated vector map, if exist""" - mset = utils.get_mapset_vector(test_vector_name, mapset="") + # Remove the generated vector map, if exist + mset = get_mapset_vector(test_vector_name, mapset="") if mset: run_command("g.remove", flags="f", type="vector", name=test_vector_name) - mset = utils.get_mapset_raster(test_raster_name, mapset="") + mset = get_mapset_raster(test_raster_name, mapset="") if mset: run_command("g.remove", flags="f", type="raster", name=test_raster_name) diff --git a/python/grass/pygrass/vector/testsuite/test_geometry.py b/python/grass/pygrass/vector/testsuite/test_geometry.py index 8680251d8c6..b2a7a1e37df 100644 --- a/python/grass/pygrass/vector/testsuite/test_geometry.py +++ b/python/grass/pygrass/vector/testsuite/test_geometry.py @@ -86,7 +86,7 @@ def test_eq(self): point1 = Point(1, 0) self.assertFalse(point0 == point1) self.assertFalse(point0 == (1, 0)) - self.assertTrue(point0 == point0) + self.assertTrue(point0 == point0) # noqa: PLR0124 self.assertTrue(point0 == (0, 0)) def test_repr(self): diff --git a/python/grass/script/core.py b/python/grass/script/core.py index a5e14aa92db..a86738926f0 100644 --- a/python/grass/script/core.py +++ b/python/grass/script/core.py @@ -40,7 +40,7 @@ # subprocess wrapper that uses shell on Windows class Popen(subprocess.Popen): - _builtin_exts = set([".com", ".exe", ".bat", ".cmd"]) + _builtin_exts = {".com", ".exe", ".bat", ".cmd"} @staticmethod def _escape_for_shell(arg): diff --git a/python/grass/temporal/datetime_math.py b/python/grass/temporal/datetime_math.py index 368836c5b7a..e5be1191551 100644 --- a/python/grass/temporal/datetime_math.py +++ b/python/grass/temporal/datetime_math.py @@ -141,7 +141,7 @@ def decrement_datetime_by_string(mydate, increment, mult=1): :param mult: A multiplier, default is 1 :return: The new datetime object or none in case of an error """ - return modify_datetime_by_string(mydate, increment, mult, sign=int(-1)) + return modify_datetime_by_string(mydate, increment, mult, sign=-1) ############################################################################### diff --git a/scripts/g.extension/g.extension.py b/scripts/g.extension/g.extension.py index 5077f85f4f4..868e2a0b5da 100644 --- a/scripts/g.extension/g.extension.py +++ b/scripts/g.extension/g.extension.py @@ -155,7 +155,7 @@ import json import xml.etree.ElementTree as etree -if sys.version_info.major == 3 and sys.version_info.minor < 8: +if sys.version_info < (3, 8): from distutils.dir_util import copy_tree else: from functools import partial @@ -1461,7 +1461,7 @@ def get_multi_addon_addons_which_install_only_html_man_page(): rf".*{options['extension']}*.", get_addons_paths(gg_addons_base_dir=options["prefix"]), ) - addon_dir_paths = set([os.path.dirname(i) for i in addon_paths]) + addon_dir_paths = {os.path.dirname(i) for i in addon_paths} for addon_dir in addon_dir_paths: addon_src_files = list( re.finditer(rf"{addon_dir}/(.*py)|(.*c)\n", "\n".join(addon_paths)), diff --git a/scripts/g.extension/testsuite/test_addons_download.py b/scripts/g.extension/testsuite/test_addons_download.py index c4618306d12..07be93cf878 100644 --- a/scripts/g.extension/testsuite/test_addons_download.py +++ b/scripts/g.extension/testsuite/test_addons_download.py @@ -49,7 +49,7 @@ class TestModuleDownloadFromDifferentSources(TestCase): def setUp(self): """Make sure we are not dealing with some old files""" if self.install_prefix.exists(): - files = list(path.name for path in self.install_prefix.iterdir()) + files = [path.name for path in self.install_prefix.iterdir()] if files: raise RuntimeError( f"Install prefix path '{self.install_prefix}' \ From 0d5d274b6d4ca9414f66783a967f9b1331303243 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 15 Aug 2024 02:20:04 +0000 Subject: [PATCH 130/471] CI(deps): Update pre-commit hook pre-commit/mirrors-clang-format to v18 (#4183) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * CI(deps): Update pre-commit hook pre-commit/mirrors-clang-format to v18 * Update clang-format-check.yml to use version 18.1.8 * Format files with clang-format 18.1.8 * Fix missing whitespace around operator (flake8) --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Edouard Choinière <27212526+echoix@users.noreply.github.com> --- .github/workflows/clang-format-check.yml | 4 +- .pre-commit-config.yaml | 2 +- display/d.colortable/main.c | 2 +- display/d.histogram/bar.c | 2 +- imagery/i.ortho.photo/i.ortho.rectify/defs.h | 2 +- imagery/i.rectify/defs.h | 2 +- imagery/i.segment/mean_shift.c | 4 +- imagery/i.segment/region_growing.c | 8 ++-- include/grass/iostream/embuffer.h | 2 +- include/grass/iostream/empq_adaptive.h | 2 +- include/grass/iostream/minmaxheap.h | 10 ++-- include/grass/iostream/replacementHeap.h | 2 +- include/grass/iostream/replacementHeapBlock.h | 2 +- include/grass/iostream/rtimer.h | 16 +++---- lib/datetime/incr1.c | 2 +- lib/display/symbol.c | 2 +- lib/external/parson/parson.c | 2 +- lib/gis/lz4.h | 10 ++-- lib/gis/plot.c | 6 +-- lib/gmath/findzc.c | 2 +- lib/gmath/la.c | 2 +- lib/imagery/georef.c | 2 +- lib/imagery/georef_tps.c | 2 +- lib/lidar/raster.c | 2 +- lib/nviz/render.c | 48 +++++++++---------- lib/ogsf/gsd_surf.c | 4 +- lib/ogsf/gsd_wire.c | 2 +- lib/ogsf/gsget.h | 6 +-- lib/raster/close.c | 2 +- lib/raster/quant.c | 2 +- lib/raster3d/raster3d_intern.h | 15 ++---- lib/vector/Vlib/break_lines.c | 2 +- lib/vector/Vlib/dangles.c | 4 +- lib/vector/Vlib/intersect2.c | 2 +- lib/vector/Vlib/snap.c | 4 +- lib/vector/rtree/split.c | 4 +- lib/vector/vedit/break.c | 2 +- lib/vector/vedit/vertex.c | 2 +- ps/ps.map/ps_info.h | 4 +- raster/r.clump/minsize.c | 2 +- raster/r.contour/cont.c | 20 ++++---- raster/r.cost/heap.c | 2 +- .../r.horizon/benchmark/benchmark_rhorizon.py | 2 +- raster/r.horizon/main.c | 4 +- raster/r.in.gdal/main.c | 2 +- raster/r.proj/r.proj.h | 2 +- raster/r.quant/read_rules.c | 2 +- raster/r.random.surface/init.c | 2 +- raster/r.recode/read_rules.c | 2 +- raster/r.resamp.bspline/main.c | 2 +- raster/r.series/main.c | 2 +- raster/r.sim/simlib/hydro.c | 2 +- raster/r.slope.aspect/main.c | 2 +- raster/r.spreadpath/main.c | 2 +- raster/r.stream.extract/do_astar.c | 2 +- raster/r.sun/main.c | 2 +- raster/r.terraflow/ccforest.h | 4 +- raster/r.terraflow/flow.cpp | 2 +- raster/r.terraflow/grass2str.h | 8 ++-- raster/r.terraflow/nodata.h | 4 +- raster/r.terraflow/plateau.cpp | 2 +- raster/r.terraflow/plateau.h | 6 +-- raster/r.terraflow/sweep.cpp | 2 +- raster/r.terraflow/types.h | 6 +-- raster/r.terraflow/water.cpp | 4 +- raster/r.terraflow/water.h | 24 +++++----- raster/r.terraflow/weightWindow.cpp | 4 +- raster/r.terraflow/weightWindow.h | 2 +- raster/r.thin/thin_lines.c | 4 +- raster/r.walk/heap.c | 2 +- raster/r.watershed/ram/do_astar.c | 2 +- raster/r.watershed/ram/do_astar.h | 2 +- raster/r.watershed/seg/do_astar.h | 2 +- raster/r.watershed/seg/init_vars.c | 2 +- raster3d/r3.showdspf/main_ogl.c | 2 +- vector/v.cluster/main.c | 2 +- vector/v.db.connect/main.c | 6 +-- vector/v.distance/main.c | 4 +- vector/v.extrude/main.c | 2 +- vector/v.in.ascii/points.c | 2 +- vector/v.lidar.correction/correction.c | 2 +- vector/v.lidar.correction/main.c | 2 +- vector/v.lidar.edgedetection/edgedetection.c | 6 +-- vector/v.lidar.edgedetection/main.c | 2 +- vector/v.lidar.growing/main.c | 2 +- vector/v.outlier/main.c | 2 +- vector/v.outlier/outlier.c | 4 +- vector/v.rectify/crs3d.c | 2 +- vector/v.surf.bspline/main.c | 4 +- vector/v.to.rast/dense_line.c | 6 +-- vector/v.vect.stats/main.c | 2 +- 91 files changed, 187 insertions(+), 196 deletions(-) diff --git a/.github/workflows/clang-format-check.yml b/.github/workflows/clang-format-check.yml index 8eeadca924d..dc39cdf463c 100644 --- a/.github/workflows/clang-format-check.yml +++ b/.github/workflows/clang-format-check.yml @@ -19,10 +19,10 @@ jobs: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: persist-credentials: false - - uses: DoozyX/clang-format-lint-action@11b773b1598aa4ae3b32f023701bca5201c3817d # v0.17 + - uses: DoozyX/clang-format-lint-action@caa179272c6ee7f1d25dfb503ee0c410c26ebd98 # v0.18.1 with: source: "." - clangFormatVersion: 17 + clangFormatVersion: 18.1.8 inplace: True - name: Create and uploads code suggestions to apply id: diff diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2dc9dc65f35..d900a158831 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -66,7 +66,7 @@ repos: .*/testsuite/.* ) - repo: https://github.com/pre-commit/mirrors-clang-format - rev: v17.0.6 + rev: v18.1.8 hooks: - id: clang-format types_or: [c, c++, javascript, json, objective-c] diff --git a/display/d.colortable/main.c b/display/d.colortable/main.c index 8400f973f54..53a1d32fa3d 100644 --- a/display/d.colortable/main.c +++ b/display/d.colortable/main.c @@ -222,7 +222,7 @@ int main(int argc, char **argv) if (atcat > (int)dmax) break; } /* col loop */ - } /* int map */ + } /* int map */ else { diff --git a/display/d.histogram/bar.c b/display/d.histogram/bar.c index e02527256f5..2a7f9e20688 100644 --- a/display/d.histogram/bar.c +++ b/display/d.histogram/bar.c @@ -249,7 +249,7 @@ int bar(struct stat_list *dist_stats, /* list of distribution statistics */ y_box[1] = y_box[2] = bar_height; D_polygon_abs(x_box, y_box, 4); } - } /* fp */ + } /* fp */ else { /* 1-color bar for int data or null */ D_color((CELL)bar_color, colors); diff --git a/imagery/i.ortho.photo/i.ortho.rectify/defs.h b/imagery/i.ortho.photo/i.ortho.rectify/defs.h index 9dd82ad9845..70c9c2ec66e 100644 --- a/imagery/i.ortho.photo/i.ortho.rectify/defs.h +++ b/imagery/i.ortho.photo/i.ortho.rectify/defs.h @@ -12,7 +12,7 @@ #define L2BSIZE (2 * (L2BDIM)) #define BSIZE (1 << (L2BSIZE)) #define HI(i) ((i) >> (L2BDIM)) -#define LO(i) ((i) & ((BDIM)-1)) +#define LO(i) ((i) & ((BDIM) - 1)) typedef DCELL block[BDIM][BDIM]; /* FCELL sufficient ? */ diff --git a/imagery/i.rectify/defs.h b/imagery/i.rectify/defs.h index 9dd82ad9845..70c9c2ec66e 100644 --- a/imagery/i.rectify/defs.h +++ b/imagery/i.rectify/defs.h @@ -12,7 +12,7 @@ #define L2BSIZE (2 * (L2BDIM)) #define BSIZE (1 << (L2BSIZE)) #define HI(i) ((i) >> (L2BDIM)) -#define LO(i) ((i) & ((BDIM)-1)) +#define LO(i) ((i) & ((BDIM) - 1)) typedef DCELL block[BDIM][BDIM]; /* FCELL sufficient ? */ diff --git a/imagery/i.segment/mean_shift.c b/imagery/i.segment/mean_shift.c index 5a6f4c5ab1c..833ff5ca8a8 100644 --- a/imagery/i.segment/mean_shift.c +++ b/imagery/i.segment/mean_shift.c @@ -536,7 +536,7 @@ static int find_best_neighbour(struct globals *globals, int row, int col, } } } - } while (n--); /* end do loop - next neighbor */ + } while (n--); /* end do loop - next neighbor */ } while (rclist_drop(&rilist, &next)); /* while there are cells to check */ rclist_destroy(&rilist); @@ -671,7 +671,7 @@ static int update_rid(struct globals *globals, int row, int col, int new_id) Segment_put(&globals->rid_seg, (void *)&new_id, rown, coln); } } - } while (n--); /* end do loop - next neighbor */ + } while (n--); /* end do loop - next neighbor */ } while (rclist_drop(&rilist, &next)); /* while there are cells to check */ rclist_destroy(&rilist); diff --git a/imagery/i.segment/region_growing.c b/imagery/i.segment/region_growing.c index dc5b5bd2157..d6ad2a02039 100644 --- a/imagery/i.segment/region_growing.c +++ b/imagery/i.segment/region_growing.c @@ -520,9 +520,9 @@ int region_growing(struct globals *globals) } } } /* end if < threshold */ - } /* end pathflag */ - } /* next col */ - } /* next row */ + } /* end pathflag */ + } /* next col */ + } /* next row */ G_percent(1, 1, 1); /* finished one pass for processing candidate pixels */ @@ -844,7 +844,7 @@ static int find_best_neighbor(struct ngbr_stats *Ri, struct reg_stats *Ri_rs, } } } - } while (n--); /* end do loop - next neighbor */ + } while (n--); /* end do loop - next neighbor */ } while (rclist_drop(&rilist, &next)); /* while there are cells to check */ /* clean up */ diff --git a/include/grass/iostream/embuffer.h b/include/grass/iostream/embuffer.h index af6f1fa7db5..cdc168614f9 100644 --- a/include/grass/iostream/embuffer.h +++ b/include/grass/iostream/embuffer.h @@ -817,7 +817,7 @@ void em_buffer::cleanup() } j++; } // if data[i] != NULL - } // for i + } // for i // set the index assert(index == j + empty); diff --git a/include/grass/iostream/empq_adaptive.h b/include/grass/iostream/empq_adaptive.h index 1699b9c9b2d..664545d8e27 100644 --- a/include/grass/iostream/empq_adaptive.h +++ b/include/grass/iostream/empq_adaptive.h @@ -57,7 +57,7 @@ class EMPQueueAdaptive { public: /* start in INMEM regim by allocating im of size precisely twice the size of the (pqueue within) the em_pqueue; */ - EMPQueueAdaptive(long N UNUSED) : EMPQueueAdaptive(){}; + EMPQueueAdaptive(long N UNUSED) : EMPQueueAdaptive() {}; EMPQueueAdaptive(); EMPQueueAdaptive(size_t inMem); ~EMPQueueAdaptive(); diff --git a/include/grass/iostream/minmaxheap.h b/include/grass/iostream/minmaxheap.h index b32688167c6..eab72de673e 100644 --- a/include/grass/iostream/minmaxheap.h +++ b/include/grass/iostream/minmaxheap.h @@ -780,8 +780,8 @@ void BasicMinMaxHeap::verify() template class MinMaxHeap : public BasicMinMaxHeap { public: - MinMaxHeap(HeapIndex size) : BasicMinMaxHeap(size){}; - virtual ~MinMaxHeap(){}; + MinMaxHeap(HeapIndex size) : BasicMinMaxHeap(size) {}; + virtual ~MinMaxHeap() {}; bool full(void) const { return this->size() >= this->maxsize; }; HeapIndex get_maxsize() const { return this->maxsize; }; HeapIndex fill(T *arr, HeapIndex n); @@ -822,9 +822,9 @@ HeapIndex MinMaxHeap::fill(T *arr, HeapIndex n) template class UnboundedMinMaxHeap : public BasicMinMaxHeap { public: - UnboundedMinMaxHeap() : BasicMinMaxHeap(MMHEAP_INITIAL_SIZE){}; - UnboundedMinMaxHeap(HeapIndex size) : BasicMinMaxHeap(size){}; - virtual ~UnboundedMinMaxHeap(){}; + UnboundedMinMaxHeap() : BasicMinMaxHeap(MMHEAP_INITIAL_SIZE) {}; + UnboundedMinMaxHeap(HeapIndex size) : BasicMinMaxHeap(size) {}; + virtual ~UnboundedMinMaxHeap() {}; protected: virtual void grow(); diff --git a/include/grass/iostream/replacementHeap.h b/include/grass/iostream/replacementHeap.h index ac2f0f5f995..485d4cef99b 100644 --- a/include/grass/iostream/replacementHeap.h +++ b/include/grass/iostream/replacementHeap.h @@ -51,7 +51,7 @@ class HeapElement { T value; AMI_STREAM *run; - HeapElement() : run(NULL){}; + HeapElement() : run(NULL) {}; friend ostream &operator<<(ostream &s, const HeapElement &p) { diff --git a/include/grass/iostream/replacementHeapBlock.h b/include/grass/iostream/replacementHeapBlock.h index 7e5b348b5a2..c1596d67585 100644 --- a/include/grass/iostream/replacementHeapBlock.h +++ b/include/grass/iostream/replacementHeapBlock.h @@ -52,7 +52,7 @@ class BlockHeapElement { T value; MEM_STREAM *run; - BlockHeapElement() : run(NULL){}; + BlockHeapElement() : run(NULL) {}; friend ostream &operator<<(ostream &s, const BlockHeapElement &p) { diff --git a/include/grass/iostream/rtimer.h b/include/grass/iostream/rtimer.h index d4107c92543..547dbeca5ec 100644 --- a/include/grass/iostream/rtimer.h +++ b/include/grass/iostream/rtimer.h @@ -47,17 +47,17 @@ typedef struct { time_t tv1, tv2; } Rtimer; -#define rt_start(rt) \ - if ((time(&(rt.tv1)) == ((time_t)-1))) { \ - perror("time"); \ - exit(1); \ +#define rt_start(rt) \ + if ((time(&(rt.tv1)) == ((time_t) - 1))) { \ + perror("time"); \ + exit(1); \ } /* doesn't really stop, just updates endtimes */ -#define rt_stop(rt) \ - if ((time(&(rt.tv2)) == ((time_t)-1))) { \ - perror("time"); \ - exit(1); \ +#define rt_stop(rt) \ + if ((time(&(rt.tv2)) == ((time_t) - 1))) { \ + perror("time"); \ + exit(1); \ } #define rt_u_useconds(rt) rt_w_useconds(rt) diff --git a/lib/datetime/incr1.c b/lib/datetime/incr1.c index f6bffc437a6..210b8bb2a96 100644 --- a/lib/datetime/incr1.c +++ b/lib/datetime/incr1.c @@ -452,7 +452,7 @@ static int _datetime_carry(DateTime *dt, int absolute) dt->month++; } /* end while */ - } /* end if */ + } /* end if */ /* undo giving year a SIGN, temporarily */ if (!absolute && dt->mode == DATETIME_ABSOLUTE) { diff --git a/lib/display/symbol.c b/lib/display/symbol.c index e391a5d190b..6aa56bca38a 100644 --- a/lib/display/symbol.c +++ b/lib/display/symbol.c @@ -117,7 +117,7 @@ static void symbol(const SYMBOL *Symb, double x0, double y0, break; } /* switch */ - } /* for loop */ + } /* for loop */ } /*! diff --git a/lib/external/parson/parson.c b/lib/external/parson/parson.c index b9975c2f5ea..9b85ca78cdc 100644 --- a/lib/external/parson/parson.c +++ b/lib/external/parson/parson.c @@ -96,7 +96,7 @@ #define IS_NUMBER_INVALID(x) (((x) * 0.0) != 0.0) #endif -#define OBJECT_INVALID_IX ((size_t)-1) +#define OBJECT_INVALID_IX ((size_t) - 1) static JSON_Malloc_Function parson_malloc = malloc; static JSON_Free_Function parson_free = free; diff --git a/lib/gis/lz4.h b/lib/gis/lz4.h index 952d4181636..9ab16d35e0e 100644 --- a/lib/gis/lz4.h +++ b/lib/gis/lz4.h @@ -87,11 +87,11 @@ extern "C" { #if defined(LZ4_DLL_EXPORT) && (LZ4_DLL_EXPORT == 1) #define LZ4LIB_API __declspec(dllexport) LZ4LIB_VISIBILITY #elif defined(LZ4_DLL_IMPORT) && (LZ4_DLL_IMPORT == 1) -#define LZ4LIB_API \ - __declspec(dllimport) \ - LZ4LIB_VISIBILITY /* It isn't required but allows generating better \ - code, saving a function pointer load from the IAT \ - and an indirect jump. */ +#define LZ4LIB_API \ + __declspec(dllimport) \ + LZ4LIB_VISIBILITY /* It isn't required but allows generating better \ + code, saving a function pointer load from the IAT \ + and an indirect jump. */ #else #define LZ4LIB_API LZ4LIB_VISIBILITY #endif diff --git a/lib/gis/plot.c b/lib/gis/plot.c index aedc17972f5..7cf3ac4260d 100644 --- a/lib/gis/plot.c +++ b/lib/gis/plot.c @@ -140,11 +140,11 @@ void G_setup_fill(int gap) st->row_fill = row_solid_fill; } -#define X(e) (st->left + st->xconv * ((e)-st->window.west)) +#define X(e) (st->left + st->xconv * ((e) - st->window.west)) #define Y(n) (st->top + st->yconv * (st->window.north - (n))) -#define EAST(x) (st->window.west + ((x)-st->left) / st->xconv) -#define NORTH(y) (st->window.north - ((y)-st->top) / st->yconv) +#define EAST(x) (st->window.west + ((x) - st->left) / st->xconv) +#define NORTH(y) (st->window.north - ((y) - st->top) / st->yconv) /*! * \brief Converts east,north to x,y diff --git a/lib/gmath/findzc.c b/lib/gmath/findzc.c index 57a8d6f5ac1..37755b68af3 100644 --- a/lib/gmath/findzc.c +++ b/lib/gmath/findzc.c @@ -103,7 +103,7 @@ int G_math_findzc(double conv[], int size, double zc[], double thresh, break; /* quit looking at neighbors */ } } /* for ni */ - } /* for p */ + } /* for p */ } return 0; diff --git a/lib/gmath/la.c b/lib/gmath/la.c index 51e5fb7b698..5c0e59cb862 100644 --- a/lib/gmath/la.c +++ b/lib/gmath/la.c @@ -1265,7 +1265,7 @@ double G_vector_norm_maxval(vec_struct *vc, int vflag) xval = *curpt; } } /* switch */ - } /* if(curpt != startpt) */ + } /* if(curpt != startpt) */ curpt += incr; ncells--; diff --git a/lib/imagery/georef.c b/lib/imagery/georef.c index 7c02964d4c4..f818226d447 100644 --- a/lib/imagery/georef.c +++ b/lib/imagery/georef.c @@ -43,7 +43,7 @@ struct MATRIX { /* CALCULATE OFFSET INTO ARRAY BASED ON R/C */ -#define M(row, col) m->v[(((row)-1) * (m->n)) + (col)-1] +#define M(row, col) m->v[(((row) - 1) * (m->n)) + (col) - 1] #define MSUCCESS 1 /* SUCCESS */ #define MNPTERR 0 /* NOT ENOUGH POINTS */ diff --git a/lib/imagery/georef_tps.c b/lib/imagery/georef_tps.c index f6cb1dfabed..ffbbe4d65dd 100644 --- a/lib/imagery/georef_tps.c +++ b/lib/imagery/georef_tps.c @@ -29,7 +29,7 @@ struct MATRIX { /* CALCULATE OFFSET INTO ARRAY BASED ON R/C */ -#define M(row, col) m->v[(((row)-1) * (m->n)) + (col)-1] +#define M(row, col) m->v[(((row) - 1) * (m->n)) + (col) - 1] #define MSUCCESS 1 /* SUCCESS */ #define MNPTERR 0 /* NOT ENOUGH POINTS */ diff --git a/lib/lidar/raster.c b/lib/lidar/raster.c index 7dbc120bfd9..4d7305c9a9b 100644 --- a/lib/lidar/raster.c +++ b/lib/lidar/raster.c @@ -313,6 +313,6 @@ int P_Regular_Points(struct Cell_head *Elaboration, struct Cell_head *Original, Segment_put(out_seg, &dval, row, col); } } /* END COL */ - } /* END ROW */ + } /* END ROW */ return 1; } diff --git a/lib/nviz/render.c b/lib/nviz/render.c index 682bc184c04..40c259f7ec9 100644 --- a/lib/nviz/render.c +++ b/lib/nviz/render.c @@ -158,21 +158,19 @@ int Nviz_create_render_window(struct render_window *rwin, void *display UNUSED, int width, int height) { #if defined(OPENGL_X11) - int attributeList[] = { - GLX_RGBA, - GLX_RED_SIZE, - 1, - GLX_GREEN_SIZE, - 1, - GLX_BLUE_SIZE, - 1, - GLX_DEPTH_SIZE, - 1, + int attributeList[] = {GLX_RGBA, + GLX_RED_SIZE, + 1, + GLX_GREEN_SIZE, + 1, + GLX_BLUE_SIZE, + 1, + GLX_DEPTH_SIZE, + 1, #if !defined(OPENGL_FBO) - GLX_DOUBLEBUFFER, + GLX_DOUBLEBUFFER, #endif - None - }; + None}; XVisualInfo *v; rwin->displayId = XOpenDisplay((char *)display); @@ -205,21 +203,19 @@ int Nviz_create_render_window(struct render_window *rwin, void *display UNUSED, XFree(v); #elif defined(OPENGL_AQUA) #if defined(OPENGL_AGL) - int attributeList[] = { - AGL_RGBA, - AGL_RED_SIZE, - 1, - AGL_GREEN_SIZE, - 1, - AGL_BLUE_SIZE, - 1, - AGL_DEPTH_SIZE, - 1, + int attributeList[] = {AGL_RGBA, + AGL_RED_SIZE, + 1, + AGL_GREEN_SIZE, + 1, + AGL_BLUE_SIZE, + 1, + AGL_DEPTH_SIZE, + 1, #if !defined(OPENGL_FBO) - AGL_DOUBLEBUFFER, + AGL_DOUBLEBUFFER, #endif - AGL_NONE - }; + AGL_NONE}; /* TODO: open mac display */ diff --git a/lib/ogsf/gsd_surf.c b/lib/ogsf/gsd_surf.c index 88931612665..d22e17a8918 100644 --- a/lib/ogsf/gsd_surf.c +++ b/lib/ogsf/gsd_surf.c @@ -2039,7 +2039,7 @@ int gsd_norm_arrows(geosurf *surf) cnt++; } } /* ea col */ - } /* ea row */ + } /* ea row */ gsd_popmatrix(); return (1); @@ -2355,7 +2355,7 @@ int gsd_surf_map(geosurf *surf) } /* close ii loop */ gsd_endtfan(); } /* end col */ - } /* end row */ + } /* end row */ gsd_popmatrix(); gsd_blend(0); diff --git a/lib/ogsf/gsd_wire.c b/lib/ogsf/gsd_wire.c index d036098c8c8..0da492b9b15 100644 --- a/lib/ogsf/gsd_wire.c +++ b/lib/ogsf/gsd_wire.c @@ -544,7 +544,7 @@ int gsd_wire_arrows(geosurf *surf) gsd_arrow(pt, curcolor, xres * 2, n, sz, surf); } /* ea col */ - } /* ea row */ + } /* ea row */ gsd_popmatrix(); gsd_colormode(CM_DIFFUSE); diff --git a/lib/ogsf/gsget.h b/lib/ogsf/gsget.h index 4117c50dfd7..6fe955ce5e5 100644 --- a/lib/ogsf/gsget.h +++ b/lib/ogsf/gsget.h @@ -21,9 +21,9 @@ /* cast to float, otherwise doesn't seem to handle neg. values */ -#define SCALE_ATT(att, val, low, high) \ - ((val) <= att->max_nz && (val) >= att->min_nz && att->range_nz \ - ? (((val)-att->min_nz) / att->range_nz) * ((high) - (low)) + (low) \ +#define SCALE_ATT(att, val, low, high) \ + ((val) <= att->max_nz && (val) >= att->min_nz && att->range_nz \ + ? (((val) - att->min_nz) / att->range_nz) * ((high) - (low)) + (low) \ : 0) #define GET_MAPATT(buff, offset, att) (get_mapatt(buff, offset, &(att))) diff --git a/lib/raster/close.c b/lib/raster/close.c index 7a0370285fc..36ce924cb8b 100644 --- a/lib/raster/close.c +++ b/lib/raster/close.c @@ -433,7 +433,7 @@ static int close_new(int fd, int ok) else { remove(fcb->null_temp_name); remove(path); /* again ? */ - } /* null_cur_row > 0 */ + } /* null_cur_row > 0 */ if (fcb->open_mode == OPEN_NEW_COMPRESSED) { /* auto compression */ fcb->row_ptr[fcb->cellhd.rows] = lseek(fcb->data_fd, 0L, SEEK_CUR); diff --git a/lib/raster/quant.c b/lib/raster/quant.c index 8d2ef87ffc6..17591508b01 100644 --- a/lib/raster/quant.c +++ b/lib/raster/quant.c @@ -680,7 +680,7 @@ CELL Rast_quant_get_cell_value(struct Quant *q, DCELL dcellVal) return NO_DATA; } } /* while */ - } /* looking up in fp_lookup */ + } /* looking up in fp_lookup */ if (!NO_FINITE_RULE) { p = Rast__quant_get_rule_for_d_raster_val(q, dcellVal); diff --git a/lib/raster3d/raster3d_intern.h b/lib/raster3d/raster3d_intern.h index afa7e53bba4..9680e973cc7 100644 --- a/lib/raster3d/raster3d_intern.h +++ b/lib/raster3d/raster3d_intern.h @@ -104,11 +104,8 @@ extern void Rast3d_fatal_error_noargs(const char * /* msg */); * x, y, and z are pointer to double values */ #define LOCATION_TO_COORD(region, north, east, top, x, y, z) \ - { \ - *x = EASTERN_TO_COL(east, region) * y = \ - NORTHERN_TO_ROW(north, region) * z = \ - TOP_TO_DEPTH(top, region) \ - } + { *x = EASTERN_TO_COL(east, region) *y = \ + NORTHERN_TO_ROW(north, region) *z = TOP_TO_DEPTH(top, region)} /* Row to north, col to east and depth to top macros * region is a pointer to the RASTER3D_Region structure @@ -118,10 +115,8 @@ extern void Rast3d_fatal_error_noargs(const char * /* msg */); #define COL_TO_EASTERN(region, x) region->west + x * region->ew_res; #define ROW_TO_NORTHERN(region, y) region->north - y * region->ns_res; #define DEPTH_TO_TOP(region, z) region->bottom + z * region->tb_res; -#define COORD_TO_LOCATION(region, x, y, z, north, east, top) \ - { \ - *east = COL_TO_EASTERN(region, x) * north = \ - ROW_TO_NORTHERN(region, y) * top = DEPTH_TO_TOP(region, z) \ - } +#define COORD_TO_LOCATION(region, x, y, z, north, east, top) \ + { *east = COL_TO_EASTERN(region, x) *north = \ + ROW_TO_NORTHERN(region, y) *top = DEPTH_TO_TOP(region, z)} #endif diff --git a/lib/vector/Vlib/break_lines.c b/lib/vector/Vlib/break_lines.c index aba39ae3995..6e3f066a0ef 100644 --- a/lib/vector/Vlib/break_lines.c +++ b/lib/vector/Vlib/break_lines.c @@ -572,7 +572,7 @@ int break_lines(struct Map_info *Map, struct ilist *List_break, nlines = Vect_get_num_lines(Map); } G_debug(3, "nlines = %d", nlines); - } /* for each line */ + } /* for each line */ G_percent(nlines, nlines, 1); /* finish it */ G_verbose_message(_("Intersections: %d"), nbreaks); diff --git a/lib/vector/Vlib/dangles.c b/lib/vector/Vlib/dangles.c index 51bf4b832a0..c01b43600e2 100644 --- a/lib/vector/Vlib/dangles.c +++ b/lib/vector/Vlib/dangles.c @@ -254,8 +254,8 @@ static void dangles(struct Map_info *Map, int type, int option, } dangles_removed++; } /* delete the chain */ - } /* lcount == 1 */ - } /* node <= nnodes */ + } /* lcount == 1 */ + } /* node <= nnodes */ G_verbose_message(_("%s lines: %d"), lmsg, lines_removed); G_verbose_message(_("%s dangles: %d"), lmsg, dangles_removed); } diff --git a/lib/vector/Vlib/intersect2.c b/lib/vector/Vlib/intersect2.c index a3a7ad1110f..fc4c943e6c8 100644 --- a/lib/vector/Vlib/intersect2.c +++ b/lib/vector/Vlib/intersect2.c @@ -343,7 +343,7 @@ static int cross_seg(int i, int j, int b) #define QEVT_OUT 2 #define QEVT_CRS 3 -#define GET_PARENT(p, c) ((p) = (int)(((c)-2) / 3 + 1)) +#define GET_PARENT(p, c) ((p) = (int)(((c) - 2) / 3 + 1)) #define GET_CHILD(c, p) ((c) = (int)(((p) * 3) - 1)) struct qitem { diff --git a/lib/vector/Vlib/snap.c b/lib/vector/Vlib/snap.c index 324157a6aa8..bdfe7257ee0 100644 --- a/lib/vector/Vlib/snap.c +++ b/lib/vector/Vlib/snap.c @@ -507,7 +507,7 @@ static void Vect_snap_lines_list_kdtree(struct Map_info *Map, Vect_write_line(Err, ltype, Points, Cats); } } - } /* for each line */ + } /* for each line */ G_percent(line_idx, List_lines->n_values, 2); /* finish it */ Vect_destroy_line_struct(Points); @@ -864,7 +864,7 @@ static void Vect_snap_lines_list_rtree(struct Map_info *Map, Vect_write_line(Err, ltype, Points, Cats); } } - } /* for each line */ + } /* for each line */ G_percent(line_idx, List_lines->n_values, 2); /* finish it */ Vect_destroy_line_struct(Points); diff --git a/lib/vector/rtree/split.c b/lib/vector/rtree/split.c index 5edb3ee1aa5..ba02c3ea512 100644 --- a/lib/vector/rtree/split.c +++ b/lib/vector/rtree/split.c @@ -583,9 +583,9 @@ static void RTreeMethodOne(struct RTree_PartitionVars *p, int minfill, best_side[i] = s; } } - } /* end of distribution check */ + } /* end of distribution check */ } while (s--); /* end of side check */ - } /* end of axis check */ + } /* end of axis check */ /* Use best distribution to classify branches */ if (best_axis != axis || best_side[best_axis] != side) diff --git a/lib/vector/vedit/break.c b/lib/vector/vedit/break.c index 5387bd3f016..4180de066a7 100644 --- a/lib/vector/vedit/break.c +++ b/lib/vector/vedit/break.c @@ -125,7 +125,7 @@ int Vedit_split_lines(struct Map_info *Map, struct ilist *List, nlines_modified++; } /* for each bounding box */ - } /* for each selected line */ + } /* for each selected line */ free_exit: Vect_destroy_line_struct(Points); diff --git a/lib/vector/vedit/vertex.c b/lib/vector/vedit/vertex.c index f64a10bee8a..c8f2a802eb2 100644 --- a/lib/vector/vedit/vertex.c +++ b/lib/vector/vedit/vertex.c @@ -325,7 +325,7 @@ int Vedit_remove_vertex(struct Map_info *Map, struct ilist *List, rewrite = 1; } } /* for each point */ - } /* for each bounding box */ + } /* for each bounding box */ if (rewrite) { /* rewrite the line */ diff --git a/ps/ps.map/ps_info.h b/ps/ps.map/ps_info.h index 31f9cb7bd36..5e57d3ed0b2 100644 --- a/ps/ps.map/ps_info.h +++ b/ps/ps.map/ps_info.h @@ -16,8 +16,8 @@ * and uncommented again because G_adjust_easting * in it is not best for each case, RB Jan 2000 */ -#define XCONV(E_COORD) (PS.map_left + PS.ew_to_x * ((E_COORD)-PS.w.west)) -#define YCONV(N_COORD) (PS.map_bot + PS.ns_to_y * ((N_COORD)-PS.w.south)) +#define XCONV(E_COORD) (PS.map_left + PS.ew_to_x * ((E_COORD) - PS.w.west)) +#define YCONV(N_COORD) (PS.map_bot + PS.ns_to_y * ((N_COORD) - PS.w.south)) struct PS_data { struct Cell_head w; diff --git a/raster/r.clump/minsize.c b/raster/r.clump/minsize.c index fef9bf03891..7182d69c3d0 100644 --- a/raster/r.clump/minsize.c +++ b/raster/r.clump/minsize.c @@ -316,7 +316,7 @@ static int find_best_neighbour(int bfd, int nin, DCELL *rng, int cfd, int csize, } } } - } while (n--); /* end do loop - next neighbor */ + } while (n--); /* end do loop - next neighbor */ } while (rclist_drop(&rilist, &next)); /* while there are cells to check */ if (Rbest) diff --git a/raster/r.contour/cont.c b/raster/r.contour/cont.c index f860b8719d0..eb2fca40459 100644 --- a/raster/r.contour/cont.c +++ b/raster/r.contour/cont.c @@ -124,9 +124,9 @@ void contour(double levels[], int nlevels, struct Map_info Map, DCELL **z, } Vect_reset_line(Points); } /* if checkedge */ - } /* if ! hit */ - } /* for columns */ - } /* for rows */ + } /* if ! hit */ + } /* for columns */ + } /* for rows */ /* check right and left borders (each row of first and last column) */ for (startcol = 0; startcol <= ncol - 2; startcol += (ncol - 2)) { @@ -162,9 +162,9 @@ void contour(double levels[], int nlevels, struct Map_info Map, DCELL **z, } Vect_reset_line(Points); } /* if checkedge */ - } /* if ! hit */ - } /* for rows */ - } /* for columns */ + } /* if ! hit */ + } /* for rows */ + } /* for columns */ /* check each interior Cell */ for (startrow = 1; startrow <= nrow - 3; startrow++) { @@ -202,10 +202,10 @@ void contour(double levels[], int nlevels, struct Map_info Map, DCELL **z, } Vect_reset_line(Points); } /* if checkedge */ - } /* if ! hit */ - } /* for rows */ - } /* for columns */ - } /* for levels */ + } /* if ! hit */ + } /* for rows */ + } /* for columns */ + } /* for levels */ if (ncrossing > 0) { G_warning(n_("%d crossing found", "%d crossings found", ncrossing), diff --git a/raster/r.cost/heap.c b/raster/r.cost/heap.c index 3b8f03b15a8..7894c3cfaf0 100644 --- a/raster/r.cost/heap.c +++ b/raster/r.cost/heap.c @@ -42,7 +42,7 @@ #include #include "cost.h" -#define GET_PARENT(c) (((c)-2) / 3 + 1) +#define GET_PARENT(c) (((c) - 2) / 3 + 1) #define GET_CHILD(p) (((p) * 3) - 1) static long next_point = 0; diff --git a/raster/r.horizon/benchmark/benchmark_rhorizon.py b/raster/r.horizon/benchmark/benchmark_rhorizon.py index 99ec0184aeb..e892d89307e 100644 --- a/raster/r.horizon/benchmark/benchmark_rhorizon.py +++ b/raster/r.horizon/benchmark/benchmark_rhorizon.py @@ -20,7 +20,7 @@ def main(): benchmark( size=int(mapsize**0.5), step=0, - label=f"r.horizon_{int(mapsize/1e6)}M", + label=f"r.horizon_{int(mapsize / 1e6)}M", results=results, ) diff --git a/raster/r.horizon/main.c b/raster/r.horizon/main.c index fdec073a76e..576f85f3afa 100644 --- a/raster/r.horizon/main.c +++ b/raster/r.horizon/main.c @@ -1232,8 +1232,8 @@ void calculate_raster_mode(const Settings *settings, const Geometry *geometry, horizon_raster[j - buffer_s][i - buffer_w] = shadow_angle; } /* undefs */ - } /* end of loop over columns */ - } /* end of parallel section */ + } /* end of loop over columns */ + } /* end of parallel section */ G_debug(1, "OUTGR() starts..."); OUTGR(settings, shad_filename, cellhd); diff --git a/raster/r.in.gdal/main.c b/raster/r.in.gdal/main.c index af74a591223..9066bdf9f64 100644 --- a/raster/r.in.gdal/main.c +++ b/raster/r.in.gdal/main.c @@ -1355,7 +1355,7 @@ static void ImportBand(GDALRasterBandH hBand, const char *output, Rast_put_row(cfR, cellReal, data_type); Rast_put_row(cfI, cellImg, data_type); } - } /* end of complex */ + } /* end of complex */ else if (l1bdriver) { /* AVHRR */ /* AVHRR - read from south to north to match GCPs */ /* AVHRR - as for other formats, read from north to south to match GCPs diff --git a/raster/r.proj/r.proj.h b/raster/r.proj/r.proj.h index 8ba24c139c9..2a780616760 100644 --- a/raster/r.proj/r.proj.h +++ b/raster/r.proj/r.proj.h @@ -10,7 +10,7 @@ #define L2BSIZE (2 * (L2BDIM)) #define BSIZE (1 << (L2BSIZE)) #define HI(i) ((i) >> (L2BDIM)) -#define LO(i) ((i) & ((BDIM)-1)) +#define LO(i) ((i) & ((BDIM) - 1)) typedef FCELL block[BDIM][BDIM]; diff --git a/raster/r.quant/read_rules.c b/raster/r.quant/read_rules.c index ade84427290..9f56ae9dc46 100644 --- a/raster/r.quant/read_rules.c +++ b/raster/r.quant/read_rules.c @@ -161,7 +161,7 @@ int read_rules(const char *filename) G_warning(_("%s is not a valid rule"), buf); break; } /* switch */ - } /* loop */ + } /* loop */ if (fp != stdin) fclose(fp); diff --git a/raster/r.random.surface/init.c b/raster/r.random.surface/init.c index 397f288b832..a2b4af5dc8b 100644 --- a/raster/r.random.surface/init.c +++ b/raster/r.random.surface/init.c @@ -101,7 +101,7 @@ void Init(void) for (i = 0; (Number = SeedStuff->answers[i]) && i < NumMaps; i++) { sscanf(Number, "%d", &(Seeds[i])); } /* /for */ - } /* /else */ + } /* /else */ CellBuffer = Rast_allocate_c_buf(); CatInfo.NumValue = (int *)G_malloc(CatInfo.NumCat * sizeof(int)); diff --git a/raster/r.recode/read_rules.c b/raster/r.recode/read_rules.c index e8254d5908f..b54b536a55e 100644 --- a/raster/r.recode/read_rules.c +++ b/raster/r.recode/read_rules.c @@ -129,7 +129,7 @@ int read_rules(FILE *fp) G_message(_("%s is not a valid rule"), buf); break; } /* switch */ - } /* loop */ + } /* loop */ return nrules; } diff --git a/raster/r.resamp.bspline/main.c b/raster/r.resamp.bspline/main.c index 20f0edbde70..5f82dce1a16 100644 --- a/raster/r.resamp.bspline/main.c +++ b/raster/r.resamp.bspline/main.c @@ -725,7 +725,7 @@ int main(int argc, char *argv[]) "Consider increasing the spline step.")); } } /*! END WHILE; last_column = TRUE */ - } /*! END WHILE; last_row = TRUE */ + } /*! END WHILE; last_row = TRUE */ Segment_close(&in_seg); /* release memory */ diff --git a/raster/r.series/main.c b/raster/r.series/main.c index 2997877decc..bfecbf117c0 100644 --- a/raster/r.series/main.c +++ b/raster/r.series/main.c @@ -617,7 +617,7 @@ int main(int argc, char *argv[]) computed++; } /* end for loop */ - } /* end parallel region */ + } /* end parallel region */ /* write output buffer to disk */ for (i = 0; i < num_outputs; i++) { diff --git a/raster/r.sim/simlib/hydro.c b/raster/r.sim/simlib/hydro.c index 1edcadc9458..74145ec9bff 100644 --- a/raster/r.sim/simlib/hydro.c +++ b/raster/r.sim/simlib/hydro.c @@ -388,7 +388,7 @@ void main_loop(void) } } /* else */ - } /*DEFined area */ + } /*DEFined area */ else { w[lw].m = 1e-10; /* eliminate walker if it is out of area */ diff --git a/raster/r.slope.aspect/main.c b/raster/r.slope.aspect/main.c index b3c30956467..86b4d5f5607 100644 --- a/raster/r.slope.aspect/main.c +++ b/raster/r.slope.aspect/main.c @@ -1042,7 +1042,7 @@ int main(int argc, char *argv[]) #pragma omp atomic update computed++; } /* end row loop */ - } /* end parallel region */ + } /* end parallel region */ /* write the computed buffer chunk to disk */ written = end; diff --git a/raster/r.spreadpath/main.c b/raster/r.spreadpath/main.c index df1177970aa..ee8083570d7 100644 --- a/raster/r.spreadpath/main.c +++ b/raster/r.spreadpath/main.c @@ -247,7 +247,7 @@ int main(int argc, char **argv) insert(&PRESENT_PT, row, col, backrow, backcol); } } /* loop over cols */ - } /* loop over rows */ + } /* loop over rows */ Rast_close(path_fd); } diff --git a/raster/r.stream.extract/do_astar.c b/raster/r.stream.extract/do_astar.c index b816e2bcbcb..12d0dfe6a2b 100644 --- a/raster/r.stream.extract/do_astar.c +++ b/raster/r.stream.extract/do_astar.c @@ -5,7 +5,7 @@ #include #include "local_proto.h" -#define GET_PARENT(c) ((((GW_LARGE_INT)(c)-2) >> 3) + 1) +#define GET_PARENT(c) ((((GW_LARGE_INT)(c) - 2) >> 3) + 1) #define GET_CHILD(p) (((GW_LARGE_INT)(p) << 3) - 6) HEAP_PNT heap_drop(void); diff --git a/raster/r.sun/main.c b/raster/r.sun/main.c index 87dd9e00f98..9e45e184dac 100644 --- a/raster/r.sun/main.c +++ b/raster/r.sun/main.c @@ -1441,7 +1441,7 @@ void joules2(struct SunGeometryConstDay *sunGeom, ss = 0; /* we've got the sunset */ } } /* end of while */ - } /* all-day radiation */ + } /* all-day radiation */ } /*////////////////////////////////////////////////////////////////////// */ diff --git a/raster/r.terraflow/ccforest.h b/raster/r.terraflow/ccforest.h index c32a1a3f7ba..90f65d7e06f 100644 --- a/raster/r.terraflow/ccforest.h +++ b/raster/r.terraflow/ccforest.h @@ -32,8 +32,8 @@ class keyvalue { T key, value; public: - keyvalue() : key(-1), value(-1){}; - keyvalue(T vk, T vv) : key(vk), value(vv){}; + keyvalue() : key(-1), value(-1) {}; + keyvalue(T vk, T vv) : key(vk), value(vv) {}; T getPriority() const { return key; }; T getValue() const { return value; }; diff --git a/raster/r.terraflow/flow.cpp b/raster/r.terraflow/flow.cpp index e48847052e5..83b7de0c378 100644 --- a/raster/r.terraflow/flow.cpp +++ b/raster/r.terraflow/flow.cpp @@ -119,7 +119,7 @@ class flow_waterWindower { AMI_STREAM *sweep_str; public: - flow_waterWindower(AMI_STREAM *str) : sweep_str(str){}; + flow_waterWindower(AMI_STREAM *str) : sweep_str(str) {}; void processWindow(dimension_type i, dimension_type j, waterWindowBaseType *a, waterWindowBaseType *b, waterWindowBaseType *c); diff --git a/raster/r.terraflow/grass2str.h b/raster/r.terraflow/grass2str.h index 1bafe421453..5f1aeafb451 100644 --- a/raster/r.terraflow/grass2str.h +++ b/raster/r.terraflow/grass2str.h @@ -211,7 +211,7 @@ void stream2_CELL(AMI_STREAM *str, dimension_type nrows, Rast_put_row(outfd, outrast, mtype); G_percent(i, nrows, 2); - } /* for i */ + } /* for i */ G_percent(1, 1, 2); /* finish it */ G_free(outrast); @@ -288,7 +288,7 @@ void stream2_CELL(AMI_STREAM *str, dimension_type nrows, Rast_put_row(outfd, outrast, CELL_TYPE); G_percent(i, nrows, 2); - } /* for i */ + } /* for i */ G_percent(1, 1, 1); /* finish it */ G_free(outrast); @@ -361,7 +361,7 @@ void stream2_FCELL(AMI_STREAM *str, dimension_type nrows, Rast_put_row(outfd, outrast, FCELL_TYPE); G_percent(i, nrows, 2); - } /* for i */ + } /* for i */ G_percent(1, 1, 1); /* finish it */ G_free(outrast); @@ -472,7 +472,7 @@ void stream2_FCELL(AMI_STREAM *str, dimension_type nrows, G_percent(i, nrows, 2); - } /* for i */ + } /* for i */ G_percent(1, 1, 1); /* finish it */ G_free(rast1); diff --git a/raster/r.terraflow/nodata.h b/raster/r.terraflow/nodata.h index c6f53c2c61d..c5ae09ab6ea 100644 --- a/raster/r.terraflow/nodata.h +++ b/raster/r.terraflow/nodata.h @@ -47,8 +47,8 @@ class nodataType : public ijBaseType { * this cell */ public: nodataType(dimension_type gi, dimension_type gj, cclabel_type glab) - : ijBaseType(gi, gj), label(glab), valid(true){}; - nodataType() : valid(false){}; + : ijBaseType(gi, gj), label(glab), valid(true) {}; + nodataType() : valid(false) {}; void invalidate() { valid = false; } elevation_type getElevation() { diff --git a/raster/r.terraflow/plateau.cpp b/raster/r.terraflow/plateau.cpp index 55dcae79272..edf20f7a3d1 100644 --- a/raster/r.terraflow/plateau.cpp +++ b/raster/r.terraflow/plateau.cpp @@ -311,7 +311,7 @@ class duplicateFixer { ccforest *colTree; public: - duplicateFixer(ccforest *p) : colTree(p){}; + duplicateFixer(ccforest *p) : colTree(p) {}; int compare(const plateauType &a, const plateauType &b) { int c = ijCmpPlateauType::compare(a, b); diff --git a/raster/r.terraflow/plateau.h b/raster/r.terraflow/plateau.h index fce2727d2d3..d629b18b8c4 100644 --- a/raster/r.terraflow/plateau.h +++ b/raster/r.terraflow/plateau.h @@ -37,9 +37,9 @@ class plateauType : public ijBaseType { public: plateauType(dimension_type gi, dimension_type gj, direction_type gdir, cclabel_type gcclabel = LABEL_UNDEF) - : ijBaseType(gi, gj), cclabel(gcclabel), dir(gdir), valid(true){}; + : ijBaseType(gi, gj), cclabel(gcclabel), dir(gdir), valid(true) {}; - plateauType() : valid(false){}; + plateauType() : valid(false) {}; ~plateauType() {} @@ -97,7 +97,7 @@ class plateauStats { plateauStats(cclabel_type l) : iMin(dimension_type_max), iMax(0), jMin(dimension_type_max), jMax(0), - size(0), label(l), hasSpill(false){}; + size(0), label(l), hasSpill(false) {}; void add(plateauType &pt) { diff --git a/raster/r.terraflow/sweep.cpp b/raster/r.terraflow/sweep.cpp index a9f88582938..2f7fba8bb13 100644 --- a/raster/r.terraflow/sweep.cpp +++ b/raster/r.terraflow/sweep.cpp @@ -347,6 +347,6 @@ void pushFlow(const sweepItem &swit, const flowValue &flow, } /* if (!is_nodata(elev_neighb)) */ } } /* for dj */ - } /* for di */ + } /* for di */ return; } diff --git a/raster/r.terraflow/types.h b/raster/r.terraflow/types.h index 5df8c38fa00..2982d333c59 100644 --- a/raster/r.terraflow/types.h +++ b/raster/r.terraflow/types.h @@ -74,7 +74,7 @@ typedef int bfs_depth_type; static const bfs_depth_type DEPTH_INITIAL = 1; #define IS_BOUNDARY(i, j, nr, nc) \ - (((i) == 0) || ((i) == ((nr)-1)) || ((j) == 0) || ((j) == ((nc)-1))) + (((i) == 0) || ((i) == ((nr) - 1)) || ((j) == 0) || ((j) == ((nc) - 1))) /* ---------------------------------------------------------------------- */ @@ -96,8 +96,8 @@ class labelFactory { class ijBaseType { public: dimension_type i, j; - ijBaseType() : i(-1), j(-1){}; - ijBaseType(dimension_type gi, dimension_type gj) : i(gi), j(gj){}; + ijBaseType() : i(-1), j(-1) {}; + ijBaseType(dimension_type gi, dimension_type gj) : i(gi), j(gj) {}; friend int operator==(const ijBaseType &a, const ijBaseType &b) { return (compare(a, b) == 0); diff --git a/raster/r.terraflow/water.cpp b/raster/r.terraflow/water.cpp index 2a6caf391f1..e0a0c857d0e 100644 --- a/raster/r.terraflow/water.cpp +++ b/raster/r.terraflow/water.cpp @@ -208,7 +208,7 @@ class waterWindower { AMI_STREAM *waterWindows; public: - waterWindower(AMI_STREAM *str) : waterWindows(str){}; + waterWindower(AMI_STREAM *str) : waterWindows(str) {}; void processWindow(dimension_type i, dimension_type j, waterGridType &point, waterWindowBaseType *a, waterWindowBaseType *b, waterWindowBaseType *c); @@ -405,7 +405,7 @@ class boundaryDetector { public: boundaryDetector(AMI_STREAM *str, const dimension_type gnrows, const dimension_type gncols) - : nrows(gnrows), ncols(gncols), boundaryStr(str){}; + : nrows(gnrows), ncols(gncols), boundaryStr(str) {}; void processWindow(dimension_type i, dimension_type j, labelElevType &point, labelElevType *a, labelElevType *b, labelElevType *c); diff --git a/raster/r.terraflow/water.h b/raster/r.terraflow/water.h index b9dd0cf720c..4c227c10bcc 100644 --- a/raster/r.terraflow/water.h +++ b/raster/r.terraflow/water.h @@ -40,11 +40,11 @@ class labelElevType : public ijBaseType { cclabel_type label; public: - labelElevType() : label(LABEL_UNDEF){}; + labelElevType() : label(LABEL_UNDEF) {}; labelElevType(dimension_type gi, dimension_type gj, elevation_type gel, cclabel_type glabel) - : ijBaseType(gi, gj), el(gel), label(glabel){}; + : ijBaseType(gi, gj), el(gel), label(glabel) {}; cclabel_type getLabel() const { return label; }; @@ -95,7 +95,7 @@ class boundaryType : public labelElevType { cclabel_type label2; public: - boundaryType() : label2(LABEL_UNDEF){}; + boundaryType() : label2(LABEL_UNDEF) {}; boundaryType(dimension_type gi, dimension_type gj, elevation_type gel, cclabel_type glabel1, cclabel_type glabel2) @@ -203,7 +203,7 @@ class fillPriority : public ijBaseType { bfs_depth_type depth; public: - fillPriority() : ijBaseType(-1, -1), el(-1), depth(DEPTH_INITIAL){}; + fillPriority() : ijBaseType(-1, -1), el(-1), depth(DEPTH_INITIAL) {}; fillPriority(elevation_type gel, bfs_depth_type gdepth, dimension_type gi, dimension_type gj) : ijBaseType(gi, gj), el(gel), depth(gdepth) @@ -226,7 +226,7 @@ class fillPLabel : public fillPriority { cclabel_type label; public: - fillPLabel() : label(LABEL_UNDEF){}; + fillPLabel() : label(LABEL_UNDEF) {}; fillPLabel(const fillPriority &gpriority, const cclabel_type glabel) : fillPriority(gpriority), label(glabel) { @@ -257,10 +257,10 @@ class waterWindowBaseType { direction_type dir; bfs_depth_type depth; waterWindowBaseType() - : el(nodataType::ELEVATION_NODATA), dir(0), depth(DEPTH_INITIAL){}; + : el(nodataType::ELEVATION_NODATA), dir(0), depth(DEPTH_INITIAL) {}; waterWindowBaseType(elevation_type gel, direction_type gdir, bfs_depth_type gdepth) - : el(gel), dir(gdir), depth(gdepth){}; + : el(gel), dir(gdir), depth(gdepth) {}; friend int operator==(const waterWindowBaseType &a, const waterWindowBaseType &b) { @@ -290,7 +290,7 @@ class waterType : public ijBaseType { cclabel_type label; public: - waterType() : label(LABEL_UNDEF){}; /* needed to sort */ + waterType() : label(LABEL_UNDEF) {}; /* needed to sort */ waterType(dimension_type gi, dimension_type gj, direction_type gdir, cclabel_type glabel = LABEL_UNDEF, bfs_depth_type gdepth = DEPTH_INITIAL) @@ -299,7 +299,7 @@ class waterType : public ijBaseType { } waterType(plateauType &data) : ijBaseType(data.i, data.j), dir(data.dir), depth(DEPTH_INITIAL), - label(data.cclabel){}; + label(data.cclabel) {}; direction_type getDirection() const { return dir; } bfs_depth_type getDepth() const { return depth; } cclabel_type getLabel() const { return label; } @@ -334,7 +334,7 @@ class waterGridType : public waterWindowBaseType { cclabel_type label; public: - waterGridType() : label(LABEL_UNDEF){}; + waterGridType() : label(LABEL_UNDEF) {}; waterGridType(elevation_type gel, direction_type gdir = DIRECTION_UNDEF, cclabel_type glabel = LABEL_UNDEF, bfs_depth_type gdepth = DEPTH_INITIAL) @@ -361,7 +361,7 @@ class packed8bit { unsigned char value; public: - packed8bit() : value(0){}; + packed8bit() : value(0) {}; void setBit(int k, int v = 1) { value = (int)value | ((v ? 1 : 0) << k); }; void resetBit(int k) { value &= ~(0x1 << k); }; int getBit(int k) const { return (value >> k) & 1; }; @@ -442,7 +442,7 @@ class compressedWaterWindowType : public compressedWaterWindowBaseType { public: compressedWaterWindowType() - : compressedWaterWindowBaseType(), label(LABEL_UNDEF){}; + : compressedWaterWindowBaseType(), label(LABEL_UNDEF) {}; compressedWaterWindowType(dimension_type gi, dimension_type gj, cclabel_type glabel, waterWindowBaseType *a, waterWindowBaseType *b, waterWindowBaseType *c) diff --git a/raster/r.terraflow/weightWindow.cpp b/raster/r.terraflow/weightWindow.cpp index 72ee4946a78..ccf55a82ca9 100644 --- a/raster/r.terraflow/weightWindow.cpp +++ b/raster/r.terraflow/weightWindow.cpp @@ -219,8 +219,8 @@ void weightWindow::compute(const dimension_type i, const dimension_type j, if (dirwin.get(di, dj) == true) { computeWeight(di, dj, elev_crt, elev_neighb); } - } /* for dj */ - } /* for di */ + } /* for dj */ + } /* for di */ normalize(); /* normalize the weights */ #ifdef CHECK_WEIGHTS diff --git a/raster/r.terraflow/weightWindow.h b/raster/r.terraflow/weightWindow.h index 8da5c9dd387..24a163a6932 100644 --- a/raster/r.terraflow/weightWindow.h +++ b/raster/r.terraflow/weightWindow.h @@ -58,7 +58,7 @@ class weightWindow { public: weightWindow(const float gdx, const float gdy); - ~weightWindow(){}; + ~weightWindow() {}; /***************************************************************/ /* Compute the weights of the neighbors of a cell given an elevation diff --git a/raster/r.thin/thin_lines.c b/raster/r.thin/thin_lines.c index e416999828a..4cc418dc2e5 100644 --- a/raster/r.thin/thin_lines.c +++ b/raster/r.thin/thin_lines.c @@ -152,7 +152,7 @@ int thin_lines(int iterations) } } /* end blank pixel */ - } /* end col loop */ + } /* end col loop */ for (col = box_left; col <= box_right; col++) row_buf[col] = med[col]; @@ -163,7 +163,7 @@ int thin_lines(int iterations) /* row, since put_a_row(row, med_row) was called and med */ med = bottom; } /* end row loop */ - } /* j-loop */ + } /* j-loop */ G_message(n_("Deleted %d pixel", "Deleted %d pixels", deleted), deleted); diff --git a/raster/r.walk/heap.c b/raster/r.walk/heap.c index 3b8f03b15a8..7894c3cfaf0 100644 --- a/raster/r.walk/heap.c +++ b/raster/r.walk/heap.c @@ -42,7 +42,7 @@ #include #include "cost.h" -#define GET_PARENT(c) (((c)-2) / 3 + 1) +#define GET_PARENT(c) (((c) - 2) / 3 + 1) #define GET_CHILD(p) (((p) * 3) - 1) static long next_point = 0; diff --git a/raster/r.watershed/ram/do_astar.c b/raster/r.watershed/ram/do_astar.c index 10f51ba8ac3..e829bc6cf45 100644 --- a/raster/r.watershed/ram/do_astar.c +++ b/raster/r.watershed/ram/do_astar.c @@ -176,7 +176,7 @@ int do_astar(void) } } } /* end if in region */ - } /* end sides */ + } /* end sides */ FLAG_SET(worked, r, c); } G_percent(count, do_points, 1); /* finish it */ diff --git a/raster/r.watershed/ram/do_astar.h b/raster/r.watershed/ram/do_astar.h index 886b06a18b6..151c92a293b 100644 --- a/raster/r.watershed/ram/do_astar.h +++ b/raster/r.watershed/ram/do_astar.h @@ -1,7 +1,7 @@ #ifndef __DO_ASTAR_H__ #define __DO_ASTAR_H__ -#define GET_PARENT(p, c) ((p) = (int)(((c)-2) / 3 + 1)) +#define GET_PARENT(p, c) ((p) = (int)(((c) - 2) / 3 + 1)) #define GET_CHILD(c, p) ((c) = (int)(((p) * 3) - 1)) #endif /* __DO_ASTAR_H__ */ diff --git a/raster/r.watershed/seg/do_astar.h b/raster/r.watershed/seg/do_astar.h index c2698c47dbe..f34a9696a07 100644 --- a/raster/r.watershed/seg/do_astar.h +++ b/raster/r.watershed/seg/do_astar.h @@ -1,7 +1,7 @@ #ifndef __DO_ASTAR_H__ #define __DO_ASTAR_H__ -#define GET_PARENT(c) (((((GW_LARGE_INT)(c)-2) >> 2) + 1)) +#define GET_PARENT(c) (((((GW_LARGE_INT)(c) - 2) >> 2) + 1)) #define GET_CHILD(p) ((((GW_LARGE_INT)(p) << 2) - 2)) /* diff --git a/raster/r.watershed/seg/init_vars.c b/raster/r.watershed/seg/init_vars.c index 5ee8f4f4eb3..5d7169b4c4e 100644 --- a/raster/r.watershed/seg/init_vars.c +++ b/raster/r.watershed/seg/init_vars.c @@ -593,7 +593,7 @@ int init_vars(int argc, char *argv[]) } } /* end non-NULL cell */ - } /* end column */ + } /* end column */ } G_percent(r, nrows, 1); /* finish it */ diff --git a/raster3d/r3.showdspf/main_ogl.c b/raster3d/r3.showdspf/main_ogl.c index aace5f0def6..90bf8e232a3 100644 --- a/raster3d/r3.showdspf/main_ogl.c +++ b/raster3d/r3.showdspf/main_ogl.c @@ -50,7 +50,7 @@ #endif #ifndef WAIT_ANY -#define WAIT_ANY ((pid_t)-1) +#define WAIT_ANY ((pid_t) - 1) #endif GLuint Material_1_Dlist; diff --git a/vector/v.cluster/main.c b/vector/v.cluster/main.c index 628c4032129..c3f95e78778 100644 --- a/vector/v.cluster/main.c +++ b/vector/v.cluster/main.c @@ -30,7 +30,7 @@ #define CL_OPTICS 4 #define CL_OPTICS2 5 -#define GET_PARENT(p, c) ((p) = (int)(((c)-2) / 3 + 1)) +#define GET_PARENT(p, c) ((p) = (int)(((c) - 2) / 3 + 1)) #define GET_CHILD(c, p) ((c) = (int)(((p) * 3) - 1)) struct cl_pnt { diff --git a/vector/v.db.connect/main.c b/vector/v.db.connect/main.c index 3ba874d1223..0d6c79485f3 100644 --- a/vector/v.db.connect/main.c +++ b/vector/v.db.connect/main.c @@ -203,7 +203,7 @@ int main(int argc, char **argv) } } } - } /* end print */ + } /* end print */ else { /* columns */ char *database_novar; @@ -241,8 +241,8 @@ int main(int argc, char **argv) db_close_database(driver); db_shutdown_driver(driver); } - } /* end else num_dblinks */ - } /* end print/columns */ + } /* end else num_dblinks */ + } /* end print/columns */ else { /* define new dbln settings or delete */ if (delete->answer) { diff --git a/vector/v.distance/main.c b/vector/v.distance/main.c index b12df376fa3..4aa372420f9 100644 --- a/vector/v.distance/main.c +++ b/vector/v.distance/main.c @@ -989,7 +989,7 @@ int main(int argc, char *argv[]) near->count++; } } /* done searching 'to' */ - } /* next from feature */ + } /* next from feature */ } /* Find nearest features for 'from' areas */ @@ -1353,7 +1353,7 @@ int main(int argc, char *argv[]) near->count++; } } /* done */ - } /* next feature */ + } /* next feature */ } G_debug(3, "count = %d", count); diff --git a/vector/v.extrude/main.c b/vector/v.extrude/main.c index 1050d176159..4887263dc5f 100644 --- a/vector/v.extrude/main.c +++ b/vector/v.extrude/main.c @@ -353,7 +353,7 @@ int main(int argc, char *argv[]) scale, opt.null->answer ? TRUE : FALSE, null_val, objheight, voffset, &window, type, -1); } /* for each line */ - } /* else if area */ + } /* else if area */ if (driver) { db_close_database(driver); diff --git a/vector/v.in.ascii/points.c b/vector/v.in.ascii/points.c index 280604d80a1..0fd1899c2e2 100644 --- a/vector/v.in.ascii/points.c +++ b/vector/v.in.ascii/points.c @@ -227,7 +227,7 @@ int points_analyse(FILE *ascii_in, FILE *ascii, char *fs, char *td, } } } /* if (x or y) */ - } /* PROJECTION_LL */ + } /* PROJECTION_LL */ else { if (strlen(tokens[i]) == 0) { if (i == xcol) { diff --git a/vector/v.lidar.correction/correction.c b/vector/v.lidar.correction/correction.c index a79d618b805..295880e756d 100644 --- a/vector/v.lidar.correction/correction.c +++ b/vector/v.lidar.correction/correction.c @@ -218,7 +218,7 @@ void P_Sparse_Correction(struct Map_info *In UNUSED, struct Map_info *Out, } } } /* if in General box */ - } /* while */ + } /* while */ G_percent(num_points, num_points, 2); Vect_destroy_line_struct(point); Vect_destroy_cats_struct(cats); diff --git a/vector/v.lidar.correction/main.c b/vector/v.lidar.correction/main.c index dd20046d31b..c1e71a75af4 100644 --- a/vector/v.lidar.correction/main.c +++ b/vector/v.lidar.correction/main.c @@ -443,7 +443,7 @@ int main(int argc, char *argv[]) } G_free(lcat); } /*! END WHILE; last_column = TRUE */ - } /*! END WHILE; last_row = TRUE */ + } /*! END WHILE; last_row = TRUE */ /* Dropping auxiliary table */ if (npoints > 0) { diff --git a/vector/v.lidar.edgedetection/edgedetection.c b/vector/v.lidar.edgedetection/edgedetection.c index c0d1b7312b4..90b4e3de117 100644 --- a/vector/v.lidar.edgedetection/edgedetection.c +++ b/vector/v.lidar.edgedetection/edgedetection.c @@ -373,10 +373,10 @@ void classification(struct Map_info *Out, struct Cell_head Elaboration, _("Impossible to write to aux table")); } /*else (1) */ - } /*else */ + } /*else */ } - } /*end if obs */ - } /*end for */ + } /*end if obs */ + } /*end for */ G_percent(num_points, num_points, 2); /* finish it */ db_commit_transaction(driver); diff --git a/vector/v.lidar.edgedetection/main.c b/vector/v.lidar.edgedetection/main.c index f8bde278680..be0b34a354e 100644 --- a/vector/v.lidar.edgedetection/main.c +++ b/vector/v.lidar.edgedetection/main.c @@ -476,7 +476,7 @@ int main(int argc, char *argv[]) "Consider changing the spline step.")); } } /*! END WHILE; last_column = TRUE */ - } /*! END WHILE; last_row = TRUE */ + } /*! END WHILE; last_row = TRUE */ /* Dropping auxiliary table */ if (npoints > 0) { diff --git a/vector/v.lidar.growing/main.c b/vector/v.lidar.growing/main.c index a8a36f81395..c59a145451a 100644 --- a/vector/v.lidar.growing/main.c +++ b/vector/v.lidar.growing/main.c @@ -572,7 +572,7 @@ int main(int argc, char *argv[]) } free_structmatrix(raster_matrix, 0, nrows - 1, 0, ncols - 1); } /*! END WHILE; last_column = TRUE */ - } /*! END WHILE; last_row = TRUE */ + } /*! END WHILE; last_row = TRUE */ Vect_close(&In); Vect_close(&First); diff --git a/vector/v.outlier/main.c b/vector/v.outlier/main.c index 572fba7ed98..1ae94db49f3 100644 --- a/vector/v.outlier/main.c +++ b/vector/v.outlier/main.c @@ -454,7 +454,7 @@ int main(int argc, char *argv[]) "Consider increasing spline step values.")); } } /*! END WHILE; last_column = TRUE */ - } /*! END WHILE; last_row = TRUE */ + } /*! END WHILE; last_row = TRUE */ /* Drop auxiliary table */ if (npoints > 0) { diff --git a/vector/v.outlier/outlier.c b/vector/v.outlier/outlier.c index a4e378cbe29..6d648bc64c4 100644 --- a/vector/v.outlier/outlier.c +++ b/vector/v.outlier/outlier.c @@ -216,10 +216,10 @@ void P_Outlier(struct Map_info *Out, struct Map_info *Outlier, _("Impossible to write in the database")); } /*else (1) */ - } /*else */ + } /*else */ } } /*end if obs */ - } /*end for */ + } /*end for */ G_percent(num_points, num_points, 2); G_debug(2, "P_outlier: done"); diff --git a/vector/v.rectify/crs3d.c b/vector/v.rectify/crs3d.c index 4c7f8ad96d6..0a0a732f91b 100644 --- a/vector/v.rectify/crs3d.c +++ b/vector/v.rectify/crs3d.c @@ -29,7 +29,7 @@ struct MATRIX { /* CALCULATE OFFSET INTO ARRAY BASED ON R/C */ -#define M(row, col) m->v[(((row)-1) * (m->n)) + (col)-1] +#define M(row, col) m->v[(((row) - 1) * (m->n)) + (col) - 1] #define MSUCCESS 1 /* SUCCESS */ #define MNPTERR 0 /* NOT ENOUGH POINTS */ diff --git a/vector/v.surf.bspline/main.c b/vector/v.surf.bspline/main.c index ce71dcb56a9..ef693dfb58a 100644 --- a/vector/v.surf.bspline/main.c +++ b/vector/v.surf.bspline/main.c @@ -879,7 +879,7 @@ int main(int argc, char *argv[]) G_free_matrix(obsVect_ext); G_free_ivector(lineVect_ext); } /* END FLAG_EXT == TRUE */ - } /* END GRID == FALSE */ + } /* END GRID == FALSE */ G_free_vector(parVect); G_free_matrix(obsVect); G_free_ivector(lineVect); @@ -894,7 +894,7 @@ int main(int argc, char *argv[]) "Consider increasing spline step values.")); } } /*! END WHILE; last_column = TRUE */ - } /*! END WHILE; last_row = TRUE */ + } /*! END WHILE; last_row = TRUE */ G_verbose_message(_("Writing output...")); /* Writing the output raster map */ diff --git a/vector/v.to.rast/dense_line.c b/vector/v.to.rast/dense_line.c index 37370db2884..7b7f143b81c 100644 --- a/vector/v.to.rast/dense_line.c +++ b/vector/v.to.rast/dense_line.c @@ -17,11 +17,11 @@ static struct state { static struct state *st = &state; -#define X(e) (st->left + st->xconv * ((e)-st->window.west)) +#define X(e) (st->left + st->xconv * ((e) - st->window.west)) #define Y(n) (st->top + st->yconv * (st->window.north - (n))) -#define EAST(x) (st->window.west + ((x)-st->left) / st->xconv) -#define NORTH(y) (st->window.north - ((y)-st->top) / st->yconv) +#define EAST(x) (st->window.west + ((x) - st->left) / st->xconv) +#define NORTH(y) (st->window.north - ((y) - st->top) / st->yconv) void dense_line(double x1, double y1, double x2, double y2, int (*point)(int, int)); diff --git a/vector/v.vect.stats/main.c b/vector/v.vect.stats/main.c index 5a5bd8cb8a4..ad58f4fede9 100644 --- a/vector/v.vect.stats/main.c +++ b/vector/v.vect.stats/main.c @@ -693,7 +693,7 @@ int main(int argc, char *argv[]) count++; } } /* next point in box */ - } /* next area */ + } /* next area */ G_debug(1, "count = %d", count); From 5b1a2a5f5ecf29fe0b9ab02314ef3fefc3539a46 Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Thu, 15 Aug 2024 02:21:12 -0400 Subject: [PATCH 131/471] r.terraflow: Handle memory reallocation errors gracefully (#3970) Co-authored-by: Nicklas Larsson --- raster/r.terraflow/unionFind.h | 48 ++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/raster/r.terraflow/unionFind.h b/raster/r.terraflow/unionFind.h index 698dcfc0c97..920421ca5d6 100644 --- a/raster/r.terraflow/unionFind.h +++ b/raster/r.terraflow/unionFind.h @@ -19,11 +19,15 @@ #ifndef __UNION_FIND #define __UNION_FIND -#include -#include -#include +#include +#include +#include #include +extern "C" { +#include +} + /* initial range guesstimate */ #define UNION_INITIAL_SIZE 2000 @@ -68,11 +72,19 @@ unionFind::unionFind() { maxsize = UNION_INITIAL_SIZE; /* parent = new (long)[maxsize]; */ - parent = (T *)calloc(maxsize, sizeof(T)); - assert(parent); + if (void *new_parent = std::calloc(maxsize, sizeof(T))) { + parent = static_cast(new_parent); + } + else { + G_fatal_error(_("Not enough memory for %s"), "parent"); + } /* rank = new (long)[maxsize]; */ - rank = (T *)calloc(maxsize, sizeof(T)); - assert(rank); + if (void *new_rank = std::calloc(maxsize, sizeof(T))) { + rank = static_cast(new_rank); + } + else { + G_fatal_error(_("Not enough memory for %s"), "rank"); + } } /************************************************************/ @@ -126,13 +138,21 @@ inline void unionFind::makeSet(T x) if (x >= maxsize) { /* reallocate parent */ cout << "UnionFind::makeSet: reallocate double " << maxsize << "\n"; - parent = (T *)realloc(parent, 2 * maxsize * sizeof(T)); - assert(parent); - memset(parent + maxsize, 0, maxsize * sizeof(T)); - /*reallocate rank */ - rank = (T *)realloc(rank, 2 * maxsize * sizeof(T)); - assert(rank); - memset(rank + maxsize, 0, maxsize * sizeof(T)); + if (void *new_parent = std::realloc(parent, 2 * maxsize * sizeof(T))) { + parent = static_cast(new_parent); + std::memset(parent + maxsize, 0, maxsize * sizeof(T)); + } + else { + G_fatal_error(_("Not enough memory for %s"), "parent"); + } + /* reallocate rank */ + if (void *new_rank = std::realloc(rank, 2 * maxsize * sizeof(T))) { + rank = static_cast(new_rank); + std::memset(rank + maxsize, 0, maxsize * sizeof(T)); + } + else { + G_fatal_error(_("Not enough memory for %s"), "rank"); + } /*update maxsize */ maxsize *= 2; } From cde91d34044a8cbb11c3bca70c8fc337dafb03ca Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 15 Aug 2024 12:34:40 -0400 Subject: [PATCH 132/471] CI(deps): Update super-linter/super-linter action to v7 (#4185) --- .github/workflows/super-linter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/super-linter.yml b/.github/workflows/super-linter.yml index 71d2501e573..745c1bd2b89 100644 --- a/.github/workflows/super-linter.yml +++ b/.github/workflows/super-linter.yml @@ -31,7 +31,7 @@ jobs: # list of files that changed across commits fetch-depth: 0 - name: Lint code base - uses: super-linter/super-linter/slim@1fa6ba58a88783e9714725cf89ac26d53e80c148 # v6.9.0 + uses: super-linter/super-linter/slim@02a1172d274f021e4c70f66e23f1085eadd1064b # v7.0.0 env: DEFAULT_BRANCH: main # To report GitHub Actions status checks From 35e2e4320b8e44a071dc4a58ccc96d6f5650cb7f Mon Sep 17 00:00:00 2001 From: Nicklas Larsson Date: Thu, 15 Aug 2024 20:18:48 +0200 Subject: [PATCH 133/471] utils: sync grass_clang_format script to pre-commit config (#4186) --- utils/grass_clang_format.sh | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/utils/grass_clang_format.sh b/utils/grass_clang_format.sh index ecc55067025..de9f720364d 100755 --- a/utils/grass_clang_format.sh +++ b/utils/grass_clang_format.sh @@ -22,7 +22,7 @@ set -eu # It is also possible to format the content in a (one) given directory: # ./utils/grass_clang_format.sh ./lib/raster # -# COPYRIGHT: (C) 2023 by the GRASS Development Team +# COPYRIGHT: (C) 2023-2024 by the GRASS Development Team # # This program is free software under the GNU General Public # License (>=v2). Read the file COPYING that comes with GRASS @@ -30,8 +30,19 @@ set -eu # ############################################################################### +# Check if variable is integer (POSIX compliant) +# based on https://unix.stackexchange.com/a/598047 +is_integer () +{ + case "${1#[+-]}" in + (*[!0123456789]*) echo 1 ;; + ('') echo 1 ;; + (*) echo 0 ;; + esac +} + # Required clang-format version -req_cf_v="15" +req_cf_v="18" # No need to continue if the .clang-format file isn't found if [ ! -f .clang-format ]; then @@ -52,6 +63,17 @@ if ! (${fmt} --version >/dev/null); then exit 1 fi +# Try extract ClangFormat version from pre-commit config file +pre_commit_config_file=".pre-commit-config.yaml" +pre_commit_version=$(grep -A 1 --regex "repo:.*clang-format" \ + "${pre_commit_config_file}" | sed -En 's/.*rev: v([0-9]+)\..*/\1/p') +if [ "$(is_integer "$pre_commit_version")" -eq "1" ]; then + echo "Warning: failed to retrieve ClangFormat version number from" + echo " ${pre_commit_config_file}. Falling back to version {$req_cf_v}." +else + req_cf_v="${pre_commit_version}" +fi + clang_version_full=$(${fmt} --version) clang_version=$(echo "${clang_version_full}" | \ sed -En 's/.*version ([0-9]+)\.[0-9]+\.[0-9]+.*/\1/p') From 5606b888bc17943c96a27aa7daadf3e43b3d93a2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 16 Aug 2024 06:55:43 -0400 Subject: [PATCH 134/471] CI(deps): Update rui314/setup-mold digest to 0bf4f07 (#4188) --- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/pytest.yml | 2 +- .github/workflows/python-code-quality.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index e1da939842e..c86e7bfc165 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -52,7 +52,7 @@ jobs: sudo apt-get install -y wget git gawk findutils xargs -a <(awk '! /^ *(#|$)/' ".github/workflows/apt.txt") -r -- \ sudo apt-get install -y --no-install-recommends --no-install-suggests - - uses: rui314/setup-mold@2e332a0b602c2fc65d2d3995941b1b29a5f554a0 # v1 + - uses: rui314/setup-mold@0bf4f07ef9048ec62a45f9dbf2f098afa49695f0 # v1 if: ${{ matrix.language == 'c-cpp' }} - name: Initialize CodeQL diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 17b6203ffe1..997798d8873 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -46,7 +46,7 @@ jobs: xargs -a <(awk '! /^ *(#|$)/' ".github/workflows/apt.txt") -r -- \ sudo apt-get install -y --no-install-recommends --no-install-suggests - - uses: rui314/setup-mold@2e332a0b602c2fc65d2d3995941b1b29a5f554a0 # v1 + - uses: rui314/setup-mold@0bf4f07ef9048ec62a45f9dbf2f098afa49695f0 # v1 - name: Install Python dependencies run: | diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index f4413f5a885..93d3fee4d9e 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -147,7 +147,7 @@ jobs: run: | echo "MAKEFLAGS=-j$(nproc)" >> $GITHUB_ENV - - uses: rui314/setup-mold@2e332a0b602c2fc65d2d3995941b1b29a5f554a0 # v1 + - uses: rui314/setup-mold@0bf4f07ef9048ec62a45f9dbf2f098afa49695f0 # v1 - name: Build run: .github/workflows/build_${{ matrix.os }}.sh $HOME/install From 6e97777a1362b51f98ea2e420d699debb9264ccb Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 17 Aug 2024 02:34:43 +0000 Subject: [PATCH 135/471] CI(deps): Update ruff to v0.6.1 (#4184) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * CI(deps): Update ruff to v0.6.1 * style: Use convention `import xml.etree.ElementTree as ET` for unconventional-import-alias (ICN001) * checks: Ruff now includes checking of Jupyter notebooks by default --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Edouard Choinière <27212526+echoix@users.noreply.github.com> --- .github/workflows/python-code-quality.yml | 2 +- .pre-commit-config.yaml | 3 +- gui/wxpython/animation/nviztask.py | 4 +- gui/wxpython/core/menutree.py | 4 +- gui/wxpython/core/toolboxes.py | 56 ++++++++++---------- gui/wxpython/gmodeler/model.py | 4 +- gui/wxpython/gui_core/forms.py | 4 +- gui/wxpython/lmgr/workspace.py | 4 +- gui/wxpython/tools/update_menudata.py | 4 +- pyproject.toml | 4 +- python/grass/gunittest/reporters.py | 6 +-- python/grass/script/task.py | 8 +-- scripts/g.extension.all/g.extension.all.py | 6 +-- scripts/g.extension/g.extension.py | 34 ++++++------ scripts/g.search.modules/g.search.modules.py | 8 +-- scripts/r.in.wms/wms_cap_parsers.py | 8 +-- scripts/r.in.wms/wms_gdal_drv.py | 46 ++++++++-------- 17 files changed, 101 insertions(+), 104 deletions(-) diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index 93d3fee4d9e..3ca7e512a3a 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -36,7 +36,7 @@ jobs: # renovate: datasource=pypi depName=bandit BANDIT_VERSION: "1.7.9" # renovate: datasource=pypi depName=ruff - RUFF_VERSION: "0.5.7" + RUFF_VERSION: "0.6.1" runs-on: ${{ matrix.os }} permissions: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d900a158831..4bb4781ebe6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -37,11 +37,10 @@ repos: ) - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.5.7 + rev: v0.6.1 hooks: # Run the linter. - id: ruff - types_or: [python, pyi, jupyter] args: [--fix, --preview] - repo: https://github.com/igorshubovych/markdownlint-cli rev: v0.41.0 diff --git a/gui/wxpython/animation/nviztask.py b/gui/wxpython/animation/nviztask.py index 437bba7946f..26605737ea8 100644 --- a/gui/wxpython/animation/nviztask.py +++ b/gui/wxpython/animation/nviztask.py @@ -14,7 +14,7 @@ @author Anna Petrasova """ -import xml.etree.ElementTree as etree +import xml.etree.ElementTree as ET from core.workspace import ProcessWorkspaceFile from core.gcmd import RunCommand, GException @@ -33,7 +33,7 @@ def Load(self, filename): self.task = gtask.grassTask("m.nviz.image") self.filename = filename try: - gxwXml = ProcessWorkspaceFile(etree.parse(self.filename)) + gxwXml = ProcessWorkspaceFile(ET.parse(self.filename)) except Exception: raise GException( _( diff --git a/gui/wxpython/core/menutree.py b/gui/wxpython/core/menutree.py index c0c2770c124..4c55d7e9521 100644 --- a/gui/wxpython/core/menutree.py +++ b/gui/wxpython/core/menutree.py @@ -36,7 +36,7 @@ import os import sys import copy -import xml.etree.ElementTree as etree +import xml.etree.ElementTree as ET import wx @@ -62,7 +62,7 @@ def __init__(self, filename, expandAddons=True, message_handler=GError): group="appearance", key="menustyle", subkey="selection" ) - xmlTree = etree.parse(filename) + xmlTree = ET.parse(filename) if expandAddons: expAddons(xmlTree) for message in getToolboxMessages(): diff --git a/gui/wxpython/core/toolboxes.py b/gui/wxpython/core/toolboxes.py index 2fca91f8a90..441898fa8f6 100644 --- a/gui/wxpython/core/toolboxes.py +++ b/gui/wxpython/core/toolboxes.py @@ -17,7 +17,7 @@ import sys import copy import shutil -import xml.etree.ElementTree as etree +import xml.etree.ElementTree as ET from xml.parsers import expat import grass.script.task as gtask @@ -25,7 +25,7 @@ from grass.script.utils import try_remove, decode from grass.exceptions import ScriptError, CalledModuleError -ETREE_EXCEPTIONS = (etree.ParseError, expat.ExpatError) +ETREE_EXCEPTIONS = (ET.ParseError, expat.ExpatError) # duplicating code from core/globalvar.py # if this will become part of grass Python library or module, this should be @@ -270,19 +270,19 @@ def createTree(distributionRootFile, userRootFile, userDefined=True): :return: ElementTree instance """ if userDefined and userRootFile: - mainMenu = etree.parse(userRootFile) + mainMenu = ET.parse(userRootFile) else: - mainMenu = etree.parse(distributionRootFile) + mainMenu = ET.parse(distributionRootFile) - toolboxes = etree.parse(toolboxesFile) + toolboxes = ET.parse(toolboxesFile) if userDefined and _getUserToolboxesFile(): - userToolboxes = etree.parse(_getUserToolboxesFile()) + userToolboxes = ET.parse(_getUserToolboxesFile()) else: userToolboxes = None - wxguiItems = etree.parse(wxguiItemsFile) - moduleItems = etree.parse(moduleItemsFile) + wxguiItems = ET.parse(wxguiItemsFile) + moduleItems = ET.parse(moduleItemsFile) return toolboxes2menudata( mainMenu=mainMenu, @@ -463,11 +463,11 @@ def _expandUserToolboxesItem(node, toolboxes): for n in node.findall("./items/user-toolboxes-list"): items = node.find("./items") idx = list(items).index(n) - el = etree.Element("toolbox", attrib={"name": "GeneratedUserToolboxesList"}) + el = ET.Element("toolbox", attrib={"name": "GeneratedUserToolboxesList"}) items.insert(idx, el) - label = etree.SubElement(el, "label") + label = ET.SubElement(el, "label") label.text = _("Custom toolboxes") - it = etree.SubElement(el, "items") + it = ET.SubElement(el, "items") for toolbox in tboxes: it.append(copy.deepcopy(toolbox)) items.remove(n) @@ -551,15 +551,15 @@ def _expandAddonsItem(node): idx = list(items).index(n) # do not set name since it is already in menudata file # attib={'name': 'AddonsList'} - el = etree.Element("menu") + el = ET.Element("menu") items.insert(idx, el) - label = etree.SubElement(el, "label") + label = ET.SubElement(el, "label") label.text = _("Addons") - it = etree.SubElement(el, "items") + it = ET.SubElement(el, "items") for addon in addons: - addonItem = etree.SubElement(it, "module-item") + addonItem = ET.SubElement(it, "module-item") addonItem.attrib = {"name": addon} - addonLabel = etree.SubElement(addonItem, "label") + addonLabel = ET.SubElement(addonItem, "label") addonLabel.text = addon items.remove(n) @@ -613,7 +613,7 @@ def _expandRuntimeModules(node, loadMetadata=True): for module in modules: name = module.get("name") if module.find("module") is None: - n = etree.SubElement(module, "module") + n = ET.SubElement(module, "module") n.text = name if module.find("description") is None: @@ -627,9 +627,9 @@ def _expandRuntimeModules(node, loadMetadata=True): desc, keywords = _("Module not installed"), "" else: desc, keywords = "", "" - n = etree.SubElement(module, "description") + n = ET.SubElement(module, "description") n.text = _escapeXML(desc) - n = etree.SubElement(module, "keywords") + n = ET.SubElement(module, "keywords") n.text = _escapeXML(",".join(keywords)) if hasErrors: @@ -672,13 +672,13 @@ def _addHandlers(node): """Add missing handlers to modules""" for n in node.findall(".//module-item"): if n.find("handler") is None: - handlerNode = etree.SubElement(n, "handler") + handlerNode = ET.SubElement(n, "handler") handlerNode.text = "OnMenuCmd" # e.g. g.region -p for n in node.findall(".//wxgui-item"): if n.find("command") is not None: - handlerNode = etree.SubElement(n, "handler") + handlerNode = ET.SubElement(n, "handler") handlerNode.text = "RunMenuCmd" @@ -751,7 +751,7 @@ def _getXMLString(root): :return: XML as string """ - xml = etree.tostring(root, encoding="UTF-8") + xml = ET.tostring(root, encoding="UTF-8") return xml.replace( b"\n", b"\n" @@ -820,12 +820,12 @@ def module_test(): wxguiItemsFile = os.path.join(WXGUIDIR, "xml", "wxgui_items.xml") moduleItemsFile = os.path.join(WXGUIDIR, "xml", "module_items.xml") - toolboxes = etree.parse(toolboxesFile) - userToolboxes = etree.parse(userToolboxesFile) - menu = etree.parse(menuFile) + toolboxes = ET.parse(toolboxesFile) + userToolboxes = ET.parse(userToolboxesFile) + menu = ET.parse(menuFile) - wxguiItems = etree.parse(wxguiItemsFile) - moduleItems = etree.parse(moduleItemsFile) + wxguiItems = ET.parse(wxguiItemsFile) + moduleItems = ET.parse(moduleItemsFile) tree = toolboxes2menudata( mainMenu=menu, @@ -868,7 +868,7 @@ def module_test(): def validate_file(filename): try: - etree.parse(filename) + ET.parse(filename) except ETREE_EXCEPTIONS as error: print( "XML file <{name}> is not well formed: {error}".format( diff --git a/gui/wxpython/gmodeler/model.py b/gui/wxpython/gmodeler/model.py index 1d0e508d7c5..4a587230fa9 100644 --- a/gui/wxpython/gmodeler/model.py +++ b/gui/wxpython/gmodeler/model.py @@ -38,7 +38,7 @@ import mimetypes import time -import xml.etree.ElementTree as etree +import xml.etree.ElementTree as ET from xml.sax import saxutils import wx @@ -323,7 +323,7 @@ def LoadModel(self, filename): """ # parse workspace file try: - gxmXml = ProcessModelFile(etree.parse(filename)) + gxmXml = ProcessModelFile(ET.parse(filename)) except Exception as e: raise GException("{}".format(e)) diff --git a/gui/wxpython/gui_core/forms.py b/gui/wxpython/gui_core/forms.py index d86c7ded554..4ff6f0ee0ee 100644 --- a/gui/wxpython/gui_core/forms.py +++ b/gui/wxpython/gui_core/forms.py @@ -65,7 +65,7 @@ import wx.lib.filebrowsebutton as filebrowse from wx.lib.newevent import NewEvent -import xml.etree.ElementTree as etree +import xml.etree.ElementTree as ET # needed when started from command line and for testing if __name__ == "__main__": @@ -3221,7 +3221,7 @@ def GetCommandInputMapParamKey(self, cmd): """ # parse the interface description if not self.grass_task: - tree = etree.fromstring(gtask.get_interface_description(cmd)) + tree = ET.fromstring(gtask.get_interface_description(cmd)) self.grass_task = gtask.processTask(tree).get_task() for p in self.grass_task.params: diff --git a/gui/wxpython/lmgr/workspace.py b/gui/wxpython/lmgr/workspace.py index d8c9be0befc..febb3922dd7 100644 --- a/gui/wxpython/lmgr/workspace.py +++ b/gui/wxpython/lmgr/workspace.py @@ -16,7 +16,7 @@ import os import tempfile -import xml.etree.ElementTree as etree +import xml.etree.ElementTree as ET import wx import wx.aui @@ -171,7 +171,7 @@ def Load(self, filename): """ # parse workspace file try: - gxwXml = ProcessWorkspaceFile(etree.parse(filename)) + gxwXml = ProcessWorkspaceFile(ET.parse(filename)) except Exception as e: GError( parent=self.lmgr, diff --git a/gui/wxpython/tools/update_menudata.py b/gui/wxpython/tools/update_menudata.py index d2f979fcef0..2f548da3087 100644 --- a/gui/wxpython/tools/update_menudata.py +++ b/gui/wxpython/tools/update_menudata.py @@ -23,7 +23,7 @@ import sys import tempfile -import xml.etree.ElementTree as etree +import xml.etree.ElementTree as ET from grass.script import core as grass from grass.script import task as gtask @@ -97,7 +97,7 @@ def updateData(data, modules): grass.warning("%s: keywords missing" % module) else: if node.find("keywords") is None: - node.insert(2, etree.Element("keywords")) + node.insert(2, ET.Element("keywords")) grass.warning("Adding tag 'keywords' to '%s'" % module) node.find("keywords").text = ",".join(modules[module]["keywords"]) diff --git a/pyproject.toml b/pyproject.toml index c28c944f345..930c538049b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,14 +14,12 @@ extend-exclude = ''' ''' [tool.ruff] -required-version = ">=0.5.0" +required-version = ">=0.6.0" builtins = ["_"] # In addition to the standard set of exclusions, omit the following files or folders. extend-exclude = ["python/libgrass_interface_generator"] -# In addition to the standard set of inclusions, include `.ipynb` files. -extend-include = ["*.ipynb"] [tool.ruff.lint] # See https://docs.astral.sh/ruff/rules/ diff --git a/python/grass/gunittest/reporters.py b/python/grass/gunittest/reporters.py index bf82c768509..0911ca0fdd7 100644 --- a/python/grass/gunittest/reporters.py +++ b/python/grass/gunittest/reporters.py @@ -13,7 +13,7 @@ import datetime from pathlib import Path from xml.sax import saxutils -import xml.etree.ElementTree as et +import xml.etree.ElementTree as ET import subprocess import collections import re @@ -199,7 +199,7 @@ def get_svn_info(): rc = p.poll() info = {} if not rc: - root = et.fromstring(stdout) + root = ET.fromstring(stdout) # TODO: get also date if this make sense # expecting only one element entry = root.find("entry") @@ -257,7 +257,7 @@ def get_svn_path_authors(path, from_date=None): stdout, stderr = p.communicate() rc = p.poll() if not rc: - root = et.fromstring(stdout) + root = ET.fromstring(stdout) # TODO: get also date if this make sense # expecting only one element author_nodes = root.iterfind("*/author") diff --git a/python/grass/script/task.py b/python/grass/script/task.py index 7d635dc83bb..35234ba65e6 100644 --- a/python/grass/script/task.py +++ b/python/grass/script/task.py @@ -21,14 +21,14 @@ import os import re import sys -import xml.etree.ElementTree as etree +import xml.etree.ElementTree as ET from xml.parsers import expat from grass.exceptions import ScriptError from .utils import decode, split from .core import Popen, PIPE, get_real_command -ETREE_EXCEPTIONS = (etree.ParseError, expat.ExpatError) +ETREE_EXCEPTIONS = (ET.ParseError, expat.ExpatError) class grassTask: @@ -63,7 +63,7 @@ def __init__(self, path=None, blackList=None): if path is not None: try: processTask( - tree=etree.fromstring(get_interface_description(path)), task=self + tree=ET.fromstring(get_interface_description(path)), task=self ) except ScriptError as e: self.errorMsg = e.value @@ -523,7 +523,7 @@ def parse_interface(name, parser=processTask, blackList=None): :param blackList: """ try: - tree = etree.fromstring(get_interface_description(name)) + tree = ET.fromstring(get_interface_description(name)) except ETREE_EXCEPTIONS as error: raise ScriptError( _("Cannot parse interface description of<{name}> module: {error}").format( diff --git a/scripts/g.extension.all/g.extension.all.py b/scripts/g.extension.all/g.extension.all.py index 8535dc06502..026589cca10 100644 --- a/scripts/g.extension.all/g.extension.all.py +++ b/scripts/g.extension.all/g.extension.all.py @@ -41,7 +41,7 @@ import re import sys -import xml.etree.ElementTree as etree +import xml.etree.ElementTree as ET from urllib import request as urlrequest from urllib.error import HTTPError, URLError @@ -66,7 +66,7 @@ def get_extensions(): # read XML file fo = open(fXML, "r") try: - tree = etree.fromstring(fo.read()) + tree = ET.fromstring(fo.read()) except Exception as e: gs.error(_("Unable to parse metadata file: %s") % e) fo.close() @@ -184,7 +184,7 @@ def find_addon_name(addons): url=url, response_format="application/xml", ) - tree = etree.fromstring(response.read()) + tree = ET.fromstring(response.read()) result = [] for addon in addons: found = False diff --git a/scripts/g.extension/g.extension.py b/scripts/g.extension/g.extension.py index 868e2a0b5da..3b0c15c0bfc 100644 --- a/scripts/g.extension/g.extension.py +++ b/scripts/g.extension/g.extension.py @@ -153,7 +153,7 @@ import zipfile import tempfile import json -import xml.etree.ElementTree as etree +import xml.etree.ElementTree as ET if sys.version_info < (3, 8): from distutils.dir_util import copy_tree @@ -172,8 +172,8 @@ # and ElementTree 1.3. from xml.parsers import expat # TODO: works for any Python? -if hasattr(etree, "ParseError"): - ETREE_EXCEPTIONS = (etree.ParseError, expat.ExpatError) +if hasattr(ET, "ParseError"): + ETREE_EXCEPTIONS = (ET.ParseError, expat.ExpatError) else: ETREE_EXCEPTIONS = expat.ExpatError @@ -571,7 +571,7 @@ def get_default_branch(full_url): def etree_fromfile(filename): """Create XML element tree from a given file name""" - return etree.fromstring(Path(filename).read_text()) + return ET.fromstring(Path(filename).read_text()) def etree_fromurl(url): @@ -588,7 +588,7 @@ def etree_fromurl(url): ), ), ) - return etree.fromstring(file_.read()) + return ET.fromstring(file_.read()) def check_progs(): @@ -1290,7 +1290,7 @@ def install_toolbox_xml(url, name): write_xml_modules(xml_file) # read XML file - tree = etree.fromstring(Path(xml_file).read_text()) + tree = ET.fromstring(Path(xml_file).read_text()) # update tree tnode = None @@ -1308,14 +1308,14 @@ def install_toolbox_xml(url, name): tnode.remove(mnode) else: # create new node for task - tnode = etree.Element("toolbox", attrib={"name": tdata["name"], "code": name}) + tnode = ET.Element("toolbox", attrib={"name": tdata["name"], "code": name}) tree.append(tnode) for cname in tdata["correlate"]: - cnode = etree.Element("correlate", attrib={"code": cname}) + cnode = ET.Element("correlate", attrib={"code": cname}) tnode.append(cnode) for tname in tdata["modules"]: - mnode = etree.Element("task", attrib={"name": tname}) + mnode = ET.Element("task", attrib={"name": tname}) tnode.append(mnode) write_xml_toolboxes(xml_file, tree) @@ -1415,7 +1415,7 @@ def install_extension_xml(edict): if tnode is None: # create new node for task - tnode = etree.Element("task", attrib={"name": name}) + tnode = ET.Element("task", attrib={"name": name}) """ dnode = etree.Element('description') dnode.text = desc @@ -1426,19 +1426,19 @@ def install_extension_xml(edict): """ # create binary - bnode = etree.Element("binary") + bnode = ET.Element("binary") # list of all installed files for this extension for file_name in edict[name]["flist"]: - fnode = etree.Element("file") + fnode = ET.Element("file") fnode.text = file_name bnode.append(fnode) tnode.append(bnode) # create modules - msnode = etree.Element("modules") + msnode = ET.Element("modules") # list of all installed modules for this extension for module_name in edict[name]["mlist"]: - mnode = etree.Element("module") + mnode = ET.Element("module") mnode.text = module_name msnode.append(mnode) tnode.append(msnode) @@ -1542,11 +1542,11 @@ def install_module_xml(mlist): if tnode is None: # create new node for task - tnode = etree.Element("task", attrib={"name": name}) - dnode = etree.Element("description") + tnode = ET.Element("task", attrib={"name": name}) + dnode = ET.Element("description") dnode.text = desc tnode.append(dnode) - knode = etree.Element("keywords") + knode = ET.Element("keywords") knode.text = (",").join(keywords) tnode.append(knode) diff --git a/scripts/g.search.modules/g.search.modules.py b/scripts/g.search.modules/g.search.modules.py index 89ea482bc5b..85247517898 100755 --- a/scripts/g.search.modules/g.search.modules.py +++ b/scripts/g.search.modules/g.search.modules.py @@ -69,7 +69,7 @@ from grass.script import core as grass from grass.exceptions import CalledModuleError -import xml.etree.ElementTree as etree +import xml.etree.ElementTree as ET COLORIZE = False @@ -201,7 +201,7 @@ def _search_module( filename = os.path.join(WXGUIDIR, "xml", "module_items.xml") menudata_file = open(filename, "r") - menudata = etree.parse(menudata_file) + menudata = ET.parse(menudata_file) menudata_file.close() items = menudata.findall("module-item") @@ -211,7 +211,7 @@ def _search_module( filename_addons = os.path.join(os.getenv("GRASS_ADDON_BASE"), "modules.xml") if os.path.isfile(filename_addons): addon_menudata_file = open(filename_addons, "r") - addon_menudata = etree.parse(addon_menudata_file) + addon_menudata = ET.parse(addon_menudata_file) addon_menudata_file.close() addon_items = addon_menudata.findall("task") items.extend(addon_items) @@ -220,7 +220,7 @@ def _search_module( filename_addons_s = os.path.join(os.getenv("GISBASE"), "modules.xml") if os.path.isfile(filename_addons_s): addon_menudata_file_s = open(filename_addons_s, "r") - addon_menudata_s = etree.parse(addon_menudata_file_s) + addon_menudata_s = ET.parse(addon_menudata_file_s) addon_menudata_file_s.close() addon_items_s = addon_menudata_s.findall("task") items.extend(addon_items_s) diff --git a/scripts/r.in.wms/wms_cap_parsers.py b/scripts/r.in.wms/wms_cap_parsers.py index 56d6e788e3a..f9b0f7095fb 100644 --- a/scripts/r.in.wms/wms_cap_parsers.py +++ b/scripts/r.in.wms/wms_cap_parsers.py @@ -21,11 +21,11 @@ from xml.etree.ElementTree import ParseError -import xml.etree.ElementTree as etree +import xml.etree.ElementTree as ET import grass.script as gs -class BaseCapabilitiesTree(etree.ElementTree): +class BaseCapabilitiesTree(ET.ElementTree): def __init__(self, cap_file): """!Initialize xml.etree.ElementTree""" is_file = False @@ -40,7 +40,7 @@ def __init__(self, cap_file): raise if is_file: try: - etree.ElementTree.__init__(self, file=cap_file) + ET.ElementTree.__init__(self, file=cap_file) except ParseError: raise ParseError(_("Unable to parse XML file")) except OSError as error: @@ -49,7 +49,7 @@ def __init__(self, cap_file): ) else: try: - etree.ElementTree.__init__(self, element=etree.fromstring(cap_file)) + ET.ElementTree.__init__(self, element=ET.fromstring(cap_file)) except ParseError: raise ParseError(_("Unable to parse XML file")) diff --git a/scripts/r.in.wms/wms_gdal_drv.py b/scripts/r.in.wms/wms_gdal_drv.py index bdbf5524ccf..6d30f5817f3 100644 --- a/scripts/r.in.wms/wms_gdal_drv.py +++ b/scripts/r.in.wms/wms_gdal_drv.py @@ -25,7 +25,7 @@ ) ) -import xml.etree.ElementTree as etree +import xml.etree.ElementTree as ET from wms_base import WMSBase, GetSRSParamVal @@ -58,65 +58,65 @@ def _createXML(self): """ self._debug("_createXML", "started") - gdal_wms = etree.Element("GDAL_WMS") - service = etree.SubElement(gdal_wms, "Service") - name = etree.Element("name") + gdal_wms = ET.Element("GDAL_WMS") + service = ET.SubElement(gdal_wms, "Service") + name = ET.Element("name") service.set("name", "WMS") - version = etree.SubElement(service, "Version") + version = ET.SubElement(service, "Version") version.text = self.params["wms_version"] - server_url = etree.SubElement(service, "ServerUrl") + server_url = ET.SubElement(service, "ServerUrl") server_url.text = self.params["url"] - srs = etree.SubElement(service, self.params["proj_name"]) + srs = ET.SubElement(service, self.params["proj_name"]) srs.text = GetSRSParamVal(self.params["srs"]) - image_format = etree.SubElement(service, "ImageFormat") + image_format = ET.SubElement(service, "ImageFormat") image_format.text = self.params["format"] - image_format = etree.SubElement(service, "Transparent") + image_format = ET.SubElement(service, "Transparent") image_format.text = self.params["transparent"] - layers = etree.SubElement(service, "Layers") + layers = ET.SubElement(service, "Layers") layers.text = self.params["layers"] - styles = etree.SubElement(service, "Styles") + styles = ET.SubElement(service, "Styles") styles.text = self.params["styles"] - data_window = etree.SubElement(gdal_wms, "DataWindow") + data_window = ET.SubElement(gdal_wms, "DataWindow") - upper_left_x = etree.SubElement(data_window, "UpperLeftX") + upper_left_x = ET.SubElement(data_window, "UpperLeftX") upper_left_x.text = str(self.bbox["minx"]) - upper_left_y = etree.SubElement(data_window, "UpperLeftY") + upper_left_y = ET.SubElement(data_window, "UpperLeftY") upper_left_y.text = str(self.bbox["maxy"]) - lower_right_x = etree.SubElement(data_window, "LowerRightX") + lower_right_x = ET.SubElement(data_window, "LowerRightX") lower_right_x.text = str(self.bbox["maxx"]) - lower_right_y = etree.SubElement(data_window, "LowerRightY") + lower_right_y = ET.SubElement(data_window, "LowerRightY") lower_right_y.text = str(self.bbox["miny"]) - size_x = etree.SubElement(data_window, "SizeX") + size_x = ET.SubElement(data_window, "SizeX") size_x.text = str(self.region["cols"]) - size_y = etree.SubElement(data_window, "SizeY") + size_y = ET.SubElement(data_window, "SizeY") size_y.text = str(self.region["rows"]) # RGB + alpha self.temp_map_bands_num = 4 - block_size_x = etree.SubElement(gdal_wms, "BandsCount") + block_size_x = ET.SubElement(gdal_wms, "BandsCount") block_size_x.text = str(self.temp_map_bands_num) - block_size_x = etree.SubElement(gdal_wms, "BlockSizeX") + block_size_x = ET.SubElement(gdal_wms, "BlockSizeX") block_size_x.text = str(self.tile_size["cols"]) - block_size_y = etree.SubElement(gdal_wms, "BlockSizeY") + block_size_y = ET.SubElement(gdal_wms, "BlockSizeY") block_size_y.text = str(self.tile_size["rows"]) if self.params["username"] and self.params["password"]: - user_password = etree.SubElement(gdal_wms, "UserPwd") + user_password = ET.SubElement(gdal_wms, "UserPwd") user_password.text = "%s:%s" % ( self.params["username"], self.params["password"], @@ -124,7 +124,7 @@ def _createXML(self): xml_file = self._tempfile() - etree.ElementTree(gdal_wms).write(xml_file) + ET.ElementTree(gdal_wms).write(xml_file) self._debug("_createXML", "finished -> %s" % xml_file) From 7ee959a8fa5083670ead00aded10709c3378d327 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 17 Aug 2024 09:59:52 -0400 Subject: [PATCH 136/471] CI(deps): Update ubuntu:22.04 Docker digest to adbb901 (#4192) --- Dockerfile | 2 +- docker/ubuntu/Dockerfile | 2 +- docker/ubuntu_wxgui/Dockerfile | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 48c57ff5d6a..23bf11ddf27 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,7 +5,7 @@ ARG GUI=without -FROM ubuntu:22.04@sha256:340d9b015b194dc6e2a13938944e0d016e57b9679963fdeb9ce021daac430221 as common_start +FROM ubuntu:22.04@sha256:adbb90115a21969d2fe6fa7f9af4253e16d45f8d4c1e930182610c4731962658 as common_start LABEL authors="Carmen Tawalika,Markus Neteler,Anika Weinmann,Stefan Blumentrath" LABEL maintainer="tawalika@mundialis.de,neteler@mundialis.de,weinmann@mundialis.de" diff --git a/docker/ubuntu/Dockerfile b/docker/ubuntu/Dockerfile index 48c57ff5d6a..23bf11ddf27 100644 --- a/docker/ubuntu/Dockerfile +++ b/docker/ubuntu/Dockerfile @@ -5,7 +5,7 @@ ARG GUI=without -FROM ubuntu:22.04@sha256:340d9b015b194dc6e2a13938944e0d016e57b9679963fdeb9ce021daac430221 as common_start +FROM ubuntu:22.04@sha256:adbb90115a21969d2fe6fa7f9af4253e16d45f8d4c1e930182610c4731962658 as common_start LABEL authors="Carmen Tawalika,Markus Neteler,Anika Weinmann,Stefan Blumentrath" LABEL maintainer="tawalika@mundialis.de,neteler@mundialis.de,weinmann@mundialis.de" diff --git a/docker/ubuntu_wxgui/Dockerfile b/docker/ubuntu_wxgui/Dockerfile index 9351de43b98..dd5f26d7ac0 100644 --- a/docker/ubuntu_wxgui/Dockerfile +++ b/docker/ubuntu_wxgui/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:22.04@sha256:340d9b015b194dc6e2a13938944e0d016e57b9679963fdeb9ce021daac430221 +FROM ubuntu:22.04@sha256:adbb90115a21969d2fe6fa7f9af4253e16d45f8d4c1e930182610c4731962658 LABEL authors="Carmen Tawalika,Markus Neteler,Anika Weinmann,Tomas Zigo" LABEL maintainer="tawalika@mundialis.de,neteler@mundialis.de,weinmann@mundialis.de" From 769032fa38ecea285f6e538302e7129884bd1fb1 Mon Sep 17 00:00:00 2001 From: Ivan Mincik Date: Mon, 19 Aug 2024 11:10:51 +0000 Subject: [PATCH 137/471] nix: fix build by using libxml2 with http support (#4196) * CI(deps): Lock file maintenance * nix: fix build by using libxml2 with http support Related nixpkgs PR: https://github.com/NixOS/nixpkgs/pull/331118 --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- flake.lock | 20 ++++++++++---------- package.nix | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/flake.lock b/flake.lock index c30f9b90775..96d1a96a3cf 100644 --- a/flake.lock +++ b/flake.lock @@ -5,11 +5,11 @@ "nixpkgs-lib": "nixpkgs-lib" }, "locked": { - "lastModified": 1719994518, - "narHash": "sha256-pQMhCCHyQGRzdfAkdJ4cIWiw+JNuWsTX7f0ZYSyz0VY=", + "lastModified": 1722555600, + "narHash": "sha256-XOQkdLafnb/p9ij77byFQjDf5m5QYl9b2REiVClC+x4=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "9227223f6d922fee3c7b190b2cc238a99527bbb7", + "rev": "8471fe90ad337a8074e957b69ca4d0089218391d", "type": "github" }, "original": { @@ -19,11 +19,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1720955038, - "narHash": "sha256-GaliJqfFwyYxReFywxAa8orCO+EnDq2NK2F+5aSc8vo=", + "lastModified": 1723891200, + "narHash": "sha256-uljX21+D/DZgb9uEFFG2dkkQbPZN+ig4Z6+UCLWFVAk=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "aa247c0c90ecf4ae7a032c54fdc21b91ca274062", + "rev": "a0d6390cb3e82062a35d0288979c45756e481f60", "type": "github" }, "original": { @@ -35,14 +35,14 @@ }, "nixpkgs-lib": { "locked": { - "lastModified": 1719876945, - "narHash": "sha256-Fm2rDDs86sHy0/1jxTOKB1118Q0O3Uc7EC0iXvXKpbI=", + "lastModified": 1722555339, + "narHash": "sha256-uFf2QeW7eAHlYXuDktm9c25OxOyCoUOQmh5SZ9amE5Q=", "type": "tarball", - "url": "https://github.com/NixOS/nixpkgs/archive/5daf0514482af3f97abaefc78a6606365c9108e2.tar.gz" + "url": "https://github.com/NixOS/nixpkgs/archive/a5d394176e64ab29c852d03346c1fc9b0b7d33eb.tar.gz" }, "original": { "type": "tarball", - "url": "https://github.com/NixOS/nixpkgs/archive/5daf0514482af3f97abaefc78a6606365c9108e2.tar.gz" + "url": "https://github.com/NixOS/nixpkgs/archive/a5d394176e64ab29c852d03346c1fc9b0b7d33eb.tar.gz" } }, "root": { diff --git a/package.nix b/package.nix index 7abbb15275d..3f1d03697c7 100644 --- a/package.nix +++ b/package.nix @@ -79,7 +79,7 @@ stdenv.mkDerivation (finalAttrs: { libpng libsvm libtiff - libxml2 + (libxml2.override { enableHttp = true; }) netcdf pdal postgresql From 13e9a1b1ce1a03cc72ec262a59d4972edc76c729 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 19 Aug 2024 12:27:56 +0000 Subject: [PATCH 138/471] CI(deps): Lock file maintenance (#4198) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 96d1a96a3cf..58a8dd324c0 100644 --- a/flake.lock +++ b/flake.lock @@ -19,11 +19,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1723891200, - "narHash": "sha256-uljX21+D/DZgb9uEFFG2dkkQbPZN+ig4Z6+UCLWFVAk=", + "lastModified": 1724015816, + "narHash": "sha256-hVESnM7Eiz93+4DeiE0a1TwMeaeph1ytRJ5QtqxYRWg=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "a0d6390cb3e82062a35d0288979c45756e481f60", + "rev": "9aa35efbea27d320d0cdc5f922f0890812affb60", "type": "github" }, "original": { From 2cf98dac35a6a3d2ab14120a68e345ff7609ba7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Mon, 19 Aug 2024 16:31:36 -0400 Subject: [PATCH 139/471] style: Fix unnecessary-dunder-call (PLC2801) (#4170) * style: Fix unnecessary-dunder-call (PLC2801) Ruff rule: https://docs.astral.sh/ruff/rules/unnecessary-dunder-call/ 12 instances fixed. Solves some reported issues by Pylint 3.2.6 through unnecessary-dunder-call / C2801 * style: Fix unnecessary-dunder-call (PLC2801) for __init__ 4 instances fixed * style: Fix unnecessary-dunder-call (PLC2801) using getattr 6 instances fixed * Ignore PLC2801 in test suite file for preserving meaning of test during the change * style: Fix unnecessary-dunder-call (PLC2801) for get and set item 3 instances fixed * style: Fix unnecessary-dunder-call (PLC2801) for repr 1 instance fixed * Ignore PLC2801 for __del__, as there seems to be some differences between both * Update pyproject.toml to remove PLC2801 exclusion * gui.wxpython.mapdisp.main: Remove next(self) from LayerList * grass.pygrass.vector: Remove next(self) from vector class (old Python 2 iterator) * Revert "grass.pygrass.vector: Remove next(self) from vector class (old Python 2 iterator)" This reverts commit f79172c1bcb69df75e51d76ca27c78d41307ed7a. * Revert "gui.wxpython.mapdisp.main: Remove next(self) from LayerList" This reverts commit 9963dd16ad0e73ea6111c86125e3ff5c61cd3da2. --- gui/wxpython/gcp/manager.py | 2 +- gui/wxpython/gmodeler/canvas.py | 4 ++-- gui/wxpython/gmodeler/model.py | 4 ++-- gui/wxpython/gmodeler/panels.py | 2 +- gui/wxpython/image2target/ii2t_manager.py | 2 +- gui/wxpython/mapdisp/main.py | 2 +- gui/wxpython/photo2image/ip2i_manager.py | 2 +- gui/wxpython/tplot/frame.py | 5 ++++- pyproject.toml | 1 - python/grass/gunittest/utils.py | 2 +- python/grass/pygrass/gis/region.py | 2 +- python/grass/pygrass/modules/interface/typedict.py | 4 ++-- python/grass/pygrass/raster/abstract.py | 4 ++-- python/grass/pygrass/raster/category.py | 12 ++++++------ python/grass/pygrass/vector/__init__.py | 2 +- python/grass/pygrass/vector/basic.py | 4 ++-- python/grass/pygrass/vector/geometry.py | 13 +++++-------- python/grass/pygrass/vector/table.py | 2 +- .../pygrass/vector/testsuite/test_geometry_attrs.py | 6 +++--- 19 files changed, 37 insertions(+), 38 deletions(-) diff --git a/gui/wxpython/gcp/manager.py b/gui/wxpython/gcp/manager.py index 98323112972..ac006a95cbf 100644 --- a/gui/wxpython/gcp/manager.py +++ b/gui/wxpython/gcp/manager.py @@ -1272,7 +1272,7 @@ def InitMapDisplay(self): # initialize column sorter self.itemDataMap = self.mapcoordlist ncols = self.list.GetColumnCount() - ColumnSorterMixin.__init__(self, ncols) + ColumnSorterMixin(self, ncols) # init to ascending sort on first click self._colSortFlag = [1] * ncols diff --git a/gui/wxpython/gmodeler/canvas.py b/gui/wxpython/gmodeler/canvas.py index ab49ab770d1..7c604ec6624 100644 --- a/gui/wxpython/gmodeler/canvas.py +++ b/gui/wxpython/gmodeler/canvas.py @@ -85,10 +85,10 @@ def RemoveShapes(self, shapes): remList, upList = self.parent.GetModel().RemoveItem(shape) shape.Select(False) diagram.RemoveShape(shape) - shape.__del__() + shape.__del__() # noqa: PLC2801, C2801 for item in remList: diagram.RemoveShape(item) - item.__del__() + item.__del__() # noqa: PLC2801, C2801 for item in upList: item.Update() diff --git a/gui/wxpython/gmodeler/model.py b/gui/wxpython/gmodeler/model.py index 4a587230fa9..b243f0beb5c 100644 --- a/gui/wxpython/gmodeler/model.py +++ b/gui/wxpython/gmodeler/model.py @@ -1577,7 +1577,7 @@ def _defineShape(self, width, height, x, y): :param width, height: dimension of the shape :param x, y: position of the shape """ - ogl.EllipseShape.__init__(self, width, height) + ogl.EllipseShape(self, width, height) if self.parent.GetCanvas(): self.SetCanvas(self.parent.GetCanvas()) @@ -1592,7 +1592,7 @@ def _defineShape(self, width, height, x, y): :param width, height: dimension of the shape :param x, y: position of the shape """ - ogl.CompositeShape.__init__(self) + ogl.CompositeShape(self) if self.parent.GetCanvas(): self.SetCanvas(self.parent.GetCanvas()) diff --git a/gui/wxpython/gmodeler/panels.py b/gui/wxpython/gmodeler/panels.py index 6d6bff45387..300220b9f7c 100644 --- a/gui/wxpython/gmodeler/panels.py +++ b/gui/wxpython/gmodeler/panels.py @@ -542,7 +542,7 @@ def GetOptData(self, dcmd, layer, params, propwin): remList, upList = self.model.RemoveItem(data, layer) for item in remList: self.canvas.diagram.RemoveShape(item) - item.__del__() + item.__del__() # noqa: PLC2801, C2801 for item in upList: item.Update() diff --git a/gui/wxpython/image2target/ii2t_manager.py b/gui/wxpython/image2target/ii2t_manager.py index 968e6c50b7d..4aec0eeba5e 100644 --- a/gui/wxpython/image2target/ii2t_manager.py +++ b/gui/wxpython/image2target/ii2t_manager.py @@ -1257,7 +1257,7 @@ def InitMapDisplay(self): # initialize column sorter self.itemDataMap = self.mapcoordlist ncols = self.list.GetColumnCount() - ColumnSorterMixin.__init__(self, ncols) + ColumnSorterMixin(self, ncols) # init to ascending sort on first click self._colSortFlag = [1] * ncols diff --git a/gui/wxpython/mapdisp/main.py b/gui/wxpython/mapdisp/main.py index 5bef25ad128..1e94d3d8ba2 100644 --- a/gui/wxpython/mapdisp/main.py +++ b/gui/wxpython/mapdisp/main.py @@ -370,7 +370,7 @@ def __next__(self): return result def next(self): - return self.__next__() + return next(self) def GetSelectedLayers(self, checkedOnly=True): # hidden and selected vs checked and selected diff --git a/gui/wxpython/photo2image/ip2i_manager.py b/gui/wxpython/photo2image/ip2i_manager.py index 042053b65e2..2a9ae19d26a 100644 --- a/gui/wxpython/photo2image/ip2i_manager.py +++ b/gui/wxpython/photo2image/ip2i_manager.py @@ -625,7 +625,7 @@ def InitMapDisplay(self): # initialize column sorter self.itemDataMap = self.mapcoordlist ncols = self.list.GetColumnCount() - ColumnSorterMixin.__init__(self, ncols) + ColumnSorterMixin(self, ncols) # init to ascending sort on first click self._colSortFlag = [1] * ncols diff --git a/gui/wxpython/tplot/frame.py b/gui/wxpython/tplot/frame.py index e15e6584808..62147f54ad6 100755 --- a/gui/wxpython/tplot/frame.py +++ b/gui/wxpython/tplot/frame.py @@ -140,7 +140,10 @@ def onClose(self, evt): if self._giface.GetMapDisplay(): self.coorval.OnClose() self.cats.OnClose() - self.__del__() + + # __del__() and del keyword seem to have differences, + # how can self.Destroy(), called after del, work otherwise + self.__del__() # noqa: PLC2801, C2801 self.Destroy() def _layout(self): diff --git a/pyproject.toml b/pyproject.toml index 930c538049b..eb866cb0e08 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -158,7 +158,6 @@ ignore = [ "PLC0415", # import-outside-top-level "PLC1901", # compare-to-empty-string "PLC2701", # import-private-name - "PLC2801", # unnecessary-dunder-call "PLE0704", # misplaced-bare-raise "PLR0904", # too-many-public-methods "PLR0911", # too-many-return-statements diff --git a/python/grass/gunittest/utils.py b/python/grass/gunittest/utils.py index 082366cacaf..c1afea3d5ad 100644 --- a/python/grass/gunittest/utils.py +++ b/python/grass/gunittest/utils.py @@ -76,7 +76,7 @@ def safe_repr(obj, short=False): try: result = repr(obj) except Exception: - result = object.__repr__(obj) + result = object.__repr__(obj) # noqa: PLC2801 if not short or len(result) < _MAX_LENGTH: return result return result[:_MAX_LENGTH] + " [truncated]..." diff --git a/python/grass/pygrass/gis/region.py b/python/grass/pygrass/gis/region.py index 0f736978026..98c75a15631 100644 --- a/python/grass/pygrass/gis/region.py +++ b/python/grass/pygrass/gis/region.py @@ -370,7 +370,7 @@ def keys(self): def items(self): """Return a list of tuple with key and value.""" - return [(k, self.__getattribute__(k)) for k in self.keys()] + return [(k, getattr(self, k)) for k in self.keys()] # ----------METHODS---------- def zoom(self, raster_name): diff --git a/python/grass/pygrass/modules/interface/typedict.py b/python/grass/pygrass/modules/interface/typedict.py index c8fdeb48a76..fb3ddb368eb 100644 --- a/python/grass/pygrass/modules/interface/typedict.py +++ b/python/grass/pygrass/modules/interface/typedict.py @@ -64,6 +64,6 @@ def __reduce__(self): def used(self): key_dict = {} for key in self: - if self.__getattr__(key): - key_dict[key] = self.__getattr__(key) + if getattr(self, key): + key_dict[key] = getattr(self, key) return key_dict diff --git a/python/grass/pygrass/raster/abstract.py b/python/grass/pygrass/raster/abstract.py index 616964d8f7b..edea42e7bdd 100644 --- a/python/grass/pygrass/raster/abstract.py +++ b/python/grass/pygrass/raster/abstract.py @@ -247,10 +247,10 @@ def keys(self): ] def items(self): - return [(k, self.__getattribute__(k)) for k in self.keys()] + return [(k, getattr(self, k)) for k in self.keys()] def __iter__(self): - return ((k, self.__getattribute__(k)) for k in self.keys()) + return ((k, getattr(self, k)) for k in self.keys()) def _repr_html_(self): return dict2html(dict(self.items()), keys=self.keys(), border="1", kdec="b") diff --git a/python/grass/pygrass/raster/category.py b/python/grass/pygrass/raster/category.py index 0a5e42093ae..00381f91130 100644 --- a/python/grass/pygrass/raster/category.py +++ b/python/grass/pygrass/raster/category.py @@ -196,13 +196,13 @@ def __del__(self): libraster.Rast_free_cats(ctypes.byref(self.c_cats)) def get_cat(self, index): - return self.__getitem__(index) + return self[index] def set_cat(self, index, value): if index is None: self.append(value) - elif index < self.__len__(): - self.__setitem__(index, value) + elif index < (len(self)): + self[index] = value else: raise TypeError("Index outside range.") @@ -221,7 +221,7 @@ def _write_cats(self): # reset only the C struct libraster.Rast_init_cats("", ctypes.byref(self.c_cats)) # write to the c struct - for cat in self.__iter__(): + for cat in iter(self): label, min_cat, max_cat = cat if max_cat is None: max_cat = min_cat @@ -273,7 +273,7 @@ def copy(self, category): self._read_cats() def ncats(self): - return self.__len__() + return len(self) def set_cats_fmt(self, fmt, m1, a1, m2, a2): """Not implemented yet. @@ -327,7 +327,7 @@ def write_rules(self, filename, sep=":"): :param str sep: the separator used to divide values and category """ cats = [] - for cat in self.__iter__(): + for cat in iter(self): if cat[-1] is None: cat = cat[:-1] cats.append(sep.join([str(i) for i in cat])) diff --git a/python/grass/pygrass/vector/__init__.py b/python/grass/pygrass/vector/__init__.py index 7869b1a29b5..6f29a167b2e 100644 --- a/python/grass/pygrass/vector/__init__.py +++ b/python/grass/pygrass/vector/__init__.py @@ -108,7 +108,7 @@ def __next__(self): @must_be_open def next(self): - return self.__next__() + return next(self) @must_be_open def rewind(self): diff --git a/python/grass/pygrass/vector/basic.py b/python/grass/pygrass/vector/basic.py index 1ce2dccedd8..079f7ac829f 100644 --- a/python/grass/pygrass/vector/basic.py +++ b/python/grass/pygrass/vector/basic.py @@ -137,7 +137,7 @@ def contains(self, point): ) def items(self): - return [(k, self.__getattribute__(k)) for k in self.keys()] + return [(k, getattr(self, k)) for k in self.keys()] def nsewtb(self, tb=True): """Return a list of values from bounding box @@ -215,7 +215,7 @@ def append(self, box): 3 """ - indx = self.__len__() + indx = len(self) libvect.Vect_boxlist_append(self.c_boxlist, indx, box.c_bbox) # def extend(self, boxlist): diff --git a/python/grass/pygrass/vector/geometry.py b/python/grass/pygrass/vector/geometry.py index bc6ad32f10a..1c5d378e6ef 100644 --- a/python/grass/pygrass/vector/geometry.py +++ b/python/grass/pygrass/vector/geometry.py @@ -958,7 +958,7 @@ def pop(self, indx): indx += self.c_points.contents.n_points if indx >= self.c_points.contents.n_points: raise IndexError("Index out of range") - pnt = self.__getitem__(indx) + pnt = self[indx] libvect.Vect_line_delete_point(self.c_points, indx) return pnt @@ -1028,7 +1028,7 @@ def remove(self, pnt): .. """ - for indx, point in enumerate(self.__iter__()): + for indx, point in enumerate(iter(self)): if pnt == point: libvect.Vect_line_delete_point(self.c_points, indx) return @@ -1086,7 +1086,7 @@ def to_list(self): .. """ - return [pnt.coords() for pnt in self.__iter__()] + return [pnt.coords() for pnt in iter(self)] def to_array(self): """Return an array of coordinates. :: @@ -1112,10 +1112,7 @@ def to_wkt_p(self): .. """ return "LINESTRING(%s)" % ", ".join( - [ - " ".join(["%f" % coord for coord in pnt.coords()]) - for pnt in self.__iter__() - ] + [" ".join(["%f" % coord for coord in pnt.coords()]) for pnt in iter(self)] ) def from_wkt(self, wkt): @@ -1592,7 +1589,7 @@ def isles_ids(self): """Return the id of isles""" return [ libvect.Vect_get_area_isle(self.c_mapinfo, self.area_id, i) - for i in range(self.__len__()) + for i in range(len(self)) ] @mapinfo_must_be_set diff --git a/python/grass/pygrass/vector/table.py b/python/grass/pygrass/vector/table.py index dec3ffbba08..4b7e99c0a83 100644 --- a/python/grass/pygrass/vector/table.py +++ b/python/grass/pygrass/vector/table.py @@ -297,7 +297,7 @@ def update_odict(self): [ "?", ] - * self.__len__() + * (len(self)) ) kv = ",".join(["%s=?" % k for k in self.odict.keys() if k != self.key]) where = "%s=?" % self.key diff --git a/python/grass/pygrass/vector/testsuite/test_geometry_attrs.py b/python/grass/pygrass/vector/testsuite/test_geometry_attrs.py index 4c17ef03d4c..356a55ec4d7 100644 --- a/python/grass/pygrass/vector/testsuite/test_geometry_attrs.py +++ b/python/grass/pygrass/vector/testsuite/test_geometry_attrs.py @@ -60,11 +60,11 @@ def test_setitem(self): newvalue = 100.0 newpairs = ("setitem_point_2", 1000.0) - self.attrs.__setitem__("name", newname) + self.attrs.__setitem__("name", newname) # noqa: PLC2801 self.assertEqual(self.attrs["name"], newname) - self.attrs.__setitem__("value", newvalue) + self.attrs.__setitem__("value", newvalue) # noqa: PLC2801 self.assertEqual(self.attrs["value"], newvalue) - self.attrs.__setitem__(("name", "value"), newpairs) + self.attrs.__setitem__(("name", "value"), newpairs) # noqa: PLC2801 self.assertEqual(self.attrs["name", "value"], newpairs) From 4d17eb509fceac7fdb4fc3943d74ff620dc6f113 Mon Sep 17 00:00:00 2001 From: Kriti Birda <164247895+kritibirda26@users.noreply.github.com> Date: Tue, 20 Aug 2024 02:15:48 +0530 Subject: [PATCH 140/471] r.object.geometry: add json support (#4105) * r.object.geometry: add json support * debug test failure in ci * fix tests --- raster/r.object.geometry/Makefile | 2 +- raster/r.object.geometry/main.c | 111 ++++++++++++++---- .../r.object.geometry/r.object.geometry.html | 71 +++++++++++ ...etry_test.py => test_r_object_geometry.py} | 67 +++++++++-- 4 files changed, 212 insertions(+), 39 deletions(-) rename raster/r.object.geometry/testsuite/{r_object_geometry_test.py => test_r_object_geometry.py} (68%) diff --git a/raster/r.object.geometry/Makefile b/raster/r.object.geometry/Makefile index aeaa235c641..e508927fd24 100644 --- a/raster/r.object.geometry/Makefile +++ b/raster/r.object.geometry/Makefile @@ -2,7 +2,7 @@ MODULE_TOPDIR = ../.. PGM = r.object.geometry -LIBES = $(RASTERLIB) $(GISLIB) +LIBES = $(RASTERLIB) $(GISLIB) $(PARSONLIB) DEPENDENCIES = $(RASTERDEP) $(GISDEP) include $(MODULE_TOPDIR)/include/Make/Module.make diff --git a/raster/r.object.geometry/main.c b/raster/r.object.geometry/main.c index f808c4b88e1..fad0cf79099 100644 --- a/raster/r.object.geometry/main.c +++ b/raster/r.object.geometry/main.c @@ -22,6 +22,9 @@ #include #include #include +#include + +enum OutputFormat { PLAIN, JSON }; /* compare two cell values * return 0 if equal, 1 if different */ @@ -42,6 +45,7 @@ int main(int argc, char *argv[]) struct Option *opt_in; struct Option *opt_out; struct Option *opt_sep; + struct Option *fmt_opt; struct Flag *flag_m; char *sep; FILE *out_fp; @@ -59,6 +63,11 @@ int main(int argc, char *argv[]) int planimetric = 0, compute_areas = 0; struct Cell_head cellhd; + enum OutputFormat format; + JSON_Array *root_array; + JSON_Object *object; + JSON_Value *root_value, *object_value; + G_gisinit(argv[0]); /* Define the different options */ @@ -82,10 +91,22 @@ int main(int argc, char *argv[]) flag_m->key = 'm'; flag_m->label = _("Use meters as units instead of cells"); + fmt_opt = G_define_standard_option(G_OPT_F_FORMAT); + fmt_opt->guisection = _("Print"); + /* parse options */ if (G_parser(argc, argv)) exit(EXIT_FAILURE); + if (strcmp(fmt_opt->answer, "json") == 0) { + format = JSON; + root_value = json_value_init_array(); + root_array = json_array(root_value); + } + else { + format = PLAIN; + } + sep = G_option_to_separator(opt_sep); in_fd = Rast_open_old(opt_in->answer, ""); @@ -294,16 +315,18 @@ int main(int argc, char *argv[]) G_free(prev_in); G_message(_("Writing output")); - /* print table */ - fprintf(out_fp, "cat%s", sep); - fprintf(out_fp, "area%s", sep); - fprintf(out_fp, "perimeter%s", sep); - fprintf(out_fp, "compact_square%s", sep); - fprintf(out_fp, "compact_circle%s", sep); - fprintf(out_fp, "fd%s", sep); - fprintf(out_fp, "mean_x%s", sep); - fprintf(out_fp, "mean_y"); - fprintf(out_fp, "\n"); + if (format == PLAIN) { + /* print table */ + fprintf(out_fp, "cat%s", sep); + fprintf(out_fp, "area%s", sep); + fprintf(out_fp, "perimeter%s", sep); + fprintf(out_fp, "compact_square%s", sep); + fprintf(out_fp, "compact_circle%s", sep); + fprintf(out_fp, "fd%s", sep); + fprintf(out_fp, "mean_x%s", sep); + fprintf(out_fp, "mean_y"); + fprintf(out_fp, "\n"); + } /* print table body */ for (i = 0; i < n_objects; i++) { @@ -312,22 +335,42 @@ int main(int argc, char *argv[]) if (obj_geos[i].area == 0) continue; - fprintf(out_fp, "%d%s", min + i, sep); - fprintf(out_fp, "%f%s", obj_geos[i].area, sep); - fprintf(out_fp, "%f%s", obj_geos[i].perimeter, sep); - fprintf(out_fp, "%f%s", - 4 * sqrt(obj_geos[i].area) / obj_geos[i].perimeter, sep); - fprintf(out_fp, "%f%s", - obj_geos[i].perimeter / (2 * sqrt(M_PI * obj_geos[i].area)), - sep); + double compact_square = + 4 * sqrt(obj_geos[i].area) / obj_geos[i].perimeter; + double compact_circle = + obj_geos[i].perimeter / (2 * sqrt(M_PI * obj_geos[i].area)); /* log 1 = 0, so avoid that by always adding 0.001 to the area: */ - fprintf(out_fp, "%f%s", - 2 * log(obj_geos[i].perimeter) / log(obj_geos[i].area + 0.001), - sep); - if (!flag_m->answer) + double fd = + 2 * log(obj_geos[i].perimeter) / log(obj_geos[i].area + 0.001); + if (!flag_m->answer) { obj_geos[i].num = obj_geos[i].area; - fprintf(out_fp, "%f%s", obj_geos[i].x / obj_geos[i].num, sep); - fprintf(out_fp, "%f", obj_geos[i].y / obj_geos[i].num); + } + double mean_x = obj_geos[i].x / obj_geos[i].num; + double mean_y = obj_geos[i].y / obj_geos[i].num; + switch (format) { + case PLAIN: + fprintf(out_fp, "%d%s", min + i, sep); + fprintf(out_fp, "%f%s", obj_geos[i].area, sep); + fprintf(out_fp, "%f%s", obj_geos[i].perimeter, sep); + fprintf(out_fp, "%f%s", compact_square, sep); + fprintf(out_fp, "%f%s", compact_circle, sep); + fprintf(out_fp, "%f%s", fd, sep); + fprintf(out_fp, "%f%s", mean_x, sep); + fprintf(out_fp, "%f", mean_y); + break; + case JSON: + object_value = json_value_init_object(); + object = json_object(object_value); + json_object_set_number(object, "category", min + i); + json_object_set_number(object, "area", obj_geos[i].area); + json_object_set_number(object, "perimeter", obj_geos[i].perimeter); + json_object_set_number(object, "compact_square", compact_square); + json_object_set_number(object, "compact_circle", compact_circle); + json_object_set_number(object, "fd", fd); + json_object_set_number(object, "mean_x", mean_x); + json_object_set_number(object, "mean_y", mean_y); + break; + } /* object id: i + min */ /* TODO */ @@ -342,8 +385,26 @@ int main(int argc, char *argv[]) /* variance of X and Y to approximate bounding ellipsoid */ - fprintf(out_fp, "\n"); + switch (format) { + case PLAIN: + fprintf(out_fp, "\n"); + break; + case JSON: + json_array_append_value(root_array, object_value); + break; + } } + + if (format == JSON) { + char *serialized_string = json_serialize_to_string_pretty(root_value); + if (serialized_string == NULL) { + G_fatal_error(_("Failed to initialize pretty JSON string.")); + } + puts(serialized_string); + json_free_serialized_string(serialized_string); + json_value_free(root_value); + } + if (out_fp != stdout) fclose(out_fp); diff --git a/raster/r.object.geometry/r.object.geometry.html b/raster/r.object.geometry/r.object.geometry.html index 722c6c78801..bdca7566868 100644 --- a/raster/r.object.geometry/r.object.geometry.html +++ b/raster/r.object.geometry/r.object.geometry.html @@ -38,6 +38,77 @@

EXAMPLE

r.object.geometry input=soilsID output=soils_geom.txt +The format=json option can be used to change the output format to JSON: + +
+r.object.geometry input=zipcodes format=json
+
+ +
+[
+    {
+        "category": 1,
+        "area": 106,
+        "perimeter": 62,
+        "compact_circle": 1.6987670351864215,
+        "compact_square": 0.66423420264432265,
+        "fd": 1.7699924681225903,
+        "mean_x": 631382.07547169807,
+        "mean_y": 222764.15094339623
+    },
+    {
+        "category": 2,
+        "area": 57,
+        "perimeter": 36,
+        "compact_circle": 1.3451172460704992,
+        "compact_square": 0.83887049280786108,
+        "fd": 1.772672742164326,
+        "mean_x": 643460.52631578944,
+        "mean_y": 217232.45614035087
+    },
+    {
+        "category": 3,
+        "area": 10,
+        "perimeter": 16,
+        "compact_circle": 1.4272992929222168,
+        "compact_square": 0.79056941504209488,
+        "fd": 2.4081353865496951,
+        "mean_x": 631300,
+        "mean_y": 215450
+    },
+    {
+        "category": 4,
+        "area": 63,
+        "perimeter": 60,
+        "compact_circle": 2.1324361862292305,
+        "compact_square": 0.52915026221291817,
+        "fd": 1.9764401337147652,
+        "mean_x": 642345.23809523811,
+        "mean_y": 226599.20634920636
+    },
+    {
+        "category": 5,
+        "area": 491,
+        "perimeter": 156,
+        "compact_circle": 1.9859985189304281,
+        "compact_square": 0.56816717451693177,
+        "fd": 1.6299200778082998,
+        "mean_x": 637912.93279022397,
+        "mean_y": 220636.96537678209
+    },
+    {
+        "category": 6,
+        "area": 83,
+        "perimeter": 60,
+        "compact_circle": 1.8578355639603314,
+        "compact_square": 0.60736223860961991,
+        "fd": 1.8531256328449071,
+        "mean_x": 635846.38554216863,
+        "mean_y": 227219.8795180723
+    }
+]
+
+

SEE ALSO

diff --git a/raster/r.object.geometry/testsuite/r_object_geometry_test.py b/raster/r.object.geometry/testsuite/test_r_object_geometry.py similarity index 68% rename from raster/r.object.geometry/testsuite/r_object_geometry_test.py rename to raster/r.object.geometry/testsuite/test_r_object_geometry.py index e6df4d7570f..419df4d780b 100644 --- a/raster/r.object.geometry/testsuite/r_object_geometry_test.py +++ b/raster/r.object.geometry/testsuite/test_r_object_geometry.py @@ -4,9 +4,15 @@ module. """ +import json import os +from sys import stderr + from grass.gunittest.case import TestCase from grass.gunittest.main import test +from grass.gunittest.gmodules import call_module + +from grass.gunittest.gmodules import SimpleModule testraster1 = """\ north: 250000 @@ -30,31 +36,25 @@ class TestObjectGeometryPixel(TestCase): test_objects1 = "test_objects1" output_file_pixel = "output_file_pixel.csv" - @classmethod - def setUpClass(cls): + def setUp(self): """Imports test raster(s), ensures expected computational region and setup""" - cls.runModule( + self.runModule( "r.in.ascii", input="-", type="CELL", stdin_=testraster1, - output=cls.test_objects1, + output=self.test_objects1, ) - cls.use_temp_region() - cls.runModule("g.region", raster=cls.test_objects1) - - @classmethod - def tearDownClass(cls): - """Remove the temporary region""" - cls.del_temp_region() + self.use_temp_region() + self.runModule("g.region", raster=self.test_objects1) def tearDown(self): - """Remove the outputs created from the object geometry module - + """Remove the outputs created from the object geometry module and the temporary region This is executed after each test run. """ if os.path.isfile(self.output_file_pixel): os.remove(self.output_file_pixel) + self.del_temp_region() self.runModule("g.remove", flags="f", type="raster", name=self.test_objects1) def test_object_geometry_pixel(self): @@ -72,6 +72,47 @@ def test_object_geometry_pixel(self): msg="Output file is not equal to reference file", ) + def test_object_geometry_json(self): + """Test json format output""" + reference = [ + { + "category": 1, + "area": 4, + "perimeter": 8, + "compact_circle": 1.1283791670955126, + "compact_square": 1, + "fd": 2.999459154496928, + "mean_x": 625000, + "mean_y": 237500, + }, + { + "category": 2, + "area": 8, + "perimeter": 12, + "compact_circle": 1.1968268412042982, + "compact_square": 0.94280904158206347, + "fd": 2.3898313512153728, + "mean_x": 655000, + "mean_y": 225000, + }, + { + "category": 3, + "area": 4, + "perimeter": 8, + "compact_circle": 1.1283791670955126, + "compact_square": 1, + "fd": 2.999459154496928, + "mean_x": 625000, + "mean_y": 212500, + }, + ] + module = SimpleModule( + "r.object.geometry", input=self.test_objects1, format="json" + ) + self.runModule(module) + data = json.loads(module.outputs.stdout) + self.assertCountEqual(reference, data) + class TestObjectGeometryMeter(TestCase): """Test case for object geometry module""" From 309b417b1a528aa8ff03ab7ad9a47aa96ec4d66d Mon Sep 17 00:00:00 2001 From: Kriti Birda <164247895+kritibirda26@users.noreply.github.com> Date: Tue, 20 Aug 2024 03:48:07 +0530 Subject: [PATCH 141/471] db.describe: add JSON support (#4021) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * db.describe: add json support * initialize method args to NULL * Apply suggestions from code review Co-authored-by: Corey White * update documentation --------- Co-authored-by: Corey White Co-authored-by: Edouard Choinière <27212526+echoix@users.noreply.github.com> --- db/db.describe/Makefile | 2 +- db/db.describe/db.describe.html | 226 ++++++++++++++++++++ db/db.describe/local_proto.h | 11 +- db/db.describe/main.c | 88 +++++++- db/db.describe/printtab.c | 157 ++++++++++---- db/db.describe/testsuite/test_dbdescribe.py | 213 ++++++++++++++++++ 6 files changed, 646 insertions(+), 51 deletions(-) diff --git a/db/db.describe/Makefile b/db/db.describe/Makefile index ce8ce0afd16..ca6caf23313 100644 --- a/db/db.describe/Makefile +++ b/db/db.describe/Makefile @@ -1,7 +1,7 @@ MODULE_TOPDIR = ../.. -LIBES = $(DBMILIB) $(GISLIB) +LIBES = $(DBMILIB) $(GISLIB) $(PARSONLIB) DEPENDENCIES = $(DBMIDEP) $(GISDEP) PGM = db.describe diff --git a/db/db.describe/db.describe.html b/db/db.describe/db.describe.html index 2fe979d74ce..4be0245f35c 100644 --- a/db/db.describe/db.describe.html +++ b/db/db.describe/db.describe.html @@ -61,6 +61,232 @@

DBF example

[...] +

JSON Output

+
+db.describe table=hospitals format=json
+
+ +
+{
+    "table": "hospitals",
+    "description": "",
+    "insert": null,
+    "delete": null,
+    "ncols": 16,
+    "nrows": 160,
+    "columns": [
+        {
+            "position": 1,
+            "column": "cat",
+            "description": "",
+            "type": "INTEGER",
+            "length": 20,
+            "scale": 0,
+            "precision": 0,
+            "default": null,
+            "nullok": true,
+            "select": null,
+            "update": null
+        },
+        {
+            "position": 2,
+            "column": "OBJECTID",
+            "description": "",
+            "type": "INTEGER",
+            "length": 20,
+            "scale": 0,
+            "precision": 0,
+            "default": null,
+            "nullok": true,
+            "select": null,
+            "update": null
+        },
+        {
+            "position": 3,
+            "column": "AREA",
+            "description": "",
+            "type": "DOUBLE PRECISION",
+            "length": 20,
+            "scale": 0,
+            "precision": 0,
+            "default": null,
+            "nullok": true,
+            "select": null,
+            "update": null
+        },
+        {
+            "position": 4,
+            "column": "PERIMETER",
+            "description": "",
+            "type": "DOUBLE PRECISION",
+            "length": 20,
+            "scale": 0,
+            "precision": 0,
+            "default": null,
+            "nullok": true,
+            "select": null,
+            "update": null
+        },
+        {
+            "position": 5,
+            "column": "HLS_",
+            "description": "",
+            "type": "INTEGER",
+            "length": 20,
+            "scale": 0,
+            "precision": 0,
+            "default": null,
+            "nullok": true,
+            "select": null,
+            "update": null
+        },
+        {
+            "position": 6,
+            "column": "HLS_ID",
+            "description": "",
+            "type": "INTEGER",
+            "length": 20,
+            "scale": 0,
+            "precision": 0,
+            "default": null,
+            "nullok": true,
+            "select": null,
+            "update": null
+        },
+        {
+            "position": 7,
+            "column": "NAME",
+            "description": "",
+            "type": "CHARACTER",
+            "length": 45,
+            "scale": 0,
+            "precision": 0,
+            "default": null,
+            "nullok": true,
+            "select": null,
+            "update": null
+        },
+        {
+            "position": 8,
+            "column": "ADDRESS",
+            "description": "",
+            "type": "CHARACTER",
+            "length": 35,
+            "scale": 0,
+            "precision": 0,
+            "default": null,
+            "nullok": true,
+            "select": null,
+            "update": null
+        },
+        {
+            "position": 9,
+            "column": "CITY",
+            "description": "",
+            "type": "CHARACTER",
+            "length": 16,
+            "scale": 0,
+            "precision": 0,
+            "default": null,
+            "nullok": true,
+            "select": null,
+            "update": null
+        },
+        {
+            "position": 10,
+            "column": "ZIP",
+            "description": "",
+            "type": "CHARACTER",
+            "length": 5,
+            "scale": 0,
+            "precision": 0,
+            "default": null,
+            "nullok": true,
+            "select": null,
+            "update": null
+        },
+        {
+            "position": 11,
+            "column": "COUNTY",
+            "description": "",
+            "type": "CHARACTER",
+            "length": 12,
+            "scale": 0,
+            "precision": 0,
+            "default": null,
+            "nullok": true,
+            "select": null,
+            "update": null
+        },
+        {
+            "position": 12,
+            "column": "PHONE",
+            "description": "",
+            "type": "CHARACTER",
+            "length": 14,
+            "scale": 0,
+            "precision": 0,
+            "default": null,
+            "nullok": true,
+            "select": null,
+            "update": null
+        },
+        {
+            "position": 13,
+            "column": "CANCER",
+            "description": "",
+            "type": "CHARACTER",
+            "length": 4,
+            "scale": 0,
+            "precision": 0,
+            "default": null,
+            "nullok": true,
+            "select": null,
+            "update": null
+        },
+        {
+            "position": 14,
+            "column": "POLYGONID",
+            "description": "",
+            "type": "INTEGER",
+            "length": 20,
+            "scale": 0,
+            "precision": 0,
+            "default": null,
+            "nullok": true,
+            "select": null,
+            "update": null
+        },
+        {
+            "position": 15,
+            "column": "SCALE",
+            "description": "",
+            "type": "DOUBLE PRECISION",
+            "length": 20,
+            "scale": 0,
+            "precision": 0,
+            "default": null,
+            "nullok": true,
+            "select": null,
+            "update": null
+        },
+        {
+            "position": 16,
+            "column": "ANGLE",
+            "description": "",
+            "type": "DOUBLE PRECISION",
+            "length": 20,
+            "scale": 0,
+            "precision": 0,
+            "default": null,
+            "nullok": true,
+            "select": null,
+            "update": null
+        }
+    ]
+}
+
+

SEE ALSO

diff --git a/db/db.describe/local_proto.h b/db/db.describe/local_proto.h index af669ae46f1..6f7c4b9e682 100644 --- a/db/db.describe/local_proto.h +++ b/db/db.describe/local_proto.h @@ -1,8 +1,13 @@ #ifndef __LOCAL_PROTO_H__ #define __LOCAL_PROTO_H__ -int print_priv(char *, int); -int print_column_definition(dbColumn *); -int print_table_definition(dbDriver *, dbTable *); +#include + +enum OutputFormat { PLAIN, JSON }; + +int print_priv(char *, int, enum OutputFormat, JSON_Object *); +int print_column_definition(dbColumn *, int, enum OutputFormat, JSON_Array *); +int print_table_definition(dbDriver *, dbTable *, enum OutputFormat, + JSON_Object *, JSON_Array *); #endif /* __LOCAL_PROTO_H__ */ diff --git a/db/db.describe/main.c b/db/db.describe/main.c index b2e6398b447..e32d1572a1a 100644 --- a/db/db.describe/main.c +++ b/db/db.describe/main.c @@ -19,11 +19,13 @@ #include #include #include +#include #include "local_proto.h" struct { char *driver, *database, *table; int printcolnames; + enum OutputFormat format; } parms; /* function prototypes */ @@ -40,7 +42,25 @@ int main(int argc, char **argv) char buf[1024]; dbString stmt; + JSON_Object *root_object, *col_object; + JSON_Value *root_value, *cols_value, *col_value; + JSON_Array *cols_array = NULL; + parse_command_line(argc, argv); + + if (parms.format == JSON) { + root_value = json_value_init_object(); + if (root_value == NULL) { + G_fatal_error(_("Failed to initialize JSON array. Out of memory?")); + } + root_object = json_object(root_value); + cols_value = json_value_init_array(); + if (cols_value == NULL) { + G_fatal_error(_("Failed to initialize JSON array. Out of memory?")); + } + cols_array = json_array(cols_value); + } + if (!db_table_exists(parms.driver, parms.database, parms.table)) { G_warning(_("Table <%s> not found in database <%s> using driver <%s>"), parms.table, parms.database, parms.driver); @@ -63,7 +83,8 @@ int main(int argc, char **argv) db_get_string(&table_name)); if (!parms.printcolnames) - print_table_definition(driver, table); + print_table_definition(driver, table, parms.format, root_object, + cols_array); else { ncols = db_get_table_number_of_columns(table); @@ -71,17 +92,57 @@ int main(int argc, char **argv) sprintf(buf, "select * from %s", db_get_table_name(table)); db_set_string(&stmt, buf); nrows = db_get_table_number_of_rows(driver, &stmt); - fprintf(stdout, "ncols: %d\n", ncols); - fprintf(stdout, "nrows: %d\n", nrows); + + switch (parms.format) { + case PLAIN: + fprintf(stdout, "ncols: %d\n", ncols); + fprintf(stdout, "nrows: %d\n", nrows); + break; + case JSON: + json_object_set_number(root_object, "ncols", ncols); + json_object_set_number(root_object, "nrows", nrows); + break; + } + for (col = 0; col < ncols; col++) { column = db_get_table_column(table, col); - fprintf(stdout, "Column %d: %s:%s:%d\n", (col + 1), - db_get_column_name(column), - db_sqltype_name(db_get_column_sqltype(column)), - db_get_column_length(column)); + + switch (parms.format) { + case PLAIN: + fprintf(stdout, "Column %d: %s:%s:%d\n", (col + 1), + db_get_column_name(column), + db_sqltype_name(db_get_column_sqltype(column)), + db_get_column_length(column)); + break; + case JSON: + col_value = json_value_init_object(); + col_object = json_object(col_value); + json_object_set_number(col_object, "position", col + 1); + json_object_set_string(col_object, "name", + db_get_column_name(column)); + json_object_set_string( + col_object, "type", + db_sqltype_name(db_get_column_sqltype(column))); + json_object_set_number(col_object, "length", + db_get_column_length(column)); + json_array_append_value(cols_array, col_value); + break; + } } } + if (parms.format == JSON) { + json_object_set_value(root_object, "columns", cols_value); + char *serialized_string = NULL; + serialized_string = json_serialize_to_string_pretty(root_value); + if (serialized_string == NULL) { + G_fatal_error(_("Failed to initialize pretty JSON string.")); + } + puts(serialized_string); + json_free_serialized_string(serialized_string); + json_value_free(root_value); + } + db_close_database(driver); db_shutdown_driver(driver); @@ -90,7 +151,7 @@ int main(int argc, char **argv) static void parse_command_line(int argc, char **argv) { - struct Option *driver, *database, *table; + struct Option *driver, *database, *table, *format_opt; struct Flag *cols; struct GModule *module; const char *drv, *db; @@ -115,9 +176,13 @@ static void parse_command_line(int argc, char **argv) if ((db = db_get_default_database_name())) database->answer = (char *)db; + format_opt = G_define_standard_option(G_OPT_F_FORMAT); + format_opt->guisection = _("Print"); + /* Set description */ module = G_define_module(); G_add_keyword(_("database")); + G_add_keyword(_("json")); G_add_keyword(_("attribute table")); module->description = _("Describes a table in detail."); @@ -128,4 +193,11 @@ static void parse_command_line(int argc, char **argv) parms.database = database->answer; parms.table = table->answer; parms.printcolnames = cols->answer; + + if (strcmp(format_opt->answer, "json") == 0) { + parms.format = JSON; + } + else { + parms.format = PLAIN; + } } diff --git a/db/db.describe/printtab.c b/db/db.describe/printtab.c index 1412f23adcf..59da4194fe7 100644 --- a/db/db.describe/printtab.c +++ b/db/db.describe/printtab.c @@ -1,19 +1,31 @@ #include #include #include "local_proto.h" -#include +#include -int print_table_definition(dbDriver *driver, dbTable *table) +int print_table_definition(dbDriver *driver, dbTable *table, + enum OutputFormat format, JSON_Object *root_object, + JSON_Array *cols_array) { int ncols, col, nrows; dbColumn *column; char buf[1024]; dbString stmt; - fprintf(stdout, "table:%s\n", db_get_table_name(table)); - fprintf(stdout, "description:%s\n", db_get_table_description(table)); - print_priv("insert", db_get_table_insert_priv(table)); - print_priv("delete", db_get_table_delete_priv(table)); + switch (format) { + case PLAIN: + fprintf(stdout, "table:%s\n", db_get_table_name(table)); + fprintf(stdout, "description:%s\n", db_get_table_description(table)); + break; + case JSON: + json_object_set_string(root_object, "table", db_get_table_name(table)); + json_object_set_string(root_object, "description", + db_get_table_description(table)); + break; + } + + print_priv("insert", db_get_table_insert_priv(table), format, root_object); + print_priv("delete", db_get_table_delete_priv(table), format, root_object); ncols = db_get_table_number_of_columns(table); @@ -21,58 +33,125 @@ int print_table_definition(dbDriver *driver, dbTable *table) sprintf(buf, "select * from %s", db_get_table_name(table)); db_set_string(&stmt, buf); nrows = db_get_table_number_of_rows(driver, &stmt); - fprintf(stdout, "ncols:%d\n", ncols); - fprintf(stdout, "nrows:%d\n", nrows); + + switch (format) { + case PLAIN: + fprintf(stdout, "ncols:%d\n", ncols); + fprintf(stdout, "nrows:%d\n", nrows); + break; + case JSON: + json_object_set_number(root_object, "ncols", ncols); + json_object_set_number(root_object, "nrows", nrows); + break; + } + for (col = 0; col < ncols; col++) { column = db_get_table_column(table, col); - fprintf(stdout, "\n"); - print_column_definition(column); + print_column_definition(column, col + 1, format, cols_array); } return 0; } -int print_column_definition(dbColumn *column) +int print_column_definition(dbColumn *column, int position, + enum OutputFormat format, JSON_Array *cols_array) { + JSON_Object *col_object = NULL; + JSON_Value *col_value = NULL; + dbString value_string; - fprintf(stdout, "column:%s\n", db_get_column_name(column)); - fprintf(stdout, "description:%s\n", db_get_column_description(column)); - fprintf(stdout, "type:%s\n", - db_sqltype_name(db_get_column_sqltype(column))); - fprintf(stdout, "len:%d\n", db_get_column_length(column)); - fprintf(stdout, "scale:%d\n", db_get_column_scale(column)); - fprintf(stdout, "precision:%d\n", db_get_column_precision(column)); - fprintf(stdout, "default:"); - if (db_test_column_has_default_value(column)) { - db_init_string(&value_string); - db_convert_column_default_value_to_string(column, &value_string); - fprintf(stdout, "%s", db_get_string(&value_string)); + switch (format) { + case PLAIN: + fprintf(stdout, "\n"); + fprintf(stdout, "column:%s\n", db_get_column_name(column)); + fprintf(stdout, "description:%s\n", db_get_column_description(column)); + fprintf(stdout, "type:%s\n", + db_sqltype_name(db_get_column_sqltype(column))); + fprintf(stdout, "len:%d\n", db_get_column_length(column)); + fprintf(stdout, "scale:%d\n", db_get_column_scale(column)); + fprintf(stdout, "precision:%d\n", db_get_column_precision(column)); + fprintf(stdout, "default:"); + if (db_test_column_has_default_value(column)) { + db_init_string(&value_string); + db_convert_column_default_value_to_string(column, &value_string); + fprintf(stdout, "%s", db_get_string(&value_string)); + } + fprintf(stdout, "\n"); + fprintf(stdout, "nullok:%s\n", + db_test_column_null_allowed(column) ? "yes" : "no"); + break; + case JSON: + col_value = json_value_init_object(); + col_object = json_object(col_value); + json_object_set_number(col_object, "position", position); + json_object_set_string(col_object, "column", + db_get_column_name(column)); + json_object_set_string(col_object, "description", + db_get_column_description(column)); + json_object_set_string(col_object, "type", + db_sqltype_name(db_get_column_sqltype(column))); + json_object_set_number(col_object, "length", + db_get_column_length(column)); + json_object_set_number(col_object, "scale", + db_get_column_scale(column)); + json_object_set_number(col_object, "precision", + db_get_column_precision(column)); + if (db_test_column_has_default_value(column)) { + db_init_string(&value_string); + db_convert_column_default_value_to_string(column, &value_string); + json_object_set_string(col_object, "default", + db_get_string(&value_string)); + } + else { + json_object_set_null(col_object, "default"); + } + json_object_set_boolean(col_object, "nullok", + db_test_column_null_allowed(column)); + break; + } + print_priv("select", db_get_column_select_priv(column), format, col_object); + print_priv("update", db_get_column_update_priv(column), format, col_object); + if (format == JSON) { + json_array_append_value(cols_array, col_value); } - fprintf(stdout, "\n"); - fprintf(stdout, "nullok:%s\n", - db_test_column_null_allowed(column) ? "yes" : "no"); - print_priv("select", db_get_column_select_priv(column)); - print_priv("update", db_get_column_update_priv(column)); return 0; } -int print_priv(char *label, int priv) +int print_priv(char *label, int priv, enum OutputFormat format, + JSON_Object *root_object) { - fprintf(stdout, "%s:", label); - switch (priv) { - case DB_GRANTED: - fprintf(stdout, "yes"); - break; - case DB_NOT_GRANTED: - fprintf(stdout, "no"); + switch (format) { + case PLAIN: + fprintf(stdout, "%s:", label); + switch (priv) { + case DB_GRANTED: + fprintf(stdout, "yes"); + break; + case DB_NOT_GRANTED: + fprintf(stdout, "no"); + break; + default: + fprintf(stdout, "?"); + break; + } + fprintf(stdout, "\n"); break; - default: - fprintf(stdout, "?"); + case JSON: + switch (priv) { + case DB_GRANTED: + json_object_set_boolean(root_object, label, 1); + break; + case DB_NOT_GRANTED: + json_object_set_boolean(root_object, label, 0); + break; + default: + json_object_set_null(root_object, label); + break; + } break; } - fprintf(stdout, "\n"); return 0; } diff --git a/db/db.describe/testsuite/test_dbdescribe.py b/db/db.describe/testsuite/test_dbdescribe.py index 1d6cc82aca5..f48debf0388 100644 --- a/db/db.describe/testsuite/test_dbdescribe.py +++ b/db/db.describe/testsuite/test_dbdescribe.py @@ -5,6 +5,7 @@ @author: lucadelu """ +import json from grass.gunittest.case import TestCase from grass.gunittest.main import test @@ -166,6 +167,202 @@ Column 12: SHAPE_Area:DOUBLE PRECISION:20 """ +output_json = { + "table": "zipcodes", + "description": "", + "insert": None, + "delete": None, + "ncols": 12, + "nrows": 44, + "columns": [ + { + "column": "cat", + "description": "", + "type": "INTEGER", + "length": 20, + "scale": 0, + "position": 1, + "precision": 0, + "default": None, + "nullok": True, + "select": None, + "update": None, + }, + { + "column": "OBJECTID", + "description": "", + "type": "INTEGER", + "length": 20, + "scale": 0, + "position": 2, + "precision": 0, + "default": None, + "nullok": True, + "select": None, + "update": None, + }, + { + "column": "WAKE_ZIPCO", + "description": "", + "type": "DOUBLE PRECISION", + "length": 20, + "scale": 0, + "position": 3, + "precision": 0, + "default": None, + "nullok": True, + "select": None, + "update": None, + }, + { + "column": "PERIMETER", + "description": "", + "type": "DOUBLE PRECISION", + "length": 20, + "scale": 0, + "position": 4, + "precision": 0, + "default": None, + "nullok": True, + "select": None, + "update": None, + }, + { + "column": "ZIPCODE_", + "description": "", + "type": "DOUBLE PRECISION", + "length": 20, + "scale": 0, + "position": 5, + "precision": 0, + "default": None, + "nullok": True, + "select": None, + "update": None, + }, + { + "column": "ZIPCODE_ID", + "description": "", + "type": "DOUBLE PRECISION", + "length": 20, + "scale": 0, + "position": 6, + "precision": 0, + "default": None, + "nullok": True, + "select": None, + "update": None, + }, + { + "column": "ZIPNAME", + "description": "", + "type": "CHARACTER", + "length": 15, + "scale": 0, + "position": 7, + "precision": 0, + "default": None, + "nullok": True, + "select": None, + "update": None, + }, + { + "column": "ZIPNUM", + "description": "", + "type": "DOUBLE PRECISION", + "length": 20, + "scale": 0, + "position": 8, + "precision": 0, + "default": None, + "nullok": True, + "select": None, + "update": None, + }, + { + "column": "ZIPCODE", + "description": "", + "type": "CHARACTER", + "length": 30, + "scale": 0, + "position": 9, + "precision": 0, + "default": None, + "nullok": True, + "select": None, + "update": None, + }, + { + "column": "NAME", + "description": "", + "type": "CHARACTER", + "length": 30, + "scale": 0, + "position": 10, + "precision": 0, + "default": None, + "nullok": True, + "select": None, + "update": None, + }, + { + "column": "SHAPE_Leng", + "description": "", + "type": "DOUBLE PRECISION", + "length": 20, + "scale": 0, + "position": 11, + "precision": 0, + "default": None, + "nullok": True, + "select": None, + "update": None, + }, + { + "column": "SHAPE_Area", + "description": "", + "type": "DOUBLE PRECISION", + "length": 20, + "scale": 0, + "position": 12, + "precision": 0, + "default": None, + "nullok": True, + "select": None, + "update": None, + }, + ], +} + +outcol_json = { + "ncols": 12, + "nrows": 44, + "columns": [ + {"position": 1, "name": "cat", "type": "INTEGER", "length": 20}, + {"position": 2, "name": "OBJECTID", "type": "INTEGER", "length": 20}, + {"position": 3, "name": "WAKE_ZIPCO", "type": "DOUBLE PRECISION", "length": 20}, + {"position": 4, "name": "PERIMETER", "type": "DOUBLE PRECISION", "length": 20}, + {"position": 5, "name": "ZIPCODE_", "type": "DOUBLE PRECISION", "length": 20}, + {"position": 6, "name": "ZIPCODE_ID", "type": "DOUBLE PRECISION", "length": 20}, + {"position": 7, "name": "ZIPNAME", "type": "CHARACTER", "length": 15}, + {"position": 8, "name": "ZIPNUM", "type": "DOUBLE PRECISION", "length": 20}, + {"position": 9, "name": "ZIPCODE", "type": "CHARACTER", "length": 30}, + {"position": 10, "name": "NAME", "type": "CHARACTER", "length": 30}, + { + "position": 11, + "name": "SHAPE_Leng", + "type": "DOUBLE PRECISION", + "length": 20, + }, + { + "position": 12, + "name": "SHAPE_Area", + "type": "DOUBLE PRECISION", + "length": 20, + }, + ], +} + class TestDbCopy(TestCase): invect = "zipcodes" @@ -185,6 +382,22 @@ def test_columns(self): ) self.assertEqual(first=cols, second=outcol) + def test_describe_json(self): + cols = read_command( + "db.describe", table=self.invect, database=self.mapset, format="json" + ) + self.assertEqual(output_json, json.loads(cols)) + + def test_columns_json(self): + cols = read_command( + "db.describe", + table=self.invect, + flags="c", + database=self.mapset, + format="json", + ) + self.assertEqual(outcol_json, json.loads(cols)) + if __name__ == "__main__": test() From a4257a13182275acb50609c43207970d48250da6 Mon Sep 17 00:00:00 2001 From: Markus Neteler Date: Tue, 20 Aug 2024 00:25:31 +0200 Subject: [PATCH 142/471] g.download.location: Print target path in error message (#4155) * g.download.location: print target path in error message To be less obscure in case the target location directory already exists: ``` ERROR: Location named already exists, download canceled ``` this PR improves the error message to include the path: ``` g.download.location url=https://grass.osgeo.org/sampledata/north_carolina/nc_spm_full_v2alpha2.tar.gz path=$HOME ERROR: Location named already exists in , download canceled ``` This is esp. relevant for scripted usage when the command isn't invoked directly by the user. In addition, section of **EXAMPLES** added. * fix message format Co-authored-by: Vaclav Petras * database -> project Co-authored-by: Veronica Andreo --------- Co-authored-by: Vaclav Petras Co-authored-by: Veronica Andreo --- .../g.download.location.html | 21 +++++++++++++++++++ .../g.download.location.py | 4 +++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/scripts/g.download.location/g.download.location.html b/scripts/g.download.location/g.download.location.html index 3787e5e0fb5..6900acc4388 100644 --- a/scripts/g.download.location/g.download.location.html +++ b/scripts/g.download.location/g.download.location.html @@ -11,6 +11,27 @@

DESCRIPTION

The first directory which is a project is used. Other projects or any other files are ignored. +

EXAMPLES

+ +

Download the full GRASS GIS sample project within a running session

+ +Download and unpack the full North Carolina sample project into the user's +HOME directory: + +
+g.download.location url=https://grass.osgeo.org/sampledata/north_carolina/nc_spm_full_v2alpha2.tar.gz path=$HOME
+
+ +

Download the full GRASS GIS sample project in a temporary session

+ +In a temporary session, download and unpack the full North Carolina sample project +into the user's HOME directory: + +
+grass --tmp-location XY --exec g.download.location url=https://grass.osgeo.org/sampledata/north_carolina/nc_spm_full_v2alpha2.tar.gz path=$HOME
+
+ +

SEE ALSO

diff --git a/scripts/g.download.location/g.download.location.py b/scripts/g.download.location/g.download.location.py index a04012aedca..bca79b12492 100644 --- a/scripts/g.download.location/g.download.location.py +++ b/scripts/g.download.location/g.download.location.py @@ -102,7 +102,9 @@ def main(options, unused_flags): if destination.exists(): gs.fatal( - _("Location named <{}> already exists, download canceled").format(name) + _( + "Location named <{name}> already exists in <{directory}>, download canceled" + ).format(name=name, directory=database) ) gs.message(_("Downloading and extracting...")) From fdfec39c0301a3dcc5eddbd6f349e79017486744 Mon Sep 17 00:00:00 2001 From: Anna Petrasova Date: Tue, 20 Aug 2024 13:10:33 -0400 Subject: [PATCH 143/471] gui: replace python imp library with importlib for python 3.12 (#4201) --- gui/wxpython/core/gconsole.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/gui/wxpython/core/gconsole.py b/gui/wxpython/core/gconsole.py index 671e71d1548..bad4aac29cd 100644 --- a/gui/wxpython/core/gconsole.py +++ b/gui/wxpython/core/gconsole.py @@ -585,8 +585,23 @@ def RunCmd( if len(command) == 1: if command[0].startswith("g.gui."): - import imp import inspect + import importlib.util + import importlib.machinery + + def load_source(modname, filename): + loader = importlib.machinery.SourceFileLoader( + modname, filename + ) + spec = importlib.util.spec_from_file_location( + modname, filename, loader=loader + ) + module = importlib.util.module_from_spec(spec) + # Module is always executed and not cached in sys.modules. + # Uncomment the following line to cache the module. + # sys.modules[module.__name__] = module + loader.exec_module(module) + return module pyFile = command[0] if sys.platform == "win32": @@ -601,7 +616,7 @@ def RunCmd( parent=self._guiparent, message=_("Module <%s> not found.") % command[0], ) - pymodule = imp.load_source(command[0].replace(".", "_"), pyPath) + pymodule = load_source(command[0].replace(".", "_"), pyPath) pymain = inspect.getfullargspec(pymodule.main) if pymain and "giface" in pymain.args: pymodule.main(self._giface) From 296147767f9a9942bba8ab1409c8183432c7b814 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 20 Aug 2024 17:54:25 -0400 Subject: [PATCH 144/471] CI(deps): Update github/codeql-action action to v3.26.3 (#4199) --- .github/workflows/codeql-analysis.yml | 4 ++-- .github/workflows/python-code-quality.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index c86e7bfc165..6e0115cc9a6 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -56,7 +56,7 @@ jobs: if: ${{ matrix.language == 'c-cpp' }} - name: Initialize CodeQL - uses: github/codeql-action/init@429e1977040da7a23b6822b13c129cd1ba93dbb2 # v3.26.2 + uses: github/codeql-action/init@883d8588e56d1753a8a58c1c86e88976f0c23449 # v3.26.3 with: languages: ${{ matrix.language }} config-file: ./.github/codeql/codeql-config.yml @@ -81,6 +81,6 @@ jobs: run: .github/workflows/build_ubuntu-22.04.sh "${HOME}/install" - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@429e1977040da7a23b6822b13c129cd1ba93dbb2 # v3.26.2 + uses: github/codeql-action/analyze@883d8588e56d1753a8a58c1c86e88976f0c23449 # v3.26.3 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index 3ca7e512a3a..f47b0ec8fc2 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -135,7 +135,7 @@ jobs: path: bandit.sarif - name: Upload SARIF File into Security Tab - uses: github/codeql-action/upload-sarif@429e1977040da7a23b6822b13c129cd1ba93dbb2 # v3.26.2 + uses: github/codeql-action/upload-sarif@883d8588e56d1753a8a58c1c86e88976f0c23449 # v3.26.3 with: sarif_file: bandit.sarif From e1b289fb8aa0347a56b57f1f356c983875fc4724 Mon Sep 17 00:00:00 2001 From: Paulo van Breugel Date: Wed, 21 Aug 2024 00:34:33 +0200 Subject: [PATCH 145/471] startup: Change Location to Project in first-time user messages (#4193) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update infomanager.py: Change Location for Project in messages The startup message for first-time users refers to the old Locations. This should be changed to Projects to match the new terminology used in 8.4. * Update infomanager.py From Mapset to mapset (all lowercase) * Apply suggestions from code review Co-authored-by: Edouard Choinière <27212526+echoix@users.noreply.github.com> --------- Co-authored-by: Edouard Choinière <27212526+echoix@users.noreply.github.com> --- gui/wxpython/datacatalog/infomanager.py | 26 ++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/gui/wxpython/datacatalog/infomanager.py b/gui/wxpython/datacatalog/infomanager.py index 9f2f33a7fbe..d3daf4d38fa 100644 --- a/gui/wxpython/datacatalog/infomanager.py +++ b/gui/wxpython/datacatalog/infomanager.py @@ -33,16 +33,16 @@ def __init__(self, infobar, giface): def ShowDataStructureInfo(self, onCreateLocationHandler): """Show info about the data hierarchy focused on the first-time user""" buttons = [ - (_("Create new Location"), onCreateLocationHandler), + (_("Create new project"), onCreateLocationHandler), (_("Learn more"), self._onLearnMore), ] message = _( - "GRASS GIS helps you organize your data using Locations (projects) " - "which contain Mapsets (subprojects). All data in one Location is " + "GRASS GIS helps you organize your data using projects (locations) " + "which contain mapsets (subprojects). All data in one project is " "in the same coordinate reference system (CRS).\n\n" - "You are currently in Mapset PERMANENT in default Location {loc} " + "You are currently in mapset PERMANENT in default project {loc} " "which uses WGS 84 (EPSG:4326). " - "Consider creating a new Location with a CRS " + "Consider creating a new project with a CRS " "specific to your area. You can do it now or anytime later from " "the toolbar above." ).format(loc=gisenv()["LOCATION_NAME"]) @@ -55,11 +55,11 @@ def ShowImportDataInfo(self, OnImportOgrLayersHandler, OnImportGdalLayersHandler (_("Import raster data"), OnImportGdalLayersHandler), ] message = _( - "You have successfully created a new Location {loc}. " - "Currently you are in its PERMANENT Mapset which is used for " + "You have successfully created a new project {loc}. " + "Currently you are in its PERMANENT mapset which is used for " "storing your base maps to make them readily available in other " - "Mapsets. You can create new Mapsets for different tasks by right " - "clicking on the Location name.\n\n" + "mapsets. You can create new mapsets for different tasks by right " + "clicking on the project name.\n\n" "To import data, go to the toolbar above or use the buttons below." ).format(loc=gisenv()["LOCATION_NAME"]) self.infoBar.ShowMessage(message, wx.ICON_INFORMATION, buttons) @@ -81,8 +81,8 @@ def ShowFallbackSessionInfo(self, reason_id): """Show info when last used mapset is not usable""" string = self._text_from_reason_id(reason_id) message = _( - "{string} GRASS GIS has started in a temporary Location. " - "To continue, use Data Catalog below to switch to a different Location." + "{string} GRASS GIS has started in a temporary project. " + "To continue, use Data Catalog below to switch to a different project." ).format( string=string, ) @@ -94,8 +94,8 @@ def ShowLockedMapsetInfo(self, OnSwitchMapsetHandler): buttons = [(_("Switch to last used mapset"), OnSwitchMapsetHandler)] message = _( "Last used mapset in path '{mapsetpath}' is currently in use. " - "GRASS GIS has started in a temporary Location. " - "To continue, use Data Catalog below to switch to a different Location " + "GRASS GIS has started in a temporary project. " + "To continue, use Data Catalog below to switch to a different project " "or remove lock file and switch to the last used mapset." ).format(mapsetpath=last_used_mapset_path) self.infoBar.ShowMessage(message, wx.ICON_INFORMATION, buttons) From 037a5ed0fc11bfd1f267cd1c3309ab9c0049fa7e Mon Sep 17 00:00:00 2001 From: Anna Petrasova Date: Tue, 20 Aug 2024 18:39:18 -0400 Subject: [PATCH 146/471] g.download.project: rename g.download.location (#4187) --- .github/workflows/macos.yml | 2 +- .github/workflows/test_thorough.bat | 2 +- .github/workflows/test_thorough.sh | 2 +- scripts/Makefile | 1 + .../g.download.location.html | 43 +---- .../g.download.location.py | 115 +------------ scripts/g.download.project/Makefile | 7 + .../g.download.project.html | 46 ++++++ .../g.download.project/g.download.project.py | 153 ++++++++++++++++++ 9 files changed, 224 insertions(+), 147 deletions(-) create mode 100644 scripts/g.download.project/Makefile create mode 100644 scripts/g.download.project/g.download.project.html create mode 100644 scripts/g.download.project/g.download.project.py diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index bf3859334ec..5a82b1020ea 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -88,7 +88,7 @@ jobs: shell: bash -el {0} run: | grass --tmp-project XY --exec \ - g.download.location url=${{ env.SampleData }} path=$HOME + g.download.project url=${{ env.SampleData }} path=$HOME grass --tmp-project XY --exec \ python3 -m grass.gunittest.main \ --grassdata $HOME --location nc_spm_full_v2alpha2 --location-type nc \ diff --git a/.github/workflows/test_thorough.bat b/.github/workflows/test_thorough.bat index 4b03a5608be..963f24b9b43 100644 --- a/.github/workflows/test_thorough.bat +++ b/.github/workflows/test_thorough.bat @@ -1,5 +1,5 @@ set grass=%1 set python=%2 -call %grass% --tmp-project XY --exec g.download.location url=https://grass.osgeo.org/sampledata/north_carolina/nc_spm_full_v2alpha2.tar.gz path=%USERPROFILE% +call %grass% --tmp-project XY --exec g.download.project url=https://grass.osgeo.org/sampledata/north_carolina/nc_spm_full_v2alpha2.tar.gz path=%USERPROFILE% call %grass% --tmp-project XY --exec %python% -m grass.gunittest.main --grassdata %USERPROFILE% --location nc_spm_full_v2alpha2 --location-type nc --min-success 80 diff --git a/.github/workflows/test_thorough.sh b/.github/workflows/test_thorough.sh index dba6b94b77d..6ed7d22078a 100755 --- a/.github/workflows/test_thorough.sh +++ b/.github/workflows/test_thorough.sh @@ -4,7 +4,7 @@ set -e grass --tmp-project XY --exec \ - g.download.location url=https://grass.osgeo.org/sampledata/north_carolina/nc_spm_full_v2alpha2.tar.gz path=$HOME + g.download.project url=https://grass.osgeo.org/sampledata/north_carolina/nc_spm_full_v2alpha2.tar.gz path=$HOME grass --tmp-project XY --exec \ python3 -m grass.gunittest.main \ diff --git a/scripts/Makefile b/scripts/Makefile index 84a9ad21e26..dc3d80f4209 100644 --- a/scripts/Makefile +++ b/scripts/Makefile @@ -19,6 +19,7 @@ SUBDIRS = \ db.test \ db.univar \ g.download.location \ + g.download.project \ g.extension \ g.extension.all \ g.manual \ diff --git a/scripts/g.download.location/g.download.location.html b/scripts/g.download.location/g.download.location.html index 6900acc4388..d9a73dc5513 100644 --- a/scripts/g.download.location/g.download.location.html +++ b/scripts/g.download.location/g.download.location.html @@ -1,47 +1,16 @@

DESCRIPTION

-g.download.location downloads an archived (e.g., -.zip or .tar.gz) project (previously called -location) from a given URL -and unpacks it to a specified or current GRASS GIS Spatial Database. -URL can be also a local file on the disk. - -If the archive contains a directory which contains a project, the module -will recognize that and use the project automatically. -The first directory which is a project is used. -Other projects or any other files are ignored. - -

EXAMPLES

- -

Download the full GRASS GIS sample project within a running session

- -Download and unpack the full North Carolina sample project into the user's -HOME directory: - -
-g.download.location url=https://grass.osgeo.org/sampledata/north_carolina/nc_spm_full_v2alpha2.tar.gz path=$HOME
-
- -

Download the full GRASS GIS sample project in a temporary session

- -In a temporary session, download and unpack the full North Carolina sample project -into the user's HOME directory: - -
-grass --tmp-location XY --exec g.download.location url=https://grass.osgeo.org/sampledata/north_carolina/nc_spm_full_v2alpha2.tar.gz path=$HOME
-
- +g.download.location has been renamed to +g.download.project +and exists for backwards compatibility reasons. +It will be removed in the next major version.

SEE ALSO

- g.mapset, - g.mapsets, - r.proj, - v.proj, - g.proj.all + g.download.project

AUTHOR

-Vaclav Petras, NCSU GeoForAll Lab +Vaclav Petras, NCSU GeoForAll Lab diff --git a/scripts/g.download.location/g.download.location.py b/scripts/g.download.location/g.download.location.py index bca79b12492..0e58a1ba395 100644 --- a/scripts/g.download.location/g.download.location.py +++ b/scripts/g.download.location/g.download.location.py @@ -3,8 +3,8 @@ # # MODULE: g.download.location # AUTHOR(S): Vaclav Petras -# PURPOSE: Download and extract location from web -# COPYRIGHT: (C) 2017 by the GRASS Development Team +# PURPOSE: Download and extract project (location) from web +# COPYRIGHT: (C) 2017-2024 by the GRASS Development Team # # This program is free software under the GNU General # Public License (>=v2). Read the file COPYING that @@ -12,11 +12,11 @@ # ############################################################################# -"""Download GRASS Locations""" +"""Download GRASS projects""" # %module -# % label: Download GRASS Location from the web -# % description: Get GRASS Location from an URL or file path +# % label: Download GRASS project (location) from the web +# % description: Get GRASS project from an URL or file path # % keyword: general # % keyword: data # % keyword: download @@ -26,7 +26,7 @@ # % key: url # % multiple: no # % type: string -# % label: URL of the archive with a location to be downloaded +# % label: URL of the archive with a project to be downloaded # % description: URL of ZIP, TAR.GZ, or other similar archive # % required: yes # %end @@ -42,111 +42,12 @@ # % multiple: no # %end -import atexit -import os -import shutil -from pathlib import Path - import grass.script as gs -from grass.grassdb.checks import is_location_valid -from grass.script.utils import try_rmdir -from grass.utils.download import DownloadError, download_and_extract, name_from_url - - -def find_location_in_directory(path, recurse=0): - """Return path to location in one of the subdirectories or None - - The first location found is returned. The expected usage is looking for one - location somewhere nested in subdirectories. - - By default only the immediate subdirectories of the provided directory are - tested, but with ``recurse >= 1`` additional levels of subdirectories - are tested for being locations. - - Directory names are sorted to provide a stable result. - - :param path: Path to the directory to search - :param recurse: How many additional levels of subdirectories to explore - """ - assert recurse >= 0 - full_paths = [os.path.join(path, i) for i in os.listdir(path)] - candidates = sorted([i for i in full_paths if os.path.isdir(i)]) - for candidate in candidates: - if is_location_valid(candidate): - return candidate - if recurse: - for candidate in candidates: - result = find_location_in_directory(candidate, recurse - 1) - if result: - return result - return None - - -def location_name_from_url(url): - """Create location name from URL""" - return gs.legalize_vector_name(name_from_url(url)) def main(options, unused_flags): - """Download and copy location to destination""" - url = options["url"] - name = options["name"] - database = options["path"] - - if not database: - # Use the current database path. - database = gs.gisenv()["GISDBASE"] - if not name: - name = location_name_from_url(url) - destination = Path(database) / name - - if destination.exists(): - gs.fatal( - _( - "Location named <{name}> already exists in <{directory}>, download canceled" - ).format(name=name, directory=database) - ) - - gs.message(_("Downloading and extracting...")) - try: - directory = download_and_extract(url) - if not directory.is_dir(): - gs.fatal(_("Archive contains only one file and no mapset directories")) - atexit.register(lambda: try_rmdir(directory)) - except DownloadError as error: - gs.fatal(_("Unable to get the location: {error}").format(error=error)) - if not is_location_valid(directory): - gs.verbose(_("Searching for valid location...")) - # This in fact deal with location being on the third level of directories - # thanks to how the extraction functions work (leaving out one level). - result = find_location_in_directory(directory, recurse=1) - if result: - # We just want to show relative path in the message. - # The relative path misses the root directory (name), because we - # loose it on the way. (We should use parent directory to get the - # full relative path, but the directory name is different now. - # This is the consequence of how the extract functions work.) - relative = os.path.relpath(result, start=directory) - gs.verbose( - _("Location found in a nested directory '{directory}'").format( - directory=relative - ) - ) - directory = result - else: - # The list is similarly misleading as the relative path above - # as it misses the root directory, but it still should be useful. - files_and_dirs = os.listdir(directory) - gs.fatal( - _( - "The downloaded file is not a valid GRASS Location." - " The extracted file contains these files and directories:" - "\n{files_and_dirs}" - ).format(files_and_dirs=" ".join(files_and_dirs)) - ) - gs.verbose(_("Copying to final destination...")) - shutil.copytree(src=directory, dst=destination) - gs.message(_("Path to the location now <{path}>").format(path=destination)) + """Download and copy project to destination""" + gs.run_command("g.dowload.project", **options) if __name__ == "__main__": diff --git a/scripts/g.download.project/Makefile b/scripts/g.download.project/Makefile new file mode 100644 index 00000000000..0f5e9d1c504 --- /dev/null +++ b/scripts/g.download.project/Makefile @@ -0,0 +1,7 @@ +MODULE_TOPDIR = ../.. + +PGM = g.download.project + +include $(MODULE_TOPDIR)/include/Make/Script.make + +default: script diff --git a/scripts/g.download.project/g.download.project.html b/scripts/g.download.project/g.download.project.html new file mode 100644 index 00000000000..7fc469fb0a6 --- /dev/null +++ b/scripts/g.download.project/g.download.project.html @@ -0,0 +1,46 @@ +

DESCRIPTION

+ +g.download.project downloads an archived (e.g., +.zip or .tar.gz) project (previously called +location) from a given URL +and unpacks it to a specified or current GRASS GIS Spatial Database. +URL can be also a local file on the disk. + +If the archive contains a directory which contains a project, the module +will recognize that and use the project automatically. +The first directory which is a project is used. +Other projects or any other files are ignored. + +

EXAMPLES

+ +

Download the full GRASS GIS sample project within a running session

+ +Download and unpack the full North Carolina sample project into the user's +HOME directory: + +
+g.download.project url=https://grass.osgeo.org/sampledata/north_carolina/nc_spm_full_v2alpha2.tar.gz path=$HOME
+
+ +

Download the full GRASS GIS sample project in a temporary session

+ +In a temporary session, download and unpack the full North Carolina sample project +into the user's HOME directory: + +
+grass --tmp-project XY --exec g.download.project url=https://grass.osgeo.org/sampledata/north_carolina/nc_spm_full_v2alpha2.tar.gz path=$HOME
+
+ +

SEE ALSO

+ + + g.mapset, + g.mapsets, + r.proj, + v.proj, + g.proj.all + + +

AUTHOR

+ +Vaclav Petras, NCSU GeoForAll Lab diff --git a/scripts/g.download.project/g.download.project.py b/scripts/g.download.project/g.download.project.py new file mode 100644 index 00000000000..aa963c31dd3 --- /dev/null +++ b/scripts/g.download.project/g.download.project.py @@ -0,0 +1,153 @@ +#!/usr/bin/env python3 +############################################################################ +# +# MODULE: g.download.project +# AUTHOR(S): Vaclav Petras +# PURPOSE: Download and extract project (location) from web +# COPYRIGHT: (C) 2017-2024 by the GRASS Development Team +# +# This program is free software under the GNU General +# Public License (>=v2). Read the file COPYING that +# comes with GRASS for details. +# +############################################################################# + +"""Download GRASS projects""" + +# %module +# % label: Download GRASS project from the web +# % description: Get GRASS project from an URL or file path +# % keyword: general +# % keyword: data +# % keyword: download +# % keyword: import +# %end +# %option +# % key: url +# % multiple: no +# % type: string +# % label: URL of the archive with a project to be downloaded +# % description: URL of ZIP, TAR.GZ, or other similar archive +# % required: yes +# %end +# %option G_OPT_M_LOCATION +# % key: name +# % required: no +# % multiple: no +# % key_desc: name +# %end +# %option G_OPT_M_DBASE +# % key: path +# % required: no +# % multiple: no +# %end + +import atexit +import os +import shutil +from pathlib import Path + +import grass.script as gs +from grass.grassdb.checks import is_location_valid +from grass.script.utils import try_rmdir +from grass.utils.download import DownloadError, download_and_extract, name_from_url + + +def find_location_in_directory(path, recurse=0): + """Return path to location in one of the subdirectories or None + + The first location found is returned. The expected usage is looking for one + location somewhere nested in subdirectories. + + By default only the immediate subdirectories of the provided directory are + tested, but with ``recurse >= 1`` additional levels of subdirectories + are tested for being locations. + + Directory names are sorted to provide a stable result. + + :param path: Path to the directory to search + :param recurse: How many additional levels of subdirectories to explore + """ + assert recurse >= 0 + full_paths = [os.path.join(path, i) for i in os.listdir(path)] + candidates = sorted([i for i in full_paths if os.path.isdir(i)]) + for candidate in candidates: + if is_location_valid(candidate): + return candidate + if recurse: + for candidate in candidates: + result = find_location_in_directory(candidate, recurse - 1) + if result: + return result + return None + + +def location_name_from_url(url): + """Create location name from URL""" + return gs.legalize_vector_name(name_from_url(url)) + + +def main(options, unused_flags): + """Download and copy location to destination""" + url = options["url"] + name = options["name"] + database = options["path"] + + if not database: + # Use the current database path. + database = gs.gisenv()["GISDBASE"] + if not name: + name = location_name_from_url(url) + destination = Path(database) / name + + if destination.exists(): + gs.fatal( + _( + "Project named <{}> already exists in <{directory}>, download canceled" + ).format(name=name, directory=database) + ) + + gs.message(_("Downloading and extracting...")) + try: + directory = download_and_extract(url) + if not directory.is_dir(): + gs.fatal(_("Archive contains only one file and no mapset directories")) + atexit.register(lambda: try_rmdir(directory)) + except DownloadError as error: + gs.fatal(_("Unable to get the project: {error}").format(error=error)) + if not is_location_valid(directory): + gs.verbose(_("Searching for valid project...")) + # This in fact deal with location being on the third level of directories + # thanks to how the extraction functions work (leaving out one level). + result = find_location_in_directory(directory, recurse=1) + if result: + # We just want to show relative path in the message. + # The relative path misses the root directory (name), because we + # loose it on the way. (We should use parent directory to get the + # full relative path, but the directory name is different now. + # This is the consequence of how the extract functions work.) + relative = os.path.relpath(result, start=directory) + gs.verbose( + _("Project found in a nested directory '{directory}'").format( + directory=relative + ) + ) + directory = result + else: + # The list is similarly misleading as the relative path above + # as it misses the root directory, but it still should be useful. + files_and_dirs = os.listdir(directory) + gs.fatal( + _( + "The downloaded file is not a valid GRASS project." + " The extracted file contains these files and directories:" + "\n{files_and_dirs}" + ).format(files_and_dirs=" ".join(files_and_dirs)) + ) + gs.verbose(_("Copying to final destination...")) + shutil.copytree(src=directory, dst=destination) + gs.message(_("Path to the project now <{path}>").format(path=destination)) + + +if __name__ == "__main__": + main(*gs.parser()) From 17bf103b17be28cad46895f350fc23dde1b1067b Mon Sep 17 00:00:00 2001 From: Vaclav Petras Date: Wed, 21 Aug 2024 11:01:23 -0400 Subject: [PATCH 147/471] g.download.location: Fix typo in g.download.project call (#4205) The underlying tool name was misspelled. --- scripts/g.download.location/g.download.location.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/g.download.location/g.download.location.py b/scripts/g.download.location/g.download.location.py index 0e58a1ba395..61660457a4a 100644 --- a/scripts/g.download.location/g.download.location.py +++ b/scripts/g.download.location/g.download.location.py @@ -47,7 +47,7 @@ def main(options, unused_flags): """Download and copy project to destination""" - gs.run_command("g.dowload.project", **options) + gs.run_command("g.download.project", **options) if __name__ == "__main__": From 2171389179c2e8f596acc43638ef81b4234a560b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 21 Aug 2024 18:32:06 -0400 Subject: [PATCH 148/471] CI(deps): Update github/codeql-action action to v3.26.4 (#4208) --- .github/workflows/codeql-analysis.yml | 4 ++-- .github/workflows/python-code-quality.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 6e0115cc9a6..73fbe757909 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -56,7 +56,7 @@ jobs: if: ${{ matrix.language == 'c-cpp' }} - name: Initialize CodeQL - uses: github/codeql-action/init@883d8588e56d1753a8a58c1c86e88976f0c23449 # v3.26.3 + uses: github/codeql-action/init@f0f3afee809481da311ca3a6ff1ff51d81dbeb24 # v3.26.4 with: languages: ${{ matrix.language }} config-file: ./.github/codeql/codeql-config.yml @@ -81,6 +81,6 @@ jobs: run: .github/workflows/build_ubuntu-22.04.sh "${HOME}/install" - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@883d8588e56d1753a8a58c1c86e88976f0c23449 # v3.26.3 + uses: github/codeql-action/analyze@f0f3afee809481da311ca3a6ff1ff51d81dbeb24 # v3.26.4 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index f47b0ec8fc2..3dd5e2834e8 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -135,7 +135,7 @@ jobs: path: bandit.sarif - name: Upload SARIF File into Security Tab - uses: github/codeql-action/upload-sarif@883d8588e56d1753a8a58c1c86e88976f0c23449 # v3.26.3 + uses: github/codeql-action/upload-sarif@f0f3afee809481da311ca3a6ff1ff51d81dbeb24 # v3.26.4 with: sarif_file: bandit.sarif From a46771f12fc338d76dffff6a6244245f69b8fdcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Wed, 21 Aug 2024 22:30:30 -0400 Subject: [PATCH 149/471] CI(python): Enable printing of all Python warnings with PYTHONWARNINGS (#4210) Enables to print all occurrences of warnings, and shows all warnings. --- .github/workflows/macos.yml | 2 ++ .github/workflows/osgeo4w.yml | 2 ++ .github/workflows/pytest.yml | 1 + .github/workflows/ubuntu.yml | 2 ++ 4 files changed, 7 insertions(+) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 5a82b1020ea..0a90a022d98 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -18,6 +18,8 @@ jobs: macos_build: name: macOS build runs-on: macos-14 + env: + PYTHONWARNINGS: always steps: - name: Info run: | diff --git a/.github/workflows/osgeo4w.yml b/.github/workflows/osgeo4w.yml index ba1217620c3..7152265fd3d 100644 --- a/.github/workflows/osgeo4w.yml +++ b/.github/workflows/osgeo4w.yml @@ -18,6 +18,8 @@ jobs: cancel-in-progress: true runs-on: ${{ matrix.os }} + env: + PYTHONWARNINGS: always strategy: matrix: os: diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 997798d8873..9948fafff60 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -29,6 +29,7 @@ jobs: env: FORCE_COLOR: 1 # for software including pip: https://force-color.org/ CLICOLOR_FORCE: 1 # for other software including ninja: https://bixense.com/clicolors/ + PYTHONWARNINGS: always steps: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 4f70486e05e..626d3d26676 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -18,6 +18,8 @@ jobs: cancel-in-progress: true runs-on: ${{ matrix.os }} + env: + PYTHONWARNINGS: always strategy: matrix: name: From 42739e4a266d6f346278ab12a8f470eb0f9be609 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Wed, 21 Aug 2024 22:56:14 -0400 Subject: [PATCH 150/471] CI(pytest): Run pytest tests with Python 3.13 (#4209) Also replace Python 3.8 by Python 3.9 to keep a total of 3 versions tested --- .github/workflows/pytest.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 9948fafff60..c0625635980 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -20,9 +20,9 @@ jobs: os: - ubuntu-22.04 python-version: - - '3.8' - - '3.10' + - '3.9' - '3.12' + - '3.13' fail-fast: true runs-on: ${{ matrix.os }} @@ -39,6 +39,7 @@ jobs: with: python-version: ${{ matrix.python-version }} cache: pip + allow-prereleases: true - name: Install non-Python dependencies run: | From dd5547fd6a189ced99338de7c21f2f34a73d01da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Thu, 22 Aug 2024 07:58:55 -0400 Subject: [PATCH 151/471] configure: update to latest config.guess and config.sub (#4161) --- config.guess | 11 +- config.sub | 729 +++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 563 insertions(+), 177 deletions(-) diff --git a/config.guess b/config.guess index f6d217a49f8..48a684601bd 100755 --- a/config.guess +++ b/config.guess @@ -4,7 +4,7 @@ # shellcheck disable=SC2006,SC2268 # see below for rationale -timestamp='2024-01-01' +timestamp='2024-07-27' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by @@ -123,7 +123,7 @@ set_cc_for_build() { dummy=$tmp/dummy case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in ,,) echo "int x;" > "$dummy.c" - for driver in cc gcc c89 c99 ; do + for driver in cc gcc c17 c99 c89 ; do if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then CC_FOR_BUILD=$driver break @@ -634,7 +634,8 @@ EOF sed 's/^ //' << EOF > "$dummy.c" #include - main() + int + main () { if (!__power_pc()) exit(1); @@ -718,7 +719,8 @@ EOF #include #include - int main () + int + main () { #if defined(_SC_KERNEL_BITS) long bits = sysconf(_SC_KERNEL_BITS); @@ -1621,6 +1623,7 @@ cat > "$dummy.c" <&2 exit 1 ;; - kfreebsd*-gnu*- | kopensolaris*-gnu*-) + kfreebsd*-gnu*- | knetbsd*-gnu*- | netbsd*-gnu*- | kopensolaris*-gnu*-) ;; vxworks-simlinux- | vxworks-simwindows- | vxworks-spe-) ;; @@ -1864,6 +2245,8 @@ case $kernel-$os-$obj in ;; os2-emx-) ;; + rtmk-nova-) + ;; *-eabi*- | *-gnueabi*-) ;; none--*) @@ -1890,7 +2273,7 @@ case $vendor in *-riscix*) vendor=acorn ;; - *-sunos*) + *-sunos* | *-solaris*) vendor=sun ;; *-cnk* | *-aix*) From ff98e4212640bac95e206771573506a37c8822b7 Mon Sep 17 00:00:00 2001 From: Kriti Birda <164247895+kritibirda26@users.noreply.github.com> Date: Thu, 22 Aug 2024 17:45:01 +0530 Subject: [PATCH 152/471] db.describe: fix illegal memory access report (#4202) Fix issue reported in https://github.com/OSGeo/grass/pull/4021#issuecomment-2298894298. --- db/db.describe/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/db/db.describe/main.c b/db/db.describe/main.c index e32d1572a1a..ea0c6483778 100644 --- a/db/db.describe/main.c +++ b/db/db.describe/main.c @@ -42,8 +42,8 @@ int main(int argc, char **argv) char buf[1024]; dbString stmt; - JSON_Object *root_object, *col_object; - JSON_Value *root_value, *cols_value, *col_value; + JSON_Object *root_object = NULL, *col_object = NULL; + JSON_Value *root_value = NULL, *cols_value = NULL, *col_value = NULL; JSON_Array *cols_array = NULL; parse_command_line(argc, argv); From 92cb84ab2484aaf749d281d86ec31f5feb7828e0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 22 Aug 2024 12:01:13 -0400 Subject: [PATCH 153/471] CI(deps): Update ruff to v0.6.2 (#4212) --- .github/workflows/python-code-quality.yml | 2 +- .pre-commit-config.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index 3dd5e2834e8..5f8de26bc23 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -36,7 +36,7 @@ jobs: # renovate: datasource=pypi depName=bandit BANDIT_VERSION: "1.7.9" # renovate: datasource=pypi depName=ruff - RUFF_VERSION: "0.6.1" + RUFF_VERSION: "0.6.2" runs-on: ${{ matrix.os }} permissions: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4bb4781ebe6..d20d238f908 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -37,7 +37,7 @@ repos: ) - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.6.1 + rev: v0.6.2 hooks: # Run the linter. - id: ruff From 913c62074ce65505637d639e2cb95fae9d037861 Mon Sep 17 00:00:00 2001 From: Makiko Shukunobe Date: Thu, 22 Aug 2024 23:33:47 -0400 Subject: [PATCH 154/471] Checks: Fix flake8 F841 (local variable assigned to but never used) in /temporal/t.rast.to.vect (#4206) --- .flake8 | 2 +- temporal/t.rast.to.vect/t.rast.to.vect.py | 3 ++- temporal/t.rast.to.vect/testsuite/test_to_vect.py | 7 ------- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/.flake8 b/.flake8 index 7019d947c03..b628b1ad53c 100644 --- a/.flake8 +++ b/.flake8 @@ -177,7 +177,7 @@ per-file-ignores = # Line too long (esp. module interface definitions) scripts/*/*.py: E501 # local variable 'column' is assigned to but never used - temporal/t.rast.to.vect/t.rast.to.vect.py: F841, E501 + temporal/t.rast.to.vect/t.rast.to.vect.py: E501 # local variable 'stdstype' is assigned to but never used temporal/t.vect.algebra/t.vect.algebra.py: F841, E501 # ## used (##% key: r etc) diff --git a/temporal/t.rast.to.vect/t.rast.to.vect.py b/temporal/t.rast.to.vect/t.rast.to.vect.py index 744721ac433..603196265b0 100755 --- a/temporal/t.rast.to.vect/t.rast.to.vect.py +++ b/temporal/t.rast.to.vect/t.rast.to.vect.py @@ -163,7 +163,7 @@ def main(options, flags): return # Check the new stvds - new_sp = tgis.check_new_stds(output, "stvds", dbif=dbif, overwrite=overwrite) + tgis.check_new_stds(output, "stvds", dbif=dbif, overwrite=overwrite) # Setup the flags flags = "" @@ -189,6 +189,7 @@ def main(options, flags): type=method, overwrite=overwrite, quiet=True, + column=column, ) # The module queue for parallel execution, except if attribute tables should diff --git a/temporal/t.rast.to.vect/testsuite/test_to_vect.py b/temporal/t.rast.to.vect/testsuite/test_to_vect.py index 5a4cc8582fb..2217b46a909 100644 --- a/temporal/t.rast.to.vect/testsuite/test_to_vect.py +++ b/temporal/t.rast.to.vect/testsuite/test_to_vect.py @@ -64,7 +64,6 @@ def test_simple_points(self): output="result", type="point", flags="n", - column="values", basename="test", nprocs=1, overwrite=True, @@ -93,7 +92,6 @@ def test_simple_area(self): output="result", type="area", flags="n", - column="values", basename="test", nprocs=1, overwrite=True, @@ -127,7 +125,6 @@ def test_simple_area_smooth(self): output="result", type="area", flags="s", - column="values", basename="test", nprocs=1, overwrite=True, @@ -160,7 +157,6 @@ def test_parallel(self): output="result", type="point", flags="t", - column="values", basename="test", nprocs=4, overwrite=True, @@ -188,7 +184,6 @@ def test_num_suffix(self): output="result", type="point", flags="t", - column="values", basename="test", suffix="num%03", nprocs=4, @@ -204,7 +199,6 @@ def test_time_suffix(self): output="result", type="point", flags="t", - column="values", basename="test", suffix="time", nprocs=4, @@ -261,7 +255,6 @@ def test_empty_strds(self): output="result", type="point", flags="n", - column="values", basename="test", where="start_time > '2010-01-01'", nprocs=1, From 120513715d6b93c8257216cb83349fc2d3131b06 Mon Sep 17 00:00:00 2001 From: Ondrej Pesek Date: Fri, 23 Aug 2024 10:13:12 +0200 Subject: [PATCH 155/471] i.atcorr: fix plot_filter function in create_iwave (#3911) * missing import of pyplot * fix figures not shown correctly * fix wrong axes --- imagery/i.atcorr/create_iwave.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/imagery/i.atcorr/create_iwave.py b/imagery/i.atcorr/create_iwave.py index 022fdf8d12e..8afc6bebd36 100644 --- a/imagery/i.atcorr/create_iwave.py +++ b/imagery/i.atcorr/create_iwave.py @@ -155,17 +155,28 @@ def interpolate_band(values, step=2.5): def plot_filter(values): """Plot wl response values and interpolated filter function. This is just for checking... - value is a 2 column numpy array + value is a 2-column numpy array function has to be used inside Spyder python environment """ + import matplotlib.pyplot as plt + filter_f, limits = interpolate_band(values) # removing nodata w = values[:, 1] >= 0 response = values[w] - plot(response[:, 0], response[:, 1], "ro") - plot(arange(limits[0], limits[1], 2.5), filter_f) + fig = plt.figure() + ax1 = fig.add_subplot(2, 1, 1) + ax2 = fig.add_subplot(2, 1, 2) + + ax1.plot(response[:, 0], response[:, 1], "ro") + rounded = np.arange(limits[0], limits[1], 0.0025) * 1000 + if len(rounded) == len(filter_f): + ax2.plot(rounded, filter_f) + else: + ax2.plot(rounded[:-1], filter_f) + plt.show() def pretty_print(filter_f): From 917ba5890cf34cef9dcbf1477339c1b0caaaaf24 Mon Sep 17 00:00:00 2001 From: ymdatta Date: Fri, 23 Aug 2024 07:56:05 -0400 Subject: [PATCH 156/471] r.out.png: fix consecutive fclose calls on same pointer (#4214) This patch fixes two issues: 1. In one of the code paths, we are calling fclose on a file pointer which could potentially be NULL. Doing that would lead to undefined behavior. Check if a file pointer is NULL before closing it. 2. If we call fclose on same file pointer twice, in the second instance we could be closing file descriptor allocated to some other file, which typically happens to a freed descriptor. This issue was found by using cppcheck tool. Signed-off-by: Mohana Datta Yelugoti --- raster/r.out.png/main.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/raster/r.out.png/main.c b/raster/r.out.png/main.c index 2bc826fac59..4d3895e5d9f 100644 --- a/raster/r.out.png/main.c +++ b/raster/r.out.png/main.c @@ -63,7 +63,7 @@ int main(int argc, char *argv[]) int png_compr, /* ret, */ do_alpha; struct Cell_head win; FILEDESC cellfile = 0; - FILE *fp; + FILE *fp = NULL; /* now goes from pnmtopng.c* -A.Sh */ /* @@ -207,20 +207,29 @@ int main(int argc, char *argv[]) png_create_write_struct(PNG_LIBPNG_VER_STRING, &pnmtopng_jmpbuf_struct, pnmtopng_error_handler, NULL); if (png_ptr == NULL) { - fclose(fp); + if (fp) { + fclose(fp); + fp = NULL; + } G_fatal_error("cannot allocate LIBPNG structure"); } info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { png_destroy_write_struct(&png_ptr, (png_infopp)NULL); - fclose(fp); + if (fp) { + fclose(fp); + fp = NULL; + } G_fatal_error("cannot allocate LIBPNG structure"); } if (setjmp(pnmtopng_jmpbuf_struct.jmpbuf)) { png_destroy_write_struct(&png_ptr, &info_ptr); - fclose(fp); + if (fp) { + fclose(fp); + fp = NULL; + } G_fatal_error("setjmp returns error condition (1)"); } @@ -360,7 +369,8 @@ int main(int argc, char *argv[]) /* G_free (info_ptr); */ png_destroy_write_struct(&png_ptr, &info_ptr); /* al 11/2000 */ - fclose(fp); + if (fp) + fclose(fp); if (wld_flag->answer) { if (do_stdout) From b794d953930489eea255f02a70140efd6ae79e5a Mon Sep 17 00:00:00 2001 From: Nicklas Larsson Date: Fri, 23 Aug 2024 18:29:27 +0200 Subject: [PATCH 157/471] lib: fix missing prototypes (#4191) * r.report: add missing prototype * lib/db: add missing prototype * CI: fail macOS runner on -Wstrict-prototypes * lib/gmath: suppress -Wstrict-prototypes for BLAS/LAPACK wrapper header --- .github/workflows/macos_install.sh | 2 +- include/grass/la.h | 16 +++++++++++++++- lib/db/sqlp/sqlp.l | 2 +- raster/r.report/global.h | 2 +- raster/r.report/prt_json.c | 2 +- 5 files changed, 19 insertions(+), 5 deletions(-) diff --git a/.github/workflows/macos_install.sh b/.github/workflows/macos_install.sh index ce652491bdd..79446f51867 100755 --- a/.github/workflows/macos_install.sh +++ b/.github/workflows/macos_install.sh @@ -69,7 +69,7 @@ export CPPFLAGS="-isystem${CONDA_PREFIX}/include" ./configure $CONFIGURE_FLAGS -EXEMPT="-Wno-error=deprecated-non-prototype -Wno-error=strict-prototypes" +EXEMPT="" make -j$(sysctl -n hw.ncpu) CFLAGS="$CFLAGS -Werror $EXEMPT" \ CXXFLAGS="$CXXFLAGS -Werror $EXEMPT" diff --git a/include/grass/la.h b/include/grass/la.h index bfae5a01c12..2981dc3401e 100644 --- a/include/grass/la.h +++ b/include/grass/la.h @@ -44,7 +44,7 @@ typedef long int __g77_longint; typedef unsigned long int __g77_ulongint; #include -#else /* for gcc4+ */ +#else /* for gcc4+ */ typedef int integer; typedef unsigned int uinteger; typedef char *address; @@ -67,6 +67,14 @@ typedef unsigned long ulongint; /* IO stuff */ typedef int ftnlen; +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wstrict-prototypes" +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstrict-prototypes" +#endif + /* procedure parameter types for -A */ typedef int (*U_fp)(); typedef shortint (*J_fp)(); @@ -80,6 +88,12 @@ typedef shortlogical (*K_fp)(); typedef void (*H_fp)(); typedef int (*S_fp)(); +#if defined(__clang__) +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + /* E_fp is for real functions when -R is not specified */ typedef void C_f; /* complex function */ typedef void H_f; /* character function */ diff --git a/lib/db/sqlp/sqlp.l b/lib/db/sqlp/sqlp.l index 07c00b7a975..961c25b167d 100644 --- a/lib/db/sqlp/sqlp.l +++ b/lib/db/sqlp/sqlp.l @@ -273,7 +273,7 @@ void yyerror( const char *s ) * of this func anyway so we can avoid the link dependency. * **********************************************************************/ -int yywrap() +int yywrap(void) { return 1; } diff --git a/raster/r.report/global.h b/raster/r.report/global.h index a05ac1ae1d5..4ebc49b1916 100644 --- a/raster/r.report/global.h +++ b/raster/r.report/global.h @@ -121,7 +121,7 @@ int print_unit(int, int, int); JSON_Value *make_units(int, int); JSON_Value *make_category(int, int, JSON_Value *); JSON_Value *make_categories(int, int, int); -void print_json(); +void print_json(void); /* report.c */ int report(void); diff --git a/raster/r.report/prt_json.c b/raster/r.report/prt_json.c index 94590ea7b9f..5d6a33d2c28 100644 --- a/raster/r.report/prt_json.c +++ b/raster/r.report/prt_json.c @@ -129,7 +129,7 @@ JSON_Value *make_categories(int start, int end, int level) return array_value; } -void print_json() +void print_json(void) { compute_unit_format(0, nunits - 1, JSON); From e98d07b5eb48416c7544244db788bc0107ddb99b Mon Sep 17 00:00:00 2001 From: Makiko Shukunobe Date: Fri, 23 Aug 2024 18:53:29 -0400 Subject: [PATCH 158/471] Checks: fix Flake8 F841 (local variable assigned to but never used) in python/grass/temporal (#4200) * Delete variable Declarations with Definitions * Delete variable assignments and a variable * Update python/grass/temporal/datetime_math.py Refactor Co-authored-by: Anna Petrasova * Remove unused function declaration * Update `.flake8` by deleting F841 in python/grass/temporal * Delete declarations of unused functions --------- Co-authored-by: Anna Petrasova --- .flake8 | 14 ++++++-------- .../grass/temporal/abstract_space_time_dataset.py | 3 --- python/grass/temporal/datetime_math.py | 2 +- python/grass/temporal/open_stds.py | 2 -- python/grass/temporal/temporal_algebra.py | 2 -- python/grass/temporal/temporal_granularity.py | 4 ++-- .../temporal/temporal_raster_base_algebra.py | 12 +++++------- python/grass/temporal/temporal_vector_algebra.py | 15 +-------------- 8 files changed, 15 insertions(+), 39 deletions(-) diff --git a/.flake8 b/.flake8 index b628b1ad53c..573debabe1c 100644 --- a/.flake8 +++ b/.flake8 @@ -129,17 +129,15 @@ per-file-ignores = python/grass/pygrass/raster/category.py: E721 python/grass/pygrass/rpc/__init__.py: F401, F403 python/grass/pygrass/utils.py: E402 - python/grass/temporal/abstract_space_time_dataset.py: F841, E722 - python/grass/temporal/c_libraries_interface.py: F841, E722 + python/grass/temporal/abstract_space_time_dataset.py: E722 + python/grass/temporal/c_libraries_interface.py: E722 python/grass/temporal/core.py: E722 - python/grass/temporal/datetime_math.py: F841, E722 - python/grass/temporal/open_stds.py: F841 + python/grass/temporal/datetime_math.py: E722 python/grass/temporal/spatial_topology_dataset_connector.py: E722 - python/grass/temporal/temporal_algebra.py: F841, E722 - python/grass/temporal/temporal_granularity.py: F841, E722 - python/grass/temporal/temporal_raster_base_algebra.py: F841, E722 + python/grass/temporal/temporal_algebra.py: E722 + python/grass/temporal/temporal_granularity.py: E722 + python/grass/temporal/temporal_raster_base_algebra.py: E722 python/grass/temporal/temporal_topology_dataset_connector.py: E722 - python/grass/temporal/temporal_vector_algebra.py: F841 python/grass/temporal/univar_statistics.py: E231 # Current benchmarks/tests are changing sys.path before import. # Possibly, a different approach should be taken there anyway. diff --git a/python/grass/temporal/abstract_space_time_dataset.py b/python/grass/temporal/abstract_space_time_dataset.py index 929f74e4421..515502ff3f1 100644 --- a/python/grass/temporal/abstract_space_time_dataset.py +++ b/python/grass/temporal/abstract_space_time_dataset.py @@ -2549,7 +2549,6 @@ def register_map(self, map, dbif=None): # Get basic info map_id = map.base.get_id() - map_mapset = map.base.get_mapset() map_rel_time_unit = map.get_relative_time_unit() map_ttype = map.get_temporal_type() @@ -2804,8 +2803,6 @@ def update_from_registered_maps(self, dbif=None): use_start_time = False # Get basic info - stds_name = self.base.get_name() - stds_mapset = self.base.get_mapset() sql_path = get_sql_template_path() stds_register_table = self.get_map_register() diff --git a/python/grass/temporal/datetime_math.py b/python/grass/temporal/datetime_math.py index e5be1191551..9b680760775 100644 --- a/python/grass/temporal/datetime_math.py +++ b/python/grass/temporal/datetime_math.py @@ -791,7 +791,7 @@ def check_datetime_string(time_string, use_dateutil=True): # relative time. dateutil will interpret a single number as a valid # time string, so we have to catch this case beforehand try: - value = int(time_string) + int(time_string) return _("Time string seems to specify relative time") except ValueError: pass diff --git a/python/grass/temporal/open_stds.py b/python/grass/temporal/open_stds.py index f17288695e3..3b3a28ea99a 100644 --- a/python/grass/temporal/open_stds.py +++ b/python/grass/temporal/open_stds.py @@ -269,8 +269,6 @@ def open_new_map_dataset( """ - mapset = get_current_mapset() - dbif, connection_state_changed = init_dbif(dbif) new_map = check_new_map_dataset(name, layer, type, overwrite, dbif) diff --git a/python/grass/temporal/temporal_algebra.py b/python/grass/temporal/temporal_algebra.py index 4d48b7119b0..3736a15315a 100644 --- a/python/grass/temporal/temporal_algebra.py +++ b/python/grass/temporal/temporal_algebra.py @@ -2143,7 +2143,6 @@ def eval_global_var(self, gvar, maplist): :return: List of maps from maplist with added conditional boolean values. """ - boollist = [] # Loop over maps of input map list. for map_i in maplist: # Get dictionary with temporal variables for the map. @@ -2248,7 +2247,6 @@ def build_condition_list(self, tvarexpr, thenlist, topolist=["EQUAL"]): # self.msgr.fatal("Condition list is not complete. Elements missing") for iter in range(len(tvarexpr)): expr = tvarexpr[iter] - operator = tvarexpr[iter + 1] relexpr = tvarexpr[iter + 2] if all(issubclass(type(ele), list) for ele in [expr, relexpr]): resultlist = self.build_spatio_temporal_topology_list(expr, relexpr) diff --git a/python/grass/temporal/temporal_granularity.py b/python/grass/temporal/temporal_granularity.py index 3022c27ea77..c3920b8b00b 100644 --- a/python/grass/temporal/temporal_granularity.py +++ b/python/grass/temporal/temporal_granularity.py @@ -96,12 +96,12 @@ def check_granularity_string(granularity, temporal_type): return False try: - integer = int(num) + int(num) except: return False elif temporal_type == "relative": try: - integer = int(granularity) + int(granularity) except: return False else: diff --git a/python/grass/temporal/temporal_raster_base_algebra.py b/python/grass/temporal/temporal_raster_base_algebra.py index 8333fbb698e..76517db30cc 100644 --- a/python/grass/temporal/temporal_raster_base_algebra.py +++ b/python/grass/temporal/temporal_raster_base_algebra.py @@ -511,7 +511,6 @@ def operator_cmd_value( """ temporal_relations = map_i.get_temporal_relations() - spatial_relations = map_i.get_spatial_relations() # Build comandlist list with elements from related maps and given relation # operator. @@ -933,7 +932,7 @@ def p_statement_assign(self, t): map_i.insert(dbif) # Register map in result space time dataset. if self.dry_run is False: - success = resultstds.register_map(map_i, dbif) + resultstds.register_map(map_i, dbif) if self.dry_run is False: resultstds.update_from_registered_maps(dbif) @@ -1040,10 +1039,10 @@ def p_arith1_operation(self, t): break if count == 0: # Set map name. - name = map_new.get_id() + map_new.get_id() else: # Generate an intermediate map - name = self.generate_map_name() + self.generate_map_name() # Create r.mapcalc expression string for the operation. cmdstring = self.build_command_string( @@ -1208,10 +1207,10 @@ def p_arith2_operation(self, t): break if count == 0: # Set map name. - name = map_new.get_id() + map_new.get_id() else: # Generate an intermediate map - name = self.generate_map_name() + self.generate_map_name() # Create r.mapcalc expression string for the operation. cmdstring = self.build_command_string( @@ -2038,7 +2037,6 @@ def p_hash_operation(self, t): for obj in map_i.map_value: if isinstance(obj, GlobalTemporalVar): n_maps = obj.td - mapinput = map_i.get_id() # Create r.mapcalc expression string for the operation. cmdstring = "(%s)" % (n_maps) # Append module command. diff --git a/python/grass/temporal/temporal_vector_algebra.py b/python/grass/temporal/temporal_vector_algebra.py index 4a005a7baeb..551c69783f5 100644 --- a/python/grass/temporal/temporal_vector_algebra.py +++ b/python/grass/temporal/temporal_vector_algebra.py @@ -232,19 +232,6 @@ def build_spatio_temporal_topology_list( "STARTED", "FINISHED", ] - complementdict = { - "EQUAL": "EQUAL", - "FOLLOWS": "PRECEDES", - "PRECEDES": "FOLLOWS", - "OVERLAPS": "OVERLAPPED", - "OVERLAPPED": "OVERLAPS", - "DURING": "CONTAINS", - "CONTAINS": "DURING", - "STARTS": "STARTED", - "STARTED": "STARTS", - "FINISHES": "FINISHED", - "FINISHED": "FINISHES", - } resultdict = {} # Check if given temporal relation are valid. for topo in topolist: @@ -577,7 +564,7 @@ def p_statement_assign(self, t): # Register map in result space time dataset. if self.debug: print(map_i.get_temporal_extent_as_tuple()) - success = resultstds.register_map(map_i, dbif=dbif) + resultstds.register_map(map_i, dbif=dbif) resultstds.update_from_registered_maps(dbif) # Remove intermediate maps From ef72f2d719a08e87b36865681e10d37a25de4c05 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 24 Aug 2024 00:15:40 +0000 Subject: [PATCH 159/471] CI(deps): Update github/codeql-action action to v3.26.5 (#4222) --- .github/workflows/codeql-analysis.yml | 4 ++-- .github/workflows/python-code-quality.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 73fbe757909..17d40cdae7f 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -56,7 +56,7 @@ jobs: if: ${{ matrix.language == 'c-cpp' }} - name: Initialize CodeQL - uses: github/codeql-action/init@f0f3afee809481da311ca3a6ff1ff51d81dbeb24 # v3.26.4 + uses: github/codeql-action/init@2c779ab0d087cd7fe7b826087247c2c81f27bfa6 # v3.26.5 with: languages: ${{ matrix.language }} config-file: ./.github/codeql/codeql-config.yml @@ -81,6 +81,6 @@ jobs: run: .github/workflows/build_ubuntu-22.04.sh "${HOME}/install" - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@f0f3afee809481da311ca3a6ff1ff51d81dbeb24 # v3.26.4 + uses: github/codeql-action/analyze@2c779ab0d087cd7fe7b826087247c2c81f27bfa6 # v3.26.5 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index 5f8de26bc23..dd1256b1c1c 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -135,7 +135,7 @@ jobs: path: bandit.sarif - name: Upload SARIF File into Security Tab - uses: github/codeql-action/upload-sarif@f0f3afee809481da311ca3a6ff1ff51d81dbeb24 # v3.26.4 + uses: github/codeql-action/upload-sarif@2c779ab0d087cd7fe7b826087247c2c81f27bfa6 # v3.26.5 with: sarif_file: bandit.sarif From cbc418e51b2dd92ea765131481967c55fb9a202a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2024 02:09:41 +0000 Subject: [PATCH 160/471] CI(deps): Lock file maintenance (#4226) From 22b07acb524d29fe980f16f8105f613adea83926 Mon Sep 17 00:00:00 2001 From: Anna Petrasova Date: Mon, 26 Aug 2024 12:31:16 -0400 Subject: [PATCH 161/471] contributing: add PR template (#4207) Also exclude MD041 globally --- .../pull_request_template.md | 32 +++++++++++++++++++ .markdownlint.yml | 2 ++ 2 files changed, 34 insertions(+) create mode 100644 .github/PULL_REQUEST_TEMPLATE/pull_request_template.md diff --git a/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md b/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md new file mode 100644 index 00000000000..b9d45627ffc --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md @@ -0,0 +1,32 @@ +## Description + + +## Motivation and context + + + +## How has this been tested? + + +## Screenshots (if appropriate) + +## Types of changes + + +- [ ] Bug fix (non-breaking change which fixes an issue) +- [ ] New feature (non-breaking change which adds functionality) +- [ ] Breaking change (fix or feature that would cause existing +functionality to not work as before) + +## Checklist + + +- [ ] PR title provides summary of the changes and starts with one of the +[pre-defined prefixes](../../utils/release.yml) + + +- [ ] My code follows the [code style](../../doc/development/style_guide.md) +of this project. +- [ ] My change requires a change to the documentation. +- [ ] I have updated the documentation accordingly. +- [ ] I have added tests to cover my changes. diff --git a/.markdownlint.yml b/.markdownlint.yml index e14a39a2289..44c5b53c8be 100644 --- a/.markdownlint.yml +++ b/.markdownlint.yml @@ -4,3 +4,5 @@ default: true # Fix any fixable errors (depending on the markdownlint wrapper tool used) fix: true + +MD041: false # first-line-h1 From 18bbf4b9ef438d38a623ceef174b5cfe9756a1d0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2024 16:54:31 +0000 Subject: [PATCH 162/471] CI(deps): Lock file maintenance (#4227) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 58a8dd324c0..53be10c7de4 100644 --- a/flake.lock +++ b/flake.lock @@ -19,11 +19,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1724015816, - "narHash": "sha256-hVESnM7Eiz93+4DeiE0a1TwMeaeph1ytRJ5QtqxYRWg=", + "lastModified": 1724395761, + "narHash": "sha256-zRkDV/nbrnp3Y8oCADf5ETl1sDrdmAW6/bBVJ8EbIdQ=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "9aa35efbea27d320d0cdc5f922f0890812affb60", + "rev": "ae815cee91b417be55d43781eb4b73ae1ecc396c", "type": "github" }, "original": { From e0a028854621fbca67a2200d1b99bd52a71980d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Mon, 26 Aug 2024 23:36:18 -0400 Subject: [PATCH 163/471] init: Use pathlib Path.read_text for readfile in main executable (#4234) --- lib/init/grass.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/init/grass.py b/lib/init/grass.py index 2a9a585b92b..4726193b976 100755 --- a/lib/init/grass.py +++ b/lib/init/grass.py @@ -55,6 +55,7 @@ import unicodedata import argparse import json +from pathlib import Path # mechanism meant for debugging this script (only) @@ -167,10 +168,7 @@ def fatal(msg): def readfile(path): debug("Reading %s" % path) - f = open(path, "r") - s = f.read() - f.close() - return s + return Path(path).read_text() def writefile(path, s): From 4aa4abf43f69d23ecdea932f3e550b5479df428f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Mon, 26 Aug 2024 23:40:05 -0400 Subject: [PATCH 164/471] grass.temporal.core: Replace open file + read with pathlib.Path's read_text() (#4233) Reads the whole file using a context manager internally. --- python/grass/temporal/core.py | 74 ++++++++++++----------------------- 1 file changed, 24 insertions(+), 50 deletions(-) diff --git a/python/grass/temporal/core.py b/python/grass/temporal/core.py index 60479cf4e08..dc74bff151d 100644 --- a/python/grass/temporal/core.py +++ b/python/grass/temporal/core.py @@ -32,6 +32,7 @@ # import traceback import os +from pathlib import Path import grass.script as gs from grass.pygrass import messages @@ -813,7 +814,7 @@ def _create_temporal_database_views(dbif): :param dbif: The database interface to be used """ - template_path = get_sql_template_path() + template_path = Path(get_sql_template_path()) for sql_filename in ( "raster_views", @@ -823,9 +824,7 @@ def _create_temporal_database_views(dbif): "str3ds_views", "stvds_views", ): - sql_filepath = open( - os.path.join(template_path, sql_filename + ".sql"), "r" - ).read() + sql_filepath = (template_path / f"{sql_filename}.sql").read_text() dbif.execute_transaction(sql_filepath) @@ -839,34 +838,18 @@ def create_temporal_database(dbif): """ global tgis_backend, tgis_version, tgis_db_version, tgis_database_string - template_path = get_sql_template_path() + template_path = Path(get_sql_template_path()) msgr = get_tgis_message_interface() # Read all SQL scripts and templates - map_tables_template_sql = open( - os.path.join(template_path, "map_tables_template.sql"), "r" - ).read() - raster_metadata_sql = open( - os.path.join(get_sql_template_path(), "raster_metadata_table.sql"), "r" - ).read() - raster3d_metadata_sql = open( - os.path.join(template_path, "raster3d_metadata_table.sql"), "r" - ).read() - vector_metadata_sql = open( - os.path.join(template_path, "vector_metadata_table.sql"), "r" - ).read() - stds_tables_template_sql = open( - os.path.join(template_path, "stds_tables_template.sql"), "r" - ).read() - strds_metadata_sql = open( - os.path.join(template_path, "strds_metadata_table.sql"), "r" - ).read() - str3ds_metadata_sql = open( - os.path.join(template_path, "str3ds_metadata_table.sql"), "r" - ).read() - stvds_metadata_sql = open( - os.path.join(template_path, "stvds_metadata_table.sql"), "r" - ).read() + map_tables_template_sql = (template_path / "map_tables_template.sql").read_text() + raster_metadata_sql = (template_path / "raster_metadata_table.sql").read_text() + raster3d_metadata_sql = (template_path / "raster3d_metadata_table.sql").read_text() + vector_metadata_sql = (template_path / "vector_metadata_table.sql").read_text() + stds_tables_template_sql = (template_path / "stds_tables_template.sql").read_text() + strds_metadata_sql = (template_path / "strds_metadata_table.sql").read_text() + str3ds_metadata_sql = (template_path / "str3ds_metadata_table.sql").read_text() + stvds_metadata_sql = (template_path / "stvds_metadata_table.sql").read_text() # Create the raster, raster3d and vector tables SQL statements raster_tables_sql = map_tables_template_sql.replace("GRASS_MAP", "raster") @@ -898,21 +881,15 @@ def create_temporal_database(dbif): # Set up the trigger that takes care of # the correct deletion of entries across the different tables - delete_trigger_sql = open( - os.path.join(template_path, "sqlite3_delete_trigger.sql"), "r" - ).read() - indexes_sql = open( - os.path.join(template_path, "sqlite3_indexes.sql"), "r" - ).read() + delete_trigger_sql = (template_path / "sqlite3_delete_trigger.sql").read_text() + indexes_sql = (template_path / "sqlite3_indexes.sql").read_text() else: # Set up the trigger that takes care of # the correct deletion of entries across the different tables - delete_trigger_sql = open( - os.path.join(template_path, "postgresql_delete_trigger.sql"), "r" - ).read() - indexes_sql = open( - os.path.join(template_path, "postgresql_indexes.sql"), "r" - ).read() + delete_trigger_sql = ( + template_path / "postgresql_delete_trigger.sql" + ).read_text() + indexes_sql = (template_path / "postgresql_indexes.sql").read_text() # Connect now to the database if dbif.connected is not True: @@ -989,22 +966,19 @@ def upgrade_temporal_database(dbif): dbif.close() return - template_path = get_sql_template_path() + template_path = Path(get_sql_template_path()) try: - upgrade_db_sql = open( - os.path.join( - template_path, - "upgrade_db_%s_to_%s.sql" % (upgrade_db_from, tgis_db_version), - ), - "r", - ).read() + upgrade_db_sql = ( + template_path + / "upgrade_db_{}_to_{}.sql".format(upgrade_db_from, tgis_db_version) + ).read_text() except FileNotFoundError: msgr.fatal( _("Unsupported TGIS DB upgrade scenario: from version %s to %s") % (upgrade_db_from, tgis_db_version) ) - drop_views_sql = open(os.path.join(template_path, "drop_views.sql"), "r").read() + drop_views_sql = (template_path / "drop_views.sql").read_text() msgr.message( _("Upgrading temporal database <%s> from version %s to %s...") From acfbdd891145c90de10050cfc8222be04070ac91 Mon Sep 17 00:00:00 2001 From: Anna Petrasova Date: Tue, 27 Aug 2024 09:03:07 -0400 Subject: [PATCH 165/471] wxGUI: fix adding and removing from history for new mapset (#4228) Bug introduced in 02e45e9 when trying to avoid calling complete tree refresh every time. --- gui/wxpython/history/tree.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/gui/wxpython/history/tree.py b/gui/wxpython/history/tree.py index b616ef44698..929f2d9f53b 100644 --- a/gui/wxpython/history/tree.py +++ b/gui/wxpython/history/tree.py @@ -505,6 +505,7 @@ def InsertCommand(self, entry): }, ) self._model.SortChildren(self._model.root) + self.RefreshItems() # Populate today's node by executed command command_node = self._populateDayItem(today_node, entry) @@ -621,9 +622,9 @@ def OnRemoveCmd(self, event): selected_day = selected_command.parent if selected_day and len(selected_day.children) == 0: self._model.RemoveNode(selected_day) - - # Reload day node - self._reloadNode(selected_day) + self._reloadNode(self._model.root) + else: + self._reloadNode(selected_day) self.showNotification.emit(message=_("<{}> removed").format(command)) def OnItemSelected(self, node): From 6c5190e20016da5862b133dc0bbba64ab69a91f2 Mon Sep 17 00:00:00 2001 From: Paulo van Breugel Date: Tue, 27 Aug 2024 20:34:35 +0200 Subject: [PATCH 166/471] wxGUI: Update link to the Natural Earth sample dataset (#4223) Update locdownload.py with link to latest natural earth sample dataset --- gui/wxpython/startup/locdownload.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gui/wxpython/startup/locdownload.py b/gui/wxpython/startup/locdownload.py index 4592103a0f9..f0a1ab13fd2 100644 --- a/gui/wxpython/startup/locdownload.py +++ b/gui/wxpython/startup/locdownload.py @@ -73,8 +73,8 @@ }, { "label": "Natural Earth Dataset in WGS84", - "url": "https://zenodo.org/record/3968936/files/natural-earth-dataset.tar.gz", - "size": "207 MB", + "url": "https://zenodo.org/records/13370131/files/natural_earth_dataset.zip", + "size": "121.3 MB", "epsg": "4326", "license": "ODC Public Domain Dedication and License 1.0", "maintainer": "Brendan Harmon (brendan.harmon@gmail.com)", From 42684b32c466cbace161ba70fd15faed9146437e Mon Sep 17 00:00:00 2001 From: Markus Neteler Date: Wed, 28 Aug 2024 01:26:32 +0200 Subject: [PATCH 167/471] docs: add guidelines for writing a meaningful pull request (#4197) * docs: add guidelines for writing a meaningful pull request A well-written pull request (PR) is critical because it not only improves collaboration and efficiency within a project but also helps newcomers to contribute more easily. Fixes #4194 * shorten intro text; add link to release.yml * reduce 'PR Content' description * add link to PR template --------- Co-authored-by: Veronica Andreo Co-authored-by: Anna Petrasova --- doc/development/github_guide.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/doc/development/github_guide.md b/doc/development/github_guide.md index 0346b81c5ff..c28224c9986 100644 --- a/doc/development/github_guide.md +++ b/doc/development/github_guide.md @@ -122,6 +122,25 @@ a PR. Alternatively, you can explore GitHub CLI tool (_gh_) which allows you to do `git push` and create a PR in one step with `gh pr create -fw`. +## Guidelines for writing a meaningful pull request + +A well-written pull request clearly conveys the purpose and impact of the +proposed changes. + +### PR Title + +The title should be descriptive and clearly summarize the main purpose or change +in the pull request. Start the title with the tool name or a +[keyword](https://github.com/OSGeo/grass/blob/main/utils/release.yml) (e.g.: +`tool name: Add functionality Y for Z`. Keep it short, i.e. aim for concise titles, +typically under 50-60 characters. + +### PR Content + +A pull request requires an abstract, change details, and more. When you create +the new PR, you are presented with a [template](https://github.com/OSGeo/grass/blob/main/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md) +to help standardize the content. + ## After creating a PR GRASS GIS maintainers will now review your PR. From 1cdf3978b110969ae0bbc7546fa142c0a6c7640b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 28 Aug 2024 15:42:41 +0000 Subject: [PATCH 168/471] CI(deps): Update super-linter/super-linter action to v7.1.0 (#4240) --- .github/workflows/super-linter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/super-linter.yml b/.github/workflows/super-linter.yml index 745c1bd2b89..44da8d9acb7 100644 --- a/.github/workflows/super-linter.yml +++ b/.github/workflows/super-linter.yml @@ -31,7 +31,7 @@ jobs: # list of files that changed across commits fetch-depth: 0 - name: Lint code base - uses: super-linter/super-linter/slim@02a1172d274f021e4c70f66e23f1085eadd1064b # v7.0.0 + uses: super-linter/super-linter/slim@b92721f792f381cedc002ecdbb9847a15ece5bb8 # v7.1.0 env: DEFAULT_BRANCH: main # To report GitHub Actions status checks From 931a11cb26fa4b2cca088f440d280875ef88e87b Mon Sep 17 00:00:00 2001 From: Corey White Date: Wed, 28 Aug 2024 15:46:24 -0400 Subject: [PATCH 169/471] grass.jupyter: Ensure width and height in InteractiveMap are integers (#4221) --- python/grass/jupyter/interactivemap.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python/grass/jupyter/interactivemap.py b/python/grass/jupyter/interactivemap.py index b6a5ccfb3e8..085c2843fe7 100644 --- a/python/grass/jupyter/interactivemap.py +++ b/python/grass/jupyter/interactivemap.py @@ -295,8 +295,8 @@ def _import_ipyleaflet(error): import xyzservices # pylint: disable=import-outside-toplevel # Store height and width - self.width = width - self.height = height + self.width = int(width) + self.height = int(height) self._controllers = {} # Store vector and raster name @@ -745,7 +745,7 @@ class InteractiveQueryController: _ipywidgets: The ipywidgets module. raster_name: The name of the raster layer. vector_name: The name of the vector layer. - width: The width of the map. + width: The width of the map as an int. query_control: The query control. """ From 3afa11d44d9821de76a2b319cc1c7d086c4c5375 Mon Sep 17 00:00:00 2001 From: ymdatta Date: Thu, 29 Aug 2024 01:30:24 -0400 Subject: [PATCH 170/471] r3.showdspf: fix null pointer dereference when opening database file (#4245) r3.showdspf: fix null pointer dereference In the current execution, when we face an error while trying to open a database file, as part of error log we are dereferencing a NULL file pointer (which is returend when we try to open a file). Ideally we should be printing the name of the database file rather than the file pointer. This fixes the problem of null pointer dereference. Found via cppcheck tool. Signed-off-by: Mohan Yelugoti --- raster3d/r3.showdspf/main_ogl.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/raster3d/r3.showdspf/main_ogl.c b/raster3d/r3.showdspf/main_ogl.c index 90bf8e232a3..55b3097e2cf 100644 --- a/raster3d/r3.showdspf/main_ogl.c +++ b/raster3d/r3.showdspf/main_ogl.c @@ -202,8 +202,7 @@ int main(int argc, char **argv) G_fatal_error(buff); } if ((Headfax.dspfinfp = G_fopen_old(buff, dsp, mapset)) == NULL) { - fprintf(stderr, "Unable to open <%s> for reading\n", - Headfax.dspfinfp); + fprintf(stderr, "Unable to open <%s> for reading\n", dsp); exit(EXIT_FAILURE); } From b61c61d1a5c7ef07891a4819af7eadf7024228bd Mon Sep 17 00:00:00 2001 From: ymdatta Date: Thu, 29 Aug 2024 08:12:16 -0400 Subject: [PATCH 171/471] r3.showdspf: fix accessing uninitialized pointer (#4246) color2 pointer is not initialized and it is assigned a value depending on a conditional. Potentially, in an execution path if conditional fails, we would be accessing an uninitialized pointer, whose behavior is undefined. Initialize the pointer to NULL to avoid this. Signed-off-by: Mohan Yelugoti --- raster3d/r3.showdspf/get_color_ogl.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/raster3d/r3.showdspf/get_color_ogl.c b/raster3d/r3.showdspf/get_color_ogl.c index af189d6afd1..865f16bb381 100644 --- a/raster3d/r3.showdspf/get_color_ogl.c +++ b/raster3d/r3.showdspf/get_color_ogl.c @@ -79,7 +79,7 @@ short color[3]; { int curr; float cat1, cat2; - short *color1, *color2; + short *color1 = NULL, *color2 = NULL; float delta; /*DEBUG @@ -123,9 +123,11 @@ short color[3]; } /* for any thresholds greater than last color table entry, use last entry * */ - color[0] = color2[0]; - color[1] = color2[1]; - color[2] = color2[2]; + if (color2) { + color[0] = color2[0]; + color[1] = color2[1]; + color[2] = color2[2]; + } } /****************************************************************************** From c6b1976c91362646d68c386b2c1949a222c03c8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Thu, 29 Aug 2024 08:55:54 -0400 Subject: [PATCH 172/471] init: Use pathlib Path.write_text for writefile in the main executable (#4235) --- lib/init/grass.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/init/grass.py b/lib/init/grass.py index 4726193b976..87a69f5ef65 100755 --- a/lib/init/grass.py +++ b/lib/init/grass.py @@ -173,9 +173,7 @@ def readfile(path): def writefile(path, s): debug("Writing %s" % path) - f = open(path, "w") - f.write(s) - f.close() + Path(path).write_text(s) def call(cmd, **kwargs): From 275cc4b4e4cb0a3def52aa80ba916a37fbe1c43f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 29 Aug 2024 13:29:24 +0000 Subject: [PATCH 173/471] CI(deps): Update github/codeql-action action to v3.26.6 (#4248) --- .github/workflows/codeql-analysis.yml | 4 ++-- .github/workflows/python-code-quality.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 17d40cdae7f..8adfe4a0493 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -56,7 +56,7 @@ jobs: if: ${{ matrix.language == 'c-cpp' }} - name: Initialize CodeQL - uses: github/codeql-action/init@2c779ab0d087cd7fe7b826087247c2c81f27bfa6 # v3.26.5 + uses: github/codeql-action/init@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6 with: languages: ${{ matrix.language }} config-file: ./.github/codeql/codeql-config.yml @@ -81,6 +81,6 @@ jobs: run: .github/workflows/build_ubuntu-22.04.sh "${HOME}/install" - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@2c779ab0d087cd7fe7b826087247c2c81f27bfa6 # v3.26.5 + uses: github/codeql-action/analyze@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index dd1256b1c1c..22c61a6bc66 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -135,7 +135,7 @@ jobs: path: bandit.sarif - name: Upload SARIF File into Security Tab - uses: github/codeql-action/upload-sarif@2c779ab0d087cd7fe7b826087247c2c81f27bfa6 # v3.26.5 + uses: github/codeql-action/upload-sarif@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6 with: sarif_file: bandit.sarif From 04b5af77e1a25300d793e116e2a5724d7a320c41 Mon Sep 17 00:00:00 2001 From: Anna Petrasova Date: Thu, 29 Aug 2024 10:09:47 -0400 Subject: [PATCH 174/471] grass.jupyter: Clarify InteractiveMap documentation (#4243) Clarify using different backends Fix icons --- doc/notebooks/jupyter_tutorial.ipynb | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/doc/notebooks/jupyter_tutorial.ipynb b/doc/notebooks/jupyter_tutorial.ipynb index 66bbabef1b3..71852c226e6 100644 --- a/doc/notebooks/jupyter_tutorial.ipynb +++ b/doc/notebooks/jupyter_tutorial.ipynb @@ -211,18 +211,19 @@ "source": [ "## Interactive Map Display\n", "\n", - "The `InteractiveMap` class displays *GRASS GIS* rasters and vectors with [*folium*](http://python-visualization.github.io/folium/) or [*ipyleaflet*](https://ipyleaflet.readthedocs.io/en/latest/)." + "The `InteractiveMap` class displays *GRASS GIS* rasters and vectors with [*folium*](http://python-visualization.github.io/folium/) or [*ipyleaflet*](https://ipyleaflet.readthedocs.io/en/latest/).\n", + "Backend is detected automatically; when both libraries are available, ipyleaflet is used. The backend can be also selected with the `map_backend` parameter." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "When using ipyleaflet, the map display includes a button for querying data (ℹ️), a button for displaying and editing computational region (◻️), and a button for drawing simple vector geometries (🖉) that can be saved as a GRASS native vector map.\n", + "**Only when using ipyleaflet**, the map display includes a button for querying data ( ℹ ), a button for displaying and editing computational region (□), and a button for drawing simple vector geometries (🖊) that can be saved as a GRASS native vector map.\n", "\n", - "1. Query raster/vector layers: Click the **info** button (ℹ️) to enable query mode, then select a point on the map to retrieve information. Toggle the button off when finished.\n", - "2. Draw and save geometries: Click the **pencil** button (🖉) to draw shapes on the map, name the vector map, and save it. The geometry will be added as a new layer and the drawing tool will close automatically.\n", - "3. View and edit the computational region: Click the **computational region** button (◻️) to display and optionally adjust the region. Update it by clicking **Update region**, then toggle the button off when done.\n" + "1. Query raster/vector layers: Click the **info** button ( ℹ ) to enable query mode, then select a point on the map to retrieve information. Toggle the button off when finished.\n", + "2. View and edit the computational region: Click the **computational region** button (□) to display and optionally adjust the region. Update it by clicking **Update region**, then toggle the button off when done.\n", + "3. Draw and save geometries: Click the **pencil** button (🖊) to draw shapes on the map, name the vector map, and save it. The geometry will be added as a new layer and the drawing tool will close automatically.\n" ] }, { From 591dc1959c1aa586ef1d2b3fa97a84847e098014 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 29 Aug 2024 12:29:50 -0400 Subject: [PATCH 175/471] CI(deps): Update actions/setup-python action to v5.2.0 (#4250) --- .github/workflows/additional_checks.yml | 2 +- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/create_release_draft.yml | 2 +- .github/workflows/pytest.yml | 2 +- .github/workflows/python-code-quality.yml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/additional_checks.yml b/.github/workflows/additional_checks.yml index 74602e7910b..d25cbb49194 100644 --- a/.github/workflows/additional_checks.yml +++ b/.github/workflows/additional_checks.yml @@ -43,7 +43,7 @@ jobs: exclude: mswindows .*\.bat .*/testsuite/data/.* - name: Set up Python - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 + uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 with: python-version: '3.10' diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 8adfe4a0493..05d4581b689 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -42,7 +42,7 @@ jobs: - name: Checkout repository uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Set up Python - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 + uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 with: python-version: '3.x' - name: Install non-Python dependencies diff --git a/.github/workflows/create_release_draft.yml b/.github/workflows/create_release_draft.yml index d43e1248555..09da8ad1317 100644 --- a/.github/workflows/create_release_draft.yml +++ b/.github/workflows/create_release_draft.yml @@ -35,7 +35,7 @@ jobs: ref: ${{ github.ref }} fetch-depth: 0 - name: Set up Python - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 + uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 with: python-version: '3.11' - name: Create output directory diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index c0625635980..5f6a3cab72d 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -35,7 +35,7 @@ jobs: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Set up Python - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 + uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 with: python-version: ${{ matrix.python-version }} cache: pip diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index 22c61a6bc66..54942ae2f17 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -57,7 +57,7 @@ jobs: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Set up Python - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 + uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 with: python-version: ${{ env.PYTHON_VERSION }} cache: pip From eeb0e0295373294c9ea0d023f01578f9defb05cf Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 29 Aug 2024 17:18:15 +0000 Subject: [PATCH 176/471] CI(deps): Update ruff to v0.6.3 (#4252) --- .github/workflows/python-code-quality.yml | 2 +- .pre-commit-config.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index 54942ae2f17..e44fceab07d 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -36,7 +36,7 @@ jobs: # renovate: datasource=pypi depName=bandit BANDIT_VERSION: "1.7.9" # renovate: datasource=pypi depName=ruff - RUFF_VERSION: "0.6.2" + RUFF_VERSION: "0.6.3" runs-on: ${{ matrix.os }} permissions: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d20d238f908..f12fde12473 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -37,7 +37,7 @@ repos: ) - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.6.2 + rev: v0.6.3 hooks: # Run the linter. - id: ruff From 7df0e7f69965abea47de16666b524ee79c958a28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Thu, 29 Aug 2024 18:16:20 -0400 Subject: [PATCH 177/471] grass.temporal.stds_import: Use pathlib Path.read_text and Path.write_text for proj string (#4236) --- python/grass/temporal/stds_import.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/python/grass/temporal/stds_import.py b/python/grass/temporal/stds_import.py index e8684d9ecb4..03810e49b89 100644 --- a/python/grass/temporal/stds_import.py +++ b/python/grass/temporal/stds_import.py @@ -32,6 +32,7 @@ import os import os.path import tarfile +from pathlib import Path import grass.script as gs from grass.exceptions import CalledModuleError @@ -302,16 +303,12 @@ def import_stds( # from other programs than g.proj into # new line format so that the grass file comparison function # can be used to compare the projections - proj_name_tmp = temp_name + "_in_projection" - proj_file = open(proj_name, "r") - proj_content = proj_file.read() + proj_content = Path(proj_name).read_text() proj_content = proj_content.replace(" +", "\n+") proj_content = proj_content.replace("\t+", "\n+") - proj_file.close() - proj_file = open(proj_name_tmp, "w") - proj_file.write(proj_content) - proj_file.close() + proj_name_tmp = f"{temp_name}_in_projection" + Path(proj_name_tmp).write_text(proj_content) p = gs.start_command("g.proj", flags="j", stdout=temp_file) p.communicate() @@ -336,7 +333,7 @@ def import_stds( old_env = gs.gisenv() if location: try: - proj4_string = open(proj_file_name, "r").read() + proj4_string = Path(proj_file_name).read_text() gs.create_location( dbase=old_env["GISDBASE"], location=location, proj4=proj4_string ) From cdb62825a3a4d0ac042848b24ed121d58643f524 Mon Sep 17 00:00:00 2001 From: ymdatta Date: Thu, 29 Aug 2024 23:08:54 -0400 Subject: [PATCH 178/471] v.external.out: Check for valid array before passing it to qsort (#4251) Currently, if 'HAVE_OGR' macro is defined, as part of execution, we sort all formats by name using qsort. But, array containing all formats is assigned based on a conditional and if the conditional fails, it can be NULL. Behavior of qsort when a NULL array is provided is undefined. To avoid getting into that situation, check if the array is NULL before performing qsort on it. Signed-off-by: Mohan Yelugoti --- vector/v.external.out/list.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vector/v.external.out/list.c b/vector/v.external.out/list.c index 56ec201a3e9..708d2795e76 100644 --- a/vector/v.external.out/list.c +++ b/vector/v.external.out/list.c @@ -53,7 +53,8 @@ char **format_list(int *count, size_t *len) } /* order formats by name */ - qsort(list, *count, sizeof(char *), cmp); + if (list) + qsort(list, *count, sizeof(char *), cmp); #endif #if defined HAVE_POSTGRES && !defined HAVE_OGR list = G_realloc(list, ((*count) + 1) * sizeof(char *)); From 52964a530f9602ad7e62e9832da45dfc83238f86 Mon Sep 17 00:00:00 2001 From: Anna Petrasova Date: Fri, 30 Aug 2024 23:09:48 -0400 Subject: [PATCH 179/471] GUI: fix verbose checkbox behavior in tool dialogs (#4257) --- gui/wxpython/gui_core/forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gui/wxpython/gui_core/forms.py b/gui/wxpython/gui_core/forms.py index 4ff6f0ee0ee..f9829cd9d9b 100644 --- a/gui/wxpython/gui_core/forms.py +++ b/gui/wxpython/gui_core/forms.py @@ -2766,7 +2766,7 @@ def OnVerbosity(self, event): if event.GetId() == verbose.GetId() and quiet.IsChecked(): quiet.SetValue(False) self.task.get_flag("quiet")["value"] = False - elif verbose.IsChecked(): + elif event.GetId() == quiet.GetId() and verbose.IsChecked(): verbose.SetValue(False) self.task.get_flag("verbose")["value"] = False From 95807d0ae8aeaaa039143f8bd855e4783ff9e4f6 Mon Sep 17 00:00:00 2001 From: HamishB Date: Mon, 2 Sep 2024 03:16:07 -0400 Subject: [PATCH 180/471] lib/display: D_show_conversions() %.1f to %g where values likely to be smaller than 0.0 (#4263) Display pixels or raster region rows,cols are likely be on the order of 500, while projected map units are often in the millions, so pixels/meter factor can end up as a very small number. This patch edits an unused in the production code debug helper function to not round that division into a confusing 0.0, as well as some whitespace tweaks to make it more readable. --- lib/display/cnversions.c | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/lib/display/cnversions.c b/lib/display/cnversions.c index 63c55e292d7..001fcdb1aaf 100644 --- a/lib/display/cnversions.c +++ b/lib/display/cnversions.c @@ -110,27 +110,18 @@ void D_show_conversions(void) D.east, D.south, D.north); fprintf(stderr, " A_w %10.1f A_e %10.1f A_s %10.1f A_n %10.1f\n", A.west, A.east, A.south, A.north); - fprintf(stderr, " U_w %10.1f U_e %10.1f U_s %10.1f U_n %10.1f\n", U.west, - U.east, U.south, U.north); - - fprintf(stderr, - " D_x %10.1f D_y %10.1f\n" - "\n", - D.size.x, D.size.y); - fprintf(stderr, - " A_x %10.1f A_y %10.1f\n" - "\n", - A.size.x, A.size.y); - fprintf(stderr, - " U_x %10.1f U_y %10.1f\n" - "\n", - U.size.x, U.size.y); - - fprintf(stderr, " D_to_A_conv.x %10.1f D_to_A_conv.y %10.1f \n", + fprintf(stderr, " U_w %10.1f U_e %10.1f U_s %10.1f U_n %10.1f\n\n", + U.west, U.east, U.south, U.north); + + fprintf(stderr, " D_x %10.1f D_y %10.1f\n", D.size.x, D.size.y); + fprintf(stderr, " A_x %10.1f A_y %10.1f\n", A.size.x, A.size.y); + fprintf(stderr, " U_x %10.1f U_y %10.1f\n\n", U.size.x, U.size.y); + + fprintf(stderr, " D_to_A_conv.x %10.1f D_to_A_conv.y %10.1f\n", D_to_A_conv.x, D_to_A_conv.y); - fprintf(stderr, " A_to_U_conv.x %10.1f A_to_U_conv.y %10.1f \n", + fprintf(stderr, " A_to_U_conv.x %10.1f A_to_U_conv.y %10.1f\n", A_to_U_conv.x, A_to_U_conv.y); - fprintf(stderr, " U_to_D_conv.x %10.1f U_to_D_conv.y %10.1f \n", + fprintf(stderr, " U_to_D_conv.x %10.1g U_to_D_conv.y %10.1g\n", U_to_D_conv.x, U_to_D_conv.y); } From 06612120879cca0a1a3d2401ddd9f5e9471a4bf3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 07:22:17 -0400 Subject: [PATCH 181/471] CI(deps): Lock file maintenance (#4266) --- flake.lock | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/flake.lock b/flake.lock index 53be10c7de4..257ec2317b7 100644 --- a/flake.lock +++ b/flake.lock @@ -5,11 +5,11 @@ "nixpkgs-lib": "nixpkgs-lib" }, "locked": { - "lastModified": 1722555600, - "narHash": "sha256-XOQkdLafnb/p9ij77byFQjDf5m5QYl9b2REiVClC+x4=", + "lastModified": 1725234343, + "narHash": "sha256-+ebgonl3NbiKD2UD0x4BszCZQ6sTfL4xioaM49o5B3Y=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "8471fe90ad337a8074e957b69ca4d0089218391d", + "rev": "567b938d64d4b4112ee253b9274472dc3a346eb6", "type": "github" }, "original": { @@ -19,11 +19,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1724395761, - "narHash": "sha256-zRkDV/nbrnp3Y8oCADf5ETl1sDrdmAW6/bBVJ8EbIdQ=", + "lastModified": 1725194671, + "narHash": "sha256-tLGCFEFTB5TaOKkpfw3iYT9dnk4awTP/q4w+ROpMfuw=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "ae815cee91b417be55d43781eb4b73ae1ecc396c", + "rev": "b833ff01a0d694b910daca6e2ff4a3f26dee478c", "type": "github" }, "original": { @@ -35,14 +35,14 @@ }, "nixpkgs-lib": { "locked": { - "lastModified": 1722555339, - "narHash": "sha256-uFf2QeW7eAHlYXuDktm9c25OxOyCoUOQmh5SZ9amE5Q=", + "lastModified": 1725233747, + "narHash": "sha256-Ss8QWLXdr2JCBPcYChJhz4xJm+h/xjl4G0c0XlP6a74=", "type": "tarball", - "url": "https://github.com/NixOS/nixpkgs/archive/a5d394176e64ab29c852d03346c1fc9b0b7d33eb.tar.gz" + "url": "https://github.com/NixOS/nixpkgs/archive/356624c12086a18f2ea2825fed34523d60ccc4e3.tar.gz" }, "original": { "type": "tarball", - "url": "https://github.com/NixOS/nixpkgs/archive/a5d394176e64ab29c852d03346c1fc9b0b7d33eb.tar.gz" + "url": "https://github.com/NixOS/nixpkgs/archive/356624c12086a18f2ea2825fed34523d60ccc4e3.tar.gz" } }, "root": { From fa37eb2ca38d5b1c9994168869217f2092b5736c Mon Sep 17 00:00:00 2001 From: Markus Neteler Date: Mon, 2 Sep 2024 19:27:46 +0200 Subject: [PATCH 182/471] docker: fix missing libraries in ubuntu wxgui Dockerfile (#4268) * docker: fix missing library in ubuntu wxgui Dockerfile Add `libgtk-3-dev` to fix ``` ... checking for GTK+ - version >= 3.0.0... Package gtk+-3.0 was not found in the pkg-config search path. Perhaps you should add the directory containing `gtk+-3.0.pc' to the PKG_CONFIG_PATH environment variable No package 'gtk+-3.0' found ``` * also install 'python3-wxgtk4.0' --- docker/ubuntu_wxgui/Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docker/ubuntu_wxgui/Dockerfile b/docker/ubuntu_wxgui/Dockerfile index dd5f26d7ac0..21426eca886 100644 --- a/docker/ubuntu_wxgui/Dockerfile +++ b/docker/ubuntu_wxgui/Dockerfile @@ -54,6 +54,7 @@ RUN apt-get update && apt-get upgrade -y && \ libglu1-mesa-dev \ libgsl0-dev \ libgtk-3-0 \ + libgtk-3-dev \ libjpeg-dev \ libjsoncpp-dev \ libnetcdf-dev \ @@ -93,6 +94,7 @@ RUN apt-get update && apt-get upgrade -y && \ python3-ply \ python3-setuptools \ python3-venv \ + python3-wxgtk4.0 \ software-properties-common \ sqlite3 \ subversion \ From 42b47f295a47f6dbe609fe464d7ea4bafc2650fc Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 15:29:09 -0400 Subject: [PATCH 183/471] CI(deps): Update actions/upload-artifact action to v4.4.0 (#4259) --- .github/actions/create-upload-suggestions/action.yml | 4 ++-- .github/workflows/macos.yml | 2 +- .github/workflows/osgeo4w.yml | 2 +- .github/workflows/pytest.yml | 2 +- .github/workflows/python-code-quality.yml | 4 ++-- .github/workflows/ubuntu.yml | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/actions/create-upload-suggestions/action.yml b/.github/actions/create-upload-suggestions/action.yml index 6a4cf1201dd..2493fba2573 100644 --- a/.github/actions/create-upload-suggestions/action.yml +++ b/.github/actions/create-upload-suggestions/action.yml @@ -177,7 +177,7 @@ runs: echo "diff-file-name=${INPUT_DIFF_FILE_NAME}" >> "${GITHUB_OUTPUT}" env: INPUT_DIFF_FILE_NAME: ${{ steps.tool-name-safe.outputs.diff-file-name }} - - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 + - uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 id: upload-diff if: >- ${{ (steps.files_changed.outputs.files_changed == 'true') && @@ -200,7 +200,7 @@ runs: echo 'Suggestions can only be added near to lines changed in this PR.' echo 'If any fixes can be added as code suggestions, they will be added shortly from another workflow.' } >> "${GITHUB_STEP_SUMMARY}" - - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 + - uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 id: upload-changes if: >- ${{ always() && diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 0a90a022d98..eb99b6ff7a7 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -100,7 +100,7 @@ jobs: nc_spm_full_v2alpha2.tar.gz" - name: Make HTML test report available if: ${{ !cancelled() }} - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 + uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 with: name: testreport-macOS path: testreport diff --git a/.github/workflows/osgeo4w.yml b/.github/workflows/osgeo4w.yml index 7152265fd3d..2f17427b05d 100644 --- a/.github/workflows/osgeo4w.yml +++ b/.github/workflows/osgeo4w.yml @@ -85,7 +85,7 @@ jobs: - name: Make HTML test report available if: ${{ always() }} - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 + uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 with: name: testreport-${{ matrix.os }} path: testreport diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 5f6a3cab72d..8e3b2286b1c 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -116,7 +116,7 @@ jobs: token: ${{ secrets.CODECOV_TOKEN }} - name: Make python-only code coverage test report available if: ${{ !cancelled() }} - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 + uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 with: name: python-codecoverage-report-${{ matrix.os }}-${{ matrix.python-version }} path: coverage_html_report diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index e44fceab07d..caa530e3b39 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -129,7 +129,7 @@ jobs: bandit -c pyproject.toml -iii -r . -f sarif -o bandit.sarif --exit-zero - name: Upload Bandit Scan Results - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 + uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 with: name: bandit.sarif path: bandit.sarif @@ -201,7 +201,7 @@ jobs: cp -rp dist.$ARCH/docs/html/libpython sphinx-grass - name: Make Sphinx documentation available - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 + uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 with: name: sphinx-grass path: sphinx-grass diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 626d3d26676..57b0821eec4 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -149,7 +149,7 @@ jobs: - name: Make HTML test report available if: ${{ always() }} - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 + uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 with: name: testreport-${{ matrix.os }}-${{ matrix.config }}-${{ matrix.extra-include }} path: testreport From 2cbd98d1d0422adbc5b52aa5689bfda649cc021d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 12:45:43 +0000 Subject: [PATCH 184/471] CI(deps): Update peter-evans/create-pull-request action to v7 (#4269) --- .github/workflows/periodic_update.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/periodic_update.yml b/.github/workflows/periodic_update.yml index 2b6e7b48f34..842f7404d69 100644 --- a/.github/workflows/periodic_update.yml +++ b/.github/workflows/periodic_update.yml @@ -33,7 +33,7 @@ jobs: run: git status --ignored - name: Create Pull Request id: cpr - uses: peter-evans/create-pull-request@c5a7806660adbe173f04e3e038b0ccdcd758773c # v6.1.0 + uses: peter-evans/create-pull-request@4320041ed380b20e97d388d56a7fb4f9b8c20e79 # v7.0.0 with: commit-message: "config.guess + config.sub: updated from http://git.savannah.gnu.org/cgit/config.git/plain/" branch: periodic/update-configure From 9222822ae60ae327d09c07ee9c40053762e4b7a3 Mon Sep 17 00:00:00 2001 From: Nicklas Larsson Date: Wed, 4 Sep 2024 00:03:59 +0200 Subject: [PATCH 185/471] lib: deprecate G_snprintf() and replace with C99 snprintf() (#4189) * lib: deprecate G_snprintf() and replace with C99 snprintf() Beginning with UCRT in Visual Studio 2015 and Windows 10, the behaviour of snprintf (and vsnprintf) is now C99 standard conformant. Thus, the output is now always null-terminated. The justification for a internal fix for that is no longer needed. * r3.out.bin: fix -Werror=sizeof-pointer-memaccess GCC warning * cast explicitly * add missing #include --- imagery/i.topo.corr/main.c | 4 ++-- lib/gis/color_rules.c | 11 ++++++----- lib/gis/parser.c | 2 +- lib/gis/parser_wps.c | 4 ++-- lib/gis/snprintf.c | 12 ++++-------- lib/gis/timestamp.c | 26 ++++++++++++++------------ lib/imagery/loc_info.c | 9 +++++---- lib/init/clean_temp.c | 5 +++-- lib/temporal/lib/default_name.c | 7 ++++--- raster/r.colors/edit_colors.c | 5 +++-- raster/r.external/main.c | 3 ++- raster/r.geomorphon/main.c | 6 +++--- raster/r.geomorphon/profile.c | 20 ++++++++++---------- raster/r.in.gdal/main.c | 8 ++++---- raster/r.in.pdal/main.cpp | 7 +++++-- raster/r.reclass/reclass.c | 4 ++-- raster/r.sim/simlib/output.c | 15 +++++++-------- raster3d/r3.in.ascii/main.c | 2 +- raster3d/r3.out.ascii/main.c | 6 +++--- raster3d/r3.out.bin/main.c | 11 +++++------ raster3d/r3.out.netcdf/main.c | 23 +++++++++++------------ vector/v.what.rast3/main.c | 13 ++++++------- 22 files changed, 103 insertions(+), 100 deletions(-) diff --git a/imagery/i.topo.corr/main.c b/imagery/i.topo.corr/main.c index 19fabbc1ec1..1f2b7988d26 100644 --- a/imagery/i.topo.corr/main.c +++ b/imagery/i.topo.corr/main.c @@ -184,8 +184,8 @@ int main(int argc, char *argv[]) } /* ----- */ dem.fd = Rast_open_old(base->answer, ""); - G_snprintf(out.name, GNAME_MAX - 1, "%s.%s", output->answer, - input->answers[i]); + snprintf(out.name, GNAME_MAX - 1, "%s.%s", output->answer, + input->answers[i]); out.fd = Rast_open_new(out.name, DCELL_TYPE); out.rast = Rast_allocate_buf(out.type); band.rast = Rast_allocate_buf(band.type); diff --git a/lib/gis/color_rules.c b/lib/gis/color_rules.c index eeb21d8a81e..bb3ace277f8 100644 --- a/lib/gis/color_rules.c +++ b/lib/gis/color_rules.c @@ -8,6 +8,7 @@ (C) 2001-2011 by the GRASS Development Team */ +#include #include #include @@ -267,7 +268,7 @@ struct colorinfo *get_colorinfo(int *nrules) char **cnames; /* load color rules */ - G_snprintf(path, GPATH_MAX, "%s/etc/colors", G_gisbase()); + snprintf(path, GPATH_MAX, "%s/etc/colors", G_gisbase()); *nrules = 0; cnames = G_ls2(path, nrules); @@ -283,8 +284,8 @@ struct colorinfo *get_colorinfo(int *nrules) colorinfo[i].desc = NULL; /* open color rule file */ - G_snprintf(path, GPATH_MAX, "%s/etc/colors/%s", G_gisbase(), - colorinfo[i].name); + snprintf(path, GPATH_MAX, "%s/etc/colors/%s", G_gisbase(), + colorinfo[i].name); fp = fopen(path, "r"); if (!fp) G_fatal_error(_("Unable to open color rule")); @@ -338,7 +339,7 @@ struct colorinfo *get_colorinfo(int *nrules) if (cisperc) colorinfo[i].type = G_store(_("range: map values")); else { - G_snprintf(buf, sizeof(buf) - 1, _("range: %g to %g"), rmin, rmax); + snprintf(buf, sizeof(buf) - 1, _("range: %g to %g"), rmin, rmax); colorinfo[i].type = G_store(buf); } } @@ -360,7 +361,7 @@ struct colorinfo *get_colorinfo(int *nrules) qsort(colorinfo, *nrules, sizeof(struct colorinfo), cmp_clrname); /* load color descriptions */ - G_snprintf(path, GPATH_MAX, "%s/etc/colors.desc", G_gisbase()); + snprintf(path, GPATH_MAX, "%s/etc/colors.desc", G_gisbase()); fp = fopen(path, "r"); if (!fp) G_fatal_error(_("Unable to open color descriptions")); diff --git a/lib/gis/parser.c b/lib/gis/parser.c index d84dcb174f2..510fc0f80cb 100644 --- a/lib/gis/parser.c +++ b/lib/gis/parser.c @@ -1812,7 +1812,7 @@ const char *get_renamed_option(const char *key) /* read renamed options from file (renamed_options) */ char path[GPATH_MAX]; - G_snprintf(path, GPATH_MAX, "%s/etc/renamed_options", G_gisbase()); + snprintf(path, GPATH_MAX, "%s/etc/renamed_options", G_gisbase()); st->renamed_options = G_read_key_value_file(path); } diff --git a/lib/gis/parser_wps.c b/lib/gis/parser_wps.c index 3995832582c..5500c77cac7 100644 --- a/lib/gis/parser_wps.c +++ b/lib/gis/parser_wps.c @@ -869,10 +869,10 @@ static void wps_print_literal_input_output( strcmp(datatype, "float") == 0) { str = strtok((char *)choices[0], "-"); if (str != NULL) { - G_snprintf(range[0], 24, "%s", str); + snprintf(range[0], 24, "%s", str); str = strtok(NULL, "-"); if (str != NULL) { - G_snprintf(range[1], 24, "%s", str); + snprintf(range[1], 24, "%s", str); type = TYPE_RANGE; } } diff --git a/lib/gis/snprintf.c b/lib/gis/snprintf.c index 8460e3e40f3..16d2f424d4d 100644 --- a/lib/gis/snprintf.c +++ b/lib/gis/snprintf.c @@ -19,11 +19,9 @@ * \date 2006-2008 */ -#include -#include #include -#include -#include +#include + #include /** @@ -33,6 +31,8 @@ * discouraged in favour of calculating how long the string will be and * allocating enough memory! * + * \deprecated Use C99 standard function snprintf() instead. + * * \param[in] str input string * \param[in] size length of string * \param[in] fmt @@ -48,9 +48,5 @@ int G_snprintf(char *str, size_t size, const char *fmt, ...) count = vsnprintf(str, size, fmt, ap); va_end(ap); - /* Windows' vsnprintf() doesn't always NUL-terminate the buffer */ - if (count >= 0 && (unsigned int)count == size) - str[--count] = '\0'; - return count; } diff --git a/lib/gis/timestamp.c b/lib/gis/timestamp.c index ba496d7d9cb..7c875db2ce2 100644 --- a/lib/gis/timestamp.c +++ b/lib/gis/timestamp.c @@ -76,8 +76,10 @@ * \author Soeren Gebbert, vector timestamp implementation update */ +#include #include #include + #include #include #include @@ -425,11 +427,11 @@ int G_has_vector_timestamp(const char *name, const char *layer, char ele[GNAME_MAX]; if (layer != NULL) - G_snprintf(ele, GNAME_MAX, "%s_%s", GV_TIMESTAMP_ELEMENT, layer); + snprintf(ele, GNAME_MAX, "%s_%s", GV_TIMESTAMP_ELEMENT, layer); else - G_snprintf(ele, GNAME_MAX, "%s_1", GV_TIMESTAMP_ELEMENT); + snprintf(ele, GNAME_MAX, "%s_1", GV_TIMESTAMP_ELEMENT); - G_snprintf(dir, GPATH_MAX, "%s/%s", GV_DIRECTORY, name); + snprintf(dir, GPATH_MAX, "%s/%s", GV_DIRECTORY, name); G_file_name(path, dir, ele, mapset); G_debug(1, "Check for timestamp <%s>", path); @@ -466,11 +468,11 @@ int G_read_vector_timestamp(const char *name, const char *layer, return 0; if (layer != NULL) - G_snprintf(ele, GNAME_MAX, "%s_%s", GV_TIMESTAMP_ELEMENT, layer); + snprintf(ele, GNAME_MAX, "%s_%s", GV_TIMESTAMP_ELEMENT, layer); else - G_snprintf(ele, GNAME_MAX, "%s_1", GV_TIMESTAMP_ELEMENT); + snprintf(ele, GNAME_MAX, "%s_1", GV_TIMESTAMP_ELEMENT); - G_snprintf(dir, GPATH_MAX, "%s/%s", GV_DIRECTORY, name); + snprintf(dir, GPATH_MAX, "%s/%s", GV_DIRECTORY, name); G_debug(1, "Read timestamp <%s/%s>", dir, ele); @@ -511,11 +513,11 @@ int G_write_vector_timestamp(const char *name, const char *layer, char ele[GNAME_MAX]; if (layer != NULL) - G_snprintf(ele, GNAME_MAX, "%s_%s", GV_TIMESTAMP_ELEMENT, layer); + snprintf(ele, GNAME_MAX, "%s_%s", GV_TIMESTAMP_ELEMENT, layer); else - G_snprintf(ele, GNAME_MAX, "%s_1", GV_TIMESTAMP_ELEMENT); + snprintf(ele, GNAME_MAX, "%s_1", GV_TIMESTAMP_ELEMENT); - G_snprintf(dir, GPATH_MAX, "%s/%s", GV_DIRECTORY, name); + snprintf(dir, GPATH_MAX, "%s/%s", GV_DIRECTORY, name); G_debug(1, "Write timestamp <%s/%s>", dir, ele); @@ -554,11 +556,11 @@ int G_remove_vector_timestamp(const char *name, const char *layer) char ele[GNAME_MAX]; if (layer) - G_snprintf(ele, GNAME_MAX, "%s_%s", GV_TIMESTAMP_ELEMENT, layer); + snprintf(ele, GNAME_MAX, "%s_%s", GV_TIMESTAMP_ELEMENT, layer); else - G_snprintf(ele, GNAME_MAX, "%s_1", GV_TIMESTAMP_ELEMENT); + snprintf(ele, GNAME_MAX, "%s_1", GV_TIMESTAMP_ELEMENT); - G_snprintf(dir, GPATH_MAX, "%s/%s", GV_DIRECTORY, name); + snprintf(dir, GPATH_MAX, "%s/%s", GV_DIRECTORY, name); return G_remove(dir, ele); } diff --git a/lib/imagery/loc_info.c b/lib/imagery/loc_info.c index 10f35c20414..c0cee9422a5 100644 --- a/lib/imagery/loc_info.c +++ b/lib/imagery/loc_info.c @@ -1,4 +1,5 @@ #include +#include #include #include @@ -10,13 +11,13 @@ char *I_location_info(const char *middle) char *buf; int len, buf_len; - G_snprintf(left, 80, "LOCATION: %s", G_location()); - G_snprintf(right, 80, "MAPSET: %s", G_mapset()); + snprintf(left, 80, "LOCATION: %s", G_location()); + snprintf(right, 80, "MAPSET: %s", G_mapset()); len = 79 - strlen(left) - strlen(middle) - strlen(right); buf_len = len + strlen(left) + strlen(middle) + strlen(right); buf = (char *)G_calloc(buf_len, sizeof(char)); - G_snprintf(buf, buf_len, "%s%*s%s%*s%s", left, len / 2, "", middle, len / 2, - "", right); + snprintf(buf, buf_len, "%s%*s%s%*s%s", left, len / 2, "", middle, len / 2, + "", right); return buf; } diff --git a/lib/init/clean_temp.c b/lib/init/clean_temp.c index aa6301c531e..b1edaaed6fe 100644 --- a/lib/init/clean_temp.c +++ b/lib/init/clean_temp.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -57,8 +58,8 @@ void clean_dir(const char *pathname, uid_t uid, pid_t pid, time_t now, (G_strcasecmp(cur_entry->d_name, "..") == 0)) continue; /* Skip dir and parent dir entries */ - if ((pathlen = G_snprintf(buf, BUF_MAX, "%s/%s", pathname, - cur_entry->d_name)) >= BUF_MAX) + if ((pathlen = snprintf(buf, BUF_MAX, "%s/%s", pathname, + cur_entry->d_name)) >= BUF_MAX) G_fatal_error("clean_temp: exceeded maximum pathname length %d, " "got %d, shouldn't happen", BUF_MAX, pathlen); diff --git a/lib/temporal/lib/default_name.c b/lib/temporal/lib/default_name.c index dfb8d9460b4..f3d732a5f31 100644 --- a/lib/temporal/lib/default_name.c +++ b/lib/temporal/lib/default_name.c @@ -13,6 +13,7 @@ Joel Jones (CERL/UIUC) and Radim Blazek */ +#include #include #include #include @@ -40,8 +41,8 @@ char *tgis_get_default_database_name(void) { char default_connection[2048]; - G_snprintf(default_connection, 2048, "$GISDBASE/$LOCATION_NAME/$MAPSET/%s", - TGISDB_DEFAULT_SQLITE_PATH); + snprintf(default_connection, 2048, "$GISDBASE/$LOCATION_NAME/$MAPSET/%s", + TGISDB_DEFAULT_SQLITE_PATH); return G_store(default_connection); } @@ -57,7 +58,7 @@ int tgis_set_default_connection(void) char db_name[2048]; char *tmp = tgis_get_default_database_name(); - G_snprintf(db_name, 2048, "%s", tmp); + snprintf(db_name, 2048, "%s", tmp); G_free(tmp); if (strcmp(TGISDB_DEFAULT_DRIVER, "sqlite") == 0) { diff --git a/raster/r.colors/edit_colors.c b/raster/r.colors/edit_colors.c index d987f696613..6615854b581 100644 --- a/raster/r.colors/edit_colors.c +++ b/raster/r.colors/edit_colors.c @@ -17,6 +17,7 @@ * ***************************************************************************/ +#include #include #include #include @@ -470,7 +471,7 @@ int edit_colors(int argc, char **argv, int type, const char *maptype, /* check if this style is a percentage style */ /* don't bother with native dirsep as not needed for backwards * compatibility */ - G_snprintf(path, GPATH_MAX, "%s/etc/colors/%s", G_gisbase(), style); + snprintf(path, GPATH_MAX, "%s/etc/colors/%s", G_gisbase(), style); rule_is_percent = check_percent_rule(path); do_scale = 1; } @@ -490,7 +491,7 @@ int edit_colors(int argc, char **argv, int type, const char *maptype, /* don't bother with native dirsep as not needed for backwards * compatibility */ - G_snprintf(path, GPATH_MAX, "%s/etc/colors/%s", G_gisbase(), rules); + snprintf(path, GPATH_MAX, "%s/etc/colors/%s", G_gisbase(), rules); if (!Rast_load_fp_colors(&colors, path, min, max)) G_fatal_error(_("Unable to load rules file <%s>"), rules); diff --git a/raster/r.external/main.c b/raster/r.external/main.c index baf5b7c8ea0..7107295951d 100644 --- a/raster/r.external/main.c +++ b/raster/r.external/main.c @@ -16,6 +16,7 @@ * *****************************************************************************/ +#include #include #include #include @@ -189,7 +190,7 @@ int main(int argc, char *argv[]) if (!cwd) G_fatal_error(_("Unable to get current working directory")); - G_snprintf(path, GPATH_MAX, "%s%c%s", cwd, HOST_DIRSEP, input); + snprintf(path, GPATH_MAX, "%s%c%s", cwd, HOST_DIRSEP, input); input = G_store(path); CPLFree(cwd); } diff --git a/raster/r.geomorphon/main.c b/raster/r.geomorphon/main.c index 9417e3adfd4..587eea26842 100644 --- a/raster/r.geomorphon/main.c +++ b/raster/r.geomorphon/main.c @@ -528,9 +528,9 @@ int main(int argc, char **argv) prof_int("format_version_major", 0); prof_int("format_version_minor", 9); prof_utc("timestamp", time(NULL)); - G_snprintf(buf, sizeof(buf), - "r.geomorphon GRASS GIS %s [%s]", - GRASS_VERSION_STRING, GRASS_HEADERS_VERSION); + snprintf(buf, sizeof(buf), + "r.geomorphon GRASS GIS %s [%s]", + GRASS_VERSION_STRING, GRASS_HEADERS_VERSION); prof_str("generator", buf); oneoff_done = diff --git a/raster/r.geomorphon/profile.c b/raster/r.geomorphon/profile.c index b954b38c243..304250954e9 100644 --- a/raster/r.geomorphon/profile.c +++ b/raster/r.geomorphon/profile.c @@ -56,7 +56,7 @@ static void prof_int_internal(const toktype type, const char *key, return; } token[size].type = type; - G_snprintf(token[size].key, MAX_STR_LEN, "%s", key); + snprintf(token[size].key, MAX_STR_LEN, "%s", key); token[size].int_val = val; size++; } @@ -79,7 +79,7 @@ static void prof_dbl_internal(const toktype type, const char *key, return; } token[size].type = type; - G_snprintf(token[size].key, MAX_STR_LEN, "%s", key); + snprintf(token[size].key, MAX_STR_LEN, "%s", key); token[size].dbl_val = val; size++; } @@ -101,8 +101,8 @@ void prof_str(const char *key, const char *val) return; } token[size].type = T_STR; - G_snprintf(token[size].key, MAX_STR_LEN, "%s", key); - G_snprintf(token[size].str_val, MAX_STR_LEN, "%s", val); + snprintf(token[size].key, MAX_STR_LEN, "%s", key); + snprintf(token[size].str_val, MAX_STR_LEN, "%s", val); size++; } @@ -121,7 +121,7 @@ void prof_sso(const char *key) return; } token[size].type = T_SSO; - G_snprintf(token[size].key, MAX_STR_LEN, "%s", key); + snprintf(token[size].key, MAX_STR_LEN, "%s", key); size++; } @@ -213,7 +213,7 @@ static const char *quote_val(const toktype t, const char *v) if (t != T_STR) return v; - G_snprintf(buf, sizeof(buf), "\"%s\"", v); + snprintf(buf, sizeof(buf), "\"%s\"", v); return buf; } @@ -225,19 +225,19 @@ static const char *format_token_common(const struct token *t) case T_BLN: return t->int_val ? "true" : "false"; case T_INT: - G_snprintf(buf, sizeof(buf), "%d", t->int_val); + snprintf(buf, sizeof(buf), "%d", t->int_val); return buf; case T_DBL: if (isnan(t->dbl_val)) return "null"; - G_snprintf(buf, sizeof(buf), "%.8f", t->dbl_val); + snprintf(buf, sizeof(buf), "%.8f", t->dbl_val); return buf; case T_STR: return t->str_val; case T_MTR: if (isnan(t->dbl_val)) return "null"; - G_snprintf(buf, sizeof(buf), "%.2f", t->dbl_val); + snprintf(buf, sizeof(buf), "%.2f", t->dbl_val); return buf; default: return NULL; @@ -330,7 +330,7 @@ static unsigned stack_push(const char *s) overflow = 1; return 0; } - G_snprintf(stack[stack_size], MAX_STR_LEN, "%s", s); + snprintf(stack[stack_size], MAX_STR_LEN, "%s", s); stack_size++; return 1; } diff --git a/raster/r.in.gdal/main.c b/raster/r.in.gdal/main.c index 9066bdf9f64..54280bd71c2 100644 --- a/raster/r.in.gdal/main.c +++ b/raster/r.in.gdal/main.c @@ -788,11 +788,11 @@ int main(int argc, char *argv[]) /* Generate the suffix */ if (num_digits > 0) { - G_snprintf(suffix, num_digits + 1, "%0*d", num_digits, - nBand + offset); + snprintf(suffix, num_digits + 1, "%0*d", num_digits, + nBand + offset); } else { - G_snprintf(suffix, 65, "%d", nBand + offset); + snprintf(suffix, 65, "%d", nBand + offset); } G_debug(3, "Import raster band %d", nBand); @@ -1954,7 +1954,7 @@ static int dump_rat(GDALRasterBandH hBand, char *outrat, int nBand) field_type = G_malloc(ncols * sizeof(GDALRATFieldType)); - G_snprintf(fname, GNAME_MAX, "%s_%d.csv", outrat, nBand); + snprintf(fname, GNAME_MAX, "%s_%d.csv", outrat, nBand); if (!(fp = fopen(fname, "w"))) { int err = errno; diff --git a/raster/r.in.pdal/main.cpp b/raster/r.in.pdal/main.cpp index 093b17258e1..da32dcb5484 100644 --- a/raster/r.in.pdal/main.cpp +++ b/raster/r.in.pdal/main.cpp @@ -17,6 +17,8 @@ * *****************************************************************************/ +#include + #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-parameter" @@ -892,9 +894,10 @@ int main(int argc, char *argv[]) char file_list[4096]; if (file_list_opt->answer) - G_snprintf(file_list, sizeof(file_list), "%s", file_list_opt->answer); + std::snprintf(file_list, sizeof(file_list), "%s", + file_list_opt->answer); else - G_snprintf(file_list, sizeof(file_list), "%s", input_opt->answer); + std::snprintf(file_list, sizeof(file_list), "%s", input_opt->answer); Rast_set_history(&history, HIST_DATSRC_1, file_list); Rast_write_history(outmap, &history); diff --git a/raster/r.reclass/reclass.c b/raster/r.reclass/reclass.c index def43b0239f..61f865648ed 100644 --- a/raster/r.reclass/reclass.c +++ b/raster/r.reclass/reclass.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -238,8 +239,7 @@ int reclass(const char *old_name, const char *old_mapset, const char *new_name, G_fatal_error(_("Cannot create reclass file of <%s>"), new_name); if (!title) { - G_snprintf(buf, sizeof(buf), "Reclass of %s in %s", new.name, - new.mapset); + snprintf(buf, sizeof(buf), "Reclass of %s in %s", new.name, new.mapset); title = buf; } diff --git a/raster/r.sim/simlib/output.c b/raster/r.sim/simlib/output.c index 8fecd37d960..155f3669465 100644 --- a/raster/r.sim/simlib/output.c +++ b/raster/r.sim/simlib/output.c @@ -42,8 +42,7 @@ void output_walker_as_vector(int tt_minutes, int ndigit, /* In case of time series we extent the output name with the time value */ if (ts == 1) { - G_snprintf(buf, sizeof(buf), "%s_%.*d", outwalk, ndigit, - tt_minutes); + snprintf(buf, sizeof(buf), "%s_%.*d", outwalk, ndigit, tt_minutes); outwalk_time = G_store(buf); if (Vect_open_new(&Out, outwalk_time, WITH_Z) < 0) G_fatal_error(_("Unable to create vector map <%s>"), @@ -146,7 +145,7 @@ int output_data(int tt, double ft UNUSED) if (depth) { depth_cell = Rast_allocate_f_buf(); if (ts == 1) { - G_snprintf(buf, sizeof(buf), "%s.%.*d", depth, ndigit, tt_minutes); + snprintf(buf, sizeof(buf), "%s.%.*d", depth, ndigit, tt_minutes); depth0 = G_store(buf); depth_fd = Rast_open_fp_new(depth0); } @@ -157,7 +156,7 @@ int output_data(int tt, double ft UNUSED) if (disch) { disch_cell = Rast_allocate_f_buf(); if (ts == 1) { - G_snprintf(buf, sizeof(buf), "%s.%.*d", disch, ndigit, tt_minutes); + snprintf(buf, sizeof(buf), "%s.%.*d", disch, ndigit, tt_minutes); disch0 = G_store(buf); disch_fd = Rast_open_fp_new(disch0); } @@ -168,7 +167,7 @@ int output_data(int tt, double ft UNUSED) if (err) { err_cell = Rast_allocate_f_buf(); if (ts == 1) { - G_snprintf(buf, sizeof(buf), "%s.%.*d", err, ndigit, tt_minutes); + snprintf(buf, sizeof(buf), "%s.%.*d", err, ndigit, tt_minutes); err0 = G_store(buf); err_fd = Rast_open_fp_new(err0); } @@ -179,7 +178,7 @@ int output_data(int tt, double ft UNUSED) if (conc) { conc_cell = Rast_allocate_f_buf(); if (ts == 1) { - G_snprintf(buf, sizeof(buf), "%s.%.*d", conc, ndigit, tt_minutes); + snprintf(buf, sizeof(buf), "%s.%.*d", conc, ndigit, tt_minutes); conc0 = G_store(buf); conc_fd = Rast_open_fp_new(conc0); } @@ -190,7 +189,7 @@ int output_data(int tt, double ft UNUSED) if (flux) { flux_cell = Rast_allocate_f_buf(); if (ts == 1) { - G_snprintf(buf, sizeof(buf), "%s.%.*d", flux, ndigit, tt_minutes); + snprintf(buf, sizeof(buf), "%s.%.*d", flux, ndigit, tt_minutes); flux0 = G_store(buf); flux_fd = Rast_open_fp_new(flux0); } @@ -201,7 +200,7 @@ int output_data(int tt, double ft UNUSED) if (erdep) { erdep_cell = Rast_allocate_f_buf(); if (ts == 1) { - G_snprintf(buf, sizeof(buf), "%s.%.*d", erdep, ndigit, tt_minutes); + snprintf(buf, sizeof(buf), "%s.%.*d", erdep, ndigit, tt_minutes); erdep0 = G_store(buf); erdep_fd = Rast_open_fp_new(erdep0); } diff --git a/raster3d/r3.in.ascii/main.c b/raster3d/r3.in.ascii/main.c index 73acfd131f2..edc7ac43e38 100644 --- a/raster3d/r3.in.ascii/main.c +++ b/raster3d/r3.in.ascii/main.c @@ -142,7 +142,7 @@ void readHeaderString(FILE *fp, char *valueString, double *value) char line_buff[1024]; /* to avoid buffer overflows we use G_snprintf */ - G_snprintf(format, 100, "%s %%lf", valueString); + snprintf(format, 100, "%s %%lf", valueString); G_getl2(line_buff, 1024, fp); if (sscanf(line_buff, format, value) != 1) { /* this would be ideal to merge if Rast3d_close could be solved */ diff --git a/raster3d/r3.out.ascii/main.c b/raster3d/r3.out.ascii/main.c index eeb481674c4..a72029f900a 100644 --- a/raster3d/r3.out.ascii/main.c +++ b/raster3d/r3.out.ascii/main.c @@ -137,7 +137,7 @@ void writeHeaderString(FILE *fp, char *valueString, double value) { static char format[100]; - G_snprintf(format, 100, "%s %%lf\n", valueString); + snprintf(format, 100, "%s %%lf\n", valueString); if (fprintf(fp, format, value) < 0) fatalError("writeHeaderString: header value invalid"); } @@ -147,7 +147,7 @@ void writeHeaderString2(FILE *fp, char *valueString, int value) { static char format[100]; - G_snprintf(format, 100, "%s %%d\n", valueString); + snprintf(format, 100, "%s %%d\n", valueString); if (fprintf(fp, format, value) < 0) fatalError("writeHeaderString: header value invalid"); } @@ -157,7 +157,7 @@ void writeHeaderString3(FILE *fp, char *valueString, const char *value) { static char format[100]; - G_snprintf(format, 100, "%s %%s\n", valueString); + snprintf(format, 100, "%s %%s\n", valueString); if (fprintf(fp, format, value) < 0) fatalError("writeHeaderString: header value invalid"); } diff --git a/raster3d/r3.out.bin/main.c b/raster3d/r3.out.bin/main.c index c4438dc0bc4..cd14bf8b9df 100644 --- a/raster3d/r3.out.bin/main.c +++ b/raster3d/r3.out.bin/main.c @@ -200,7 +200,7 @@ int main(int argc, char *argv[]) struct Flag *row, *depth, *integer; } flag; char *name; - char *outfile; + char outfile[GNAME_MAX]; double null_val; int do_stdout; int order = 0; @@ -287,11 +287,10 @@ int main(int argc, char *argv[]) _("Integer output doesn't support bytes=8 in this build")); #endif - if (parm.output->answer) - outfile = parm.output->answer; - else { - outfile = G_malloc(strlen(name) + 4 + 1); - G_snprintf(outfile, sizeof(outfile), "%s.bin", name); + if (snprintf(outfile, sizeof(outfile), "%s%s", + (parm.output->answer ? parm.output->answer : name), + (parm.output->answer ? "" : ".bin")) >= (int)sizeof(outfile)) { + G_fatal_error(_("Output map name too long.")); } if (G_strcasecmp(parm.order->answer, "big") == 0) diff --git a/raster3d/r3.out.netcdf/main.c b/raster3d/r3.out.netcdf/main.c index 914e4b667a9..6bcdcbaf9f5 100644 --- a/raster3d/r3.out.netcdf/main.c +++ b/raster3d/r3.out.netcdf/main.c @@ -292,8 +292,8 @@ static void write_netcdf_header(int ncid, RASTER3D_Region *region, int *varid, char long_name[1024]; char time_unit[1024]; - G_snprintf(long_name, 1024, "Time in %s", - Rast3d_get_vertical_unit(map)); + snprintf(long_name, 1024, "Time in %s", + Rast3d_get_vertical_unit(map)); if ((retval = nc_def_dim(ncid, TIME_NAME, region->depths, &time_dimid))) @@ -311,21 +311,20 @@ static void write_netcdf_header(int ncid, RASTER3D_Region *region, int *varid, /* Days since datum in ISO norm */ if (datetime_is_absolute(&ts.dt[0])) { is_absolute_time = 1; - G_snprintf(time_unit, 1024, - "%s since %d-%02d-%02d %02d:%02d:%02.0f", - Rast3d_get_vertical_unit(map), ts.dt[0].year, - ts.dt[0].month, ts.dt[0].day, ts.dt[0].hour, - ts.dt[0].minute, ts.dt[0].second); + snprintf(time_unit, 1024, + "%s since %d-%02d-%02d %02d:%02d:%02.0f", + Rast3d_get_vertical_unit(map), ts.dt[0].year, + ts.dt[0].month, ts.dt[0].day, ts.dt[0].hour, + ts.dt[0].minute, ts.dt[0].second); } else { - G_snprintf(time_unit, 1024, "%s", - Rast3d_get_vertical_unit(map)); + snprintf(time_unit, 1024, "%s", + Rast3d_get_vertical_unit(map)); } } else { - G_snprintf(time_unit, 1024, "%s since %s", - Rast3d_get_vertical_unit(map), - "1900-01-01 00:00:00"); + snprintf(time_unit, 1024, "%s since %s", + Rast3d_get_vertical_unit(map), "1900-01-01 00:00:00"); } if ((retval = nc_put_att_text(ncid, time_varid, UNITS, diff --git a/vector/v.what.rast3/main.c b/vector/v.what.rast3/main.c index 3776fa1848f..8ede8075874 100644 --- a/vector/v.what.rast3/main.c +++ b/vector/v.what.rast3/main.c @@ -280,8 +280,7 @@ int main(int argc, char *argv[]) continue; } - G_snprintf(buf, 2048, "update %s set %s = ", Fi->table, - opt.col->answer); + snprintf(buf, 2048, "update %s set %s = ", Fi->table, opt.col->answer); db_set_string(&stmt, buf); @@ -297,23 +296,23 @@ int main(int argc, char *argv[]) Rast3d_is_null_value_num(&cache[point].dvalue, DCELL_TYPE); if (is_empty) { - G_snprintf(buf, 2048, "NULL"); + snprintf(buf, 2048, "NULL"); } else { if (typeIntern == FCELL_TYPE) - G_snprintf(buf, 2048, "%.10f", cache[point].fvalue); + snprintf(buf, 2048, "%.10f", cache[point].fvalue); if (typeIntern == DCELL_TYPE) - G_snprintf(buf, 2048, "%.15f", cache[point].dvalue); + snprintf(buf, 2048, "%.15f", cache[point].dvalue); } db_append_string(&stmt, buf); - G_snprintf(buf, 2048, " where %s = %d", Fi->key, cache[point].cat); + snprintf(buf, 2048, " where %s = %d", Fi->key, cache[point].cat); db_append_string(&stmt, buf); /* user provides where condition: */ if (opt.where->answer) { - G_snprintf(buf, 2048, " AND %s", opt.where->answer); + snprintf(buf, 2048, " AND %s", opt.where->answer); db_append_string(&stmt, buf); } G_debug(3, "%s", db_get_string(&stmt)); From 66389f0ba80af1de97aeced1c86c76f15e132fd4 Mon Sep 17 00:00:00 2001 From: Stefan Blumentrath Date: Wed, 4 Sep 2024 02:56:09 +0200 Subject: [PATCH 186/471] r.in.gdal/r.external: add basic support for GDALs GDT_Int8 (#4256) * add support for Int8 from GDAL >= 3.7 * add int8 test (skiped if GDAL < 3.7) --- lib/raster/gdal.c | 4 ++ lib/raster/get_row.c | 7 ++++ raster/r.external/link.c | 8 ++++ raster/r.in.gdal/testsuite/data/int8.tif | Bin 0 -> 948 bytes raster/r.in.gdal/testsuite/data/int8.vrt | 20 +++++++++ raster/r.in.gdal/testsuite/test_r_in_gdal.py | 42 +++++++++++++++++++ 6 files changed, 81 insertions(+) create mode 100644 raster/r.in.gdal/testsuite/data/int8.tif create mode 100644 raster/r.in.gdal/testsuite/data/int8.vrt diff --git a/lib/raster/gdal.c b/lib/raster/gdal.c index a3bfce1b3b8..7fa85a9836c 100644 --- a/lib/raster/gdal.c +++ b/lib/raster/gdal.c @@ -125,6 +125,10 @@ struct GDAL_link *Rast_get_gdal_link(const char *name, const char *mapset) switch (type) { case GDT_Byte: +/* GDT_Int8 was introduced in GDAL 3.7 */ +#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3, 7, 0) + case GDT_Int8: +#endif case GDT_Int16: case GDT_UInt16: case GDT_Int32: diff --git a/lib/raster/get_row.c b/lib/raster/get_row.c index 1f08a1203f3..d95eadc6e2f 100644 --- a/lib/raster/get_row.c +++ b/lib/raster/get_row.c @@ -11,6 +11,7 @@ \author Original author CERL */ +#include #include #include #include @@ -368,6 +369,12 @@ static void gdal_values_int(int fd, const unsigned char *data, case GDT_Byte: c[i] = *(GByte *)d; break; +/* GDT_Int8 was introduced in GDAL 3.7 */ +#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3, 7, 0) + case GDT_Int8: + c[i] = *(int8_t *)d; + break; +#endif case GDT_Int16: c[i] = *(GInt16 *)d; break; diff --git a/raster/r.external/link.c b/raster/r.external/link.c index 4ff0799fe69..bde45546fc2 100644 --- a/raster/r.external/link.c +++ b/raster/r.external/link.c @@ -30,6 +30,14 @@ void query_band(GDALRasterBandH hBand, const char *output, cellhd->format = 0; break; +/* GDT_Int8 was introduced in GDAL 3.7 */ +#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3, 7, 0) + case GDT_Int8: + info->data_type = CELL_TYPE; + cellhd->format = 1; + break; +#endif + case GDT_Int16: case GDT_UInt16: info->data_type = CELL_TYPE; diff --git a/raster/r.in.gdal/testsuite/data/int8.tif b/raster/r.in.gdal/testsuite/data/int8.tif new file mode 100644 index 0000000000000000000000000000000000000000..84f3e88ce634cb0a89c706b8eac1e79fa3e486e4 GIT binary patch literal 948 zcmebD)MDUZU|`^9U|?isU<9%xfS3`9%>-qGR53%@Aa!g=Y(YjAu-+1&gea1@7?ce% zQyi+sfr)`Z21!jYlnoSS;A`e#UFAD1}z}l z3*-nm_+SrV!f3bz0fvPGOaUX<2kZ4$q literal 0 HcmV?d00001 diff --git a/raster/r.in.gdal/testsuite/data/int8.vrt b/raster/r.in.gdal/testsuite/data/int8.vrt new file mode 100644 index 00000000000..5a0b5ebe836 --- /dev/null +++ b/raster/r.in.gdal/testsuite/data/int8.vrt @@ -0,0 +1,20 @@ + + PROJCS["NAD83(HARN) / North Carolina",GEOGCS["NAD83(HARN)",DATUM["NAD83_High_Accuracy_Reference_Network",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6152"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4152"]],PROJECTION["Lambert_Conformal_Conic_2SP"],PARAMETER["latitude_of_origin",33.75],PARAMETER["central_meridian",-79],PARAMETER["standard_parallel_1",36.1666666666667],PARAMETER["standard_parallel_2",34.3333333333333],PARAMETER["false_easting",609601.22],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["Easting",EAST],AXIS["Northing",NORTH],AUTHORITY["EPSG","3358"]] + 0.0000000000000000e+00, 1.0000000000000000e+00, 0.0000000000000000e+00, 2.4000000000000000e+01, 0.0000000000000000e+00, -1.0000000000000000e+00 + + Area + + + BAND + + + Gray + + ./int8.tif + 1 + + + + + + diff --git a/raster/r.in.gdal/testsuite/test_r_in_gdal.py b/raster/r.in.gdal/testsuite/test_r_in_gdal.py index 2b8bee0982a..355e9b74eda 100644 --- a/raster/r.in.gdal/testsuite/test_r_in_gdal.py +++ b/raster/r.in.gdal/testsuite/test_r_in_gdal.py @@ -3,6 +3,10 @@ @author Soeren Gebbert """ +import unittest + +from subprocess import check_output + from grass.gunittest.case import TestCase @@ -306,6 +310,44 @@ def test_netCDF_3d_5(self): self.assertLooksLike(map_list, text_from_file) + @unittest.skipIf( + tuple( + map( + int, + check_output(["gdal-config", "--version"]) + .decode("UTF8") + .split(".")[0:2], + ) + ) + < (3, 7), + "GDAL version too old. Int8 support was introduced in GDAL 3.7", + ) + def test_int8_data(self): + """Test that Int8 VRTs are imported""" + + self.assertModule( + "r.in.gdal", + input="data/int8.vrt", + output="test_gdal_import_map", + ) + + # Output of r.info + info_string = """north=24 + south=0 + east=24 + west=0 + nsres=1 + ewres=1 + rows=24 + cols=24 + cells=576 + datatype=CELL + ncats=0""" + + self.assertRasterFitsInfo( + raster="test_gdal_import_map", reference=info_string, precision=3 + ) + class TestGdalImportFails(TestCase): def test_error_handling_1(self): From e132155a73521dbb24f98d954d7b04bf9743c518 Mon Sep 17 00:00:00 2001 From: Makiko Shukunobe Date: Wed, 4 Sep 2024 15:57:38 -0400 Subject: [PATCH 187/471] checks: Flake8 F841 fixes in the wxpython directory part 2 (#4253) --- .flake8 | 7 +++---- gui/wxpython/dbmgr/base.py | 8 +++----- gui/wxpython/dbmgr/dialogs.py | 1 - gui/wxpython/dbmgr/sqlbuilder.py | 7 ------- gui/wxpython/dbmgr/vinfo.py | 3 --- 5 files changed, 6 insertions(+), 20 deletions(-) diff --git a/.flake8 b/.flake8 index 573debabe1c..18cea9165ce 100644 --- a/.flake8 +++ b/.flake8 @@ -52,11 +52,10 @@ per-file-ignores = gui/wxpython/core/settings.py: E722 gui/wxpython/core/watchdog.py: E402 gui/wxpython/datacatalog/tree.py: E731, E402 - gui/wxpython/dbmgr/base.py: E722, F841 - gui/wxpython/dbmgr/dialogs.py: F841, E722 - gui/wxpython/dbmgr/sqlbuilder.py: E722, F841 + gui/wxpython/dbmgr/base.py: E722 + gui/wxpython/dbmgr/dialogs.py: E722 + gui/wxpython/dbmgr/sqlbuilder.py: E722 gui/wxpython/dbmgr/manager.py: E722 - gui/wxpython/dbmgr/vinfo.py: F841 gui/wxpython/docs/wxgui_sphinx/conf.py: E402, W291 gui/wxpython/gcp/g.gui.gcp.py: F841 gui/wxpython/gcp/manager.py: F841, E722 diff --git a/gui/wxpython/dbmgr/base.py b/gui/wxpython/dbmgr/base.py index 1c4349bdd8c..66321fcafda 100644 --- a/gui/wxpython/dbmgr/base.py +++ b/gui/wxpython/dbmgr/base.py @@ -228,7 +228,7 @@ def LoadData(self, layer, columns=None, where=None, sql=None): if sql: cmdParams.update({"sql": sql, "output": outFile.name, "overwrite": True}) - ret = RunCommand("db.select", **cmdParams) + RunCommand("db.select", **cmdParams) self.sqlFilter = {"sql": sql} else: cmdParams.update( @@ -246,7 +246,7 @@ def LoadData(self, layer, columns=None, where=None, sql=None): # Enclose column name with SQL standard double quotes cmdParams.update({"columns": ",".join([f'"{col}"' for col in columns])}) - ret = RunCommand("v.db.select", **cmdParams) + RunCommand("v.db.select", **cmdParams) # These two should probably be passed to init more cleanly # setting the numbers of items = number of elements in the dictionary @@ -296,7 +296,7 @@ def LoadData(self, layer, columns=None, where=None, sql=None): record = ( decode(outFile.readline(), encoding=enc).strip().replace("\n", "") ) - except UnicodeDecodeError as e: + except UnicodeDecodeError: record = ( outFile.readline() .decode(encoding=enc, errors="replace") @@ -965,7 +965,6 @@ def OnLayerPageChanged(self, event): pass if idCol: - winCol = self.FindWindowById(idCol) table = self.dbMgrData["mapDBInfo"].layers[self.selLayer]["table"] self.dbMgrData["mapDBInfo"].GetColumns(table) @@ -2687,7 +2686,6 @@ def OnTableItemDelete(self, event): tlist = self.FindWindowById(self.layerPage[self.selLayer]["tableData"]) item = tlist.GetFirstSelected() - countSelected = tlist.GetSelectedItemCount() if UserSettings.Get(group="atm", key="askOnDeleteRec", subkey="enabled"): # if the user select more columns to delete, all the columns name # will appear the the warning dialog diff --git a/gui/wxpython/dbmgr/dialogs.py b/gui/wxpython/dbmgr/dialogs.py index 757fde03cbf..26e6d876371 100644 --- a/gui/wxpython/dbmgr/dialogs.py +++ b/gui/wxpython/dbmgr/dialogs.py @@ -305,7 +305,6 @@ def OnReset(self, event=None): columns = self.mapDBInfo.tables[table] for idx in range(len(columns[key]["values"])): for name in columns.keys(): - type = columns[name]["type"] value = columns[name]["values"][idx] if value is None: value = "" diff --git a/gui/wxpython/dbmgr/sqlbuilder.py b/gui/wxpython/dbmgr/sqlbuilder.py index 92867b9ec82..36827698efc 100644 --- a/gui/wxpython/dbmgr/sqlbuilder.py +++ b/gui/wxpython/dbmgr/sqlbuilder.py @@ -356,7 +356,6 @@ def _doLayout(self, modeChoices, showDbInfo=False): def OnUniqueValues(self, event, justsample=False): """Get unique values""" - vals = [] try: idx = self.list_columns.GetSelections()[0] column = self.list_columns.GetString(idx) @@ -415,12 +414,6 @@ def OnAddValue(self, event): idx = selection[0] value = self.list_values.GetString(idx) - idx = self.list_columns.GetSelections()[0] - column = self.list_columns.GetString(idx) - - ctype = self.dbInfo.GetTableDesc(self.dbInfo.GetTable(self.layer))[column][ - "type" - ] self._add(element="value", value=value) diff --git a/gui/wxpython/dbmgr/vinfo.py b/gui/wxpython/dbmgr/vinfo.py index afb0a716063..6b04ee7a64e 100644 --- a/gui/wxpython/dbmgr/vinfo.py +++ b/gui/wxpython/dbmgr/vinfo.py @@ -102,9 +102,6 @@ def SelectByPoint(self, queryCoords, qdist): """Get attributes by coordinates (all available layers) Return line id or None if no line is found""" - line = None - nselected = 0 - try: data = gs.vector_what( map=self.map, From fc0ffaea14ded47f9f9949328bb2b927307eec38 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 4 Sep 2024 17:35:31 -0400 Subject: [PATCH 188/471] CI(deps): Update DoozyX/clang-format-lint-action action to v0.18.2 (#4274) --- .github/workflows/clang-format-check.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/clang-format-check.yml b/.github/workflows/clang-format-check.yml index dc39cdf463c..12b0b070ec9 100644 --- a/.github/workflows/clang-format-check.yml +++ b/.github/workflows/clang-format-check.yml @@ -19,7 +19,7 @@ jobs: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: persist-credentials: false - - uses: DoozyX/clang-format-lint-action@caa179272c6ee7f1d25dfb503ee0c410c26ebd98 # v0.18.1 + - uses: DoozyX/clang-format-lint-action@c71d0bf4e21876ebec3e5647491186f8797fde31 # v0.18.2 with: source: "." clangFormatVersion: 18.1.8 From 98f38fc3c00d55bac3dac84242a33022d3db4553 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 5 Sep 2024 08:00:40 -0400 Subject: [PATCH 189/471] CI(deps): Update peter-evans/create-pull-request action to v7.0.1 (#4276) --- .github/workflows/periodic_update.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/periodic_update.yml b/.github/workflows/periodic_update.yml index 842f7404d69..346fd12e2d2 100644 --- a/.github/workflows/periodic_update.yml +++ b/.github/workflows/periodic_update.yml @@ -33,7 +33,7 @@ jobs: run: git status --ignored - name: Create Pull Request id: cpr - uses: peter-evans/create-pull-request@4320041ed380b20e97d388d56a7fb4f9b8c20e79 # v7.0.0 + uses: peter-evans/create-pull-request@8867c4aba1b742c39f8d0ba35429c2dfa4b6cb20 # v7.0.1 with: commit-message: "config.guess + config.sub: updated from http://git.savannah.gnu.org/cgit/config.git/plain/" branch: periodic/update-configure From 9bbfd6bd65554d989f5bbbcd968946c0ebcd339b Mon Sep 17 00:00:00 2001 From: ymdatta Date: Fri, 6 Sep 2024 21:35:45 -0400 Subject: [PATCH 190/471] ps.map: Initialize variable before using it in conditional (#4286) --- ps/ps.map/ps_header.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ps/ps.map/ps_header.c b/ps/ps.map/ps_header.c index fd7eb996588..9a454824d9b 100644 --- a/ps/ps.map/ps_header.c +++ b/ps/ps.map/ps_header.c @@ -16,7 +16,7 @@ extern int rotate_plot; int write_PS_header(void) { struct Categories cats; - int cats_ok; + int cats_ok = 0; if (PS.do_raster) cats_ok = Rast_read_cats(PS.cell_name, PS.cell_mapset, &cats) >= 0; From 102bc38d16df035dbb0b4caf0f16e7fda0d12273 Mon Sep 17 00:00:00 2001 From: Makiko Shukunobe Date: Fri, 6 Sep 2024 21:47:24 -0400 Subject: [PATCH 191/471] checks: Flake8 F841 fixes in the scripts directory part 2 (#4241) * Fix F841 problems in `scripts` directory * Update `.flake8` with fixes for F841 * Remove unused lines based on reviewed suggestions --- .flake8 | 8 ++++---- scripts/r.semantic.label/r.semantic.label.py | 4 ++-- scripts/v.import/v.import.py | 2 -- scripts/v.report/v.report.py | 3 --- scripts/v.unpack/v.unpack.py | 4 ---- 5 files changed, 6 insertions(+), 15 deletions(-) diff --git a/.flake8 b/.flake8 index 18cea9165ce..47fa3239a24 100644 --- a/.flake8 +++ b/.flake8 @@ -158,12 +158,12 @@ per-file-ignores = scripts/r.in.wms/wms_cap_parsers.py: F841 scripts/r.in.wms/wms_drv.py: E402, E722 scripts/r.in.wms/srs.py: E722 - scripts/r.semantic.label/r.semantic.label.py: F841, E501 - scripts/v.report/v.report.py: F841, E721 + scripts/r.semantic.label/r.semantic.label.py: E501 + scripts/v.report/v.report.py: E721 scripts/db.out.ogr/db.out.ogr.py: F841 scripts/g.extension/g.extension.py: F841, E722, E501 - scripts/v.unpack/v.unpack.py: F841, E722, E501 - scripts/v.import/v.import.py: F841, E722, E501 + scripts/v.unpack/v.unpack.py: E722, E501 + scripts/v.import/v.import.py: E722, E501 scripts/db.univar/db.univar.py: E501 scripts/d.frame/d.frame.py: E722 scripts/i.pansharpen/i.pansharpen.py: E722, E501 diff --git a/scripts/r.semantic.label/r.semantic.label.py b/scripts/r.semantic.label/r.semantic.label.py index 53239772117..f3c26320ae3 100644 --- a/scripts/r.semantic.label/r.semantic.label.py +++ b/scripts/r.semantic.label/r.semantic.label.py @@ -62,7 +62,7 @@ def print_map_semantic_label(name, label_reader): label_reader.print_info(semantic_label=semantic_label) else: gs.info(_("No semantic label assigned to <{}>").format(name)) - except OpenError as e: + except OpenError: gs.error(_("Map <{}> not found").format(name)) @@ -94,7 +94,7 @@ def manage_map_semantic_label(name, semantic_label): except GrassError as e: gs.error(_("Unable to assign/dissociate semantic label. {}").format(e)) return 1 - except OpenError as e: + except OpenError: gs.error(_("Map <{}> not found in current mapset").format(name)) return 1 diff --git a/scripts/v.import/v.import.py b/scripts/v.import/v.import.py index 42e5952c76f..4f579c4bf45 100755 --- a/scripts/v.import/v.import.py +++ b/scripts/v.import/v.import.py @@ -257,8 +257,6 @@ def main(): f.write("GUI: text\n") f.close() - tgtsrs = gs.read_command("g.proj", flags="j", quiet=True) - # create temp location from input without import gs.verbose(_("Creating temporary project for <%s>...") % OGRdatasource) try: diff --git a/scripts/v.report/v.report.py b/scripts/v.report/v.report.py index 8da23bbaf91..18752a697cb 100755 --- a/scripts/v.report/v.report.py +++ b/scripts/v.report/v.report.py @@ -216,9 +216,6 @@ def main(): if not flags["c"]: sys.stdout.write(fs.join(colnames + extracolnames) + "\n") - # make and print the table: - numcols = len(colnames) + len(extracolnames) - # calculate percents if requested if units == "percent" and option != "coor": # calculate total value diff --git a/scripts/v.unpack/v.unpack.py b/scripts/v.unpack/v.unpack.py index af430f98829..0507e9eb400 100644 --- a/scripts/v.unpack/v.unpack.py +++ b/scripts/v.unpack/v.unpack.py @@ -215,10 +215,6 @@ def main(): # the db connection in the output mapset dbconn = grassdb.db_connection(force=True) todb = dbconn["database"] - # return all tables - list_fromtable = grass.read_command( - "db.tables", driver="sqlite", database=fromdb - ).splitlines() # return the list of old connection for extract layer number and key dbln = open(os.path.join(new_dir, "dbln"), "r") From 39e1e11fe8349260ba5bfd3794a2e917675cfeb3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 7 Sep 2024 02:53:01 +0000 Subject: [PATCH 192/471] CI(deps): Update alpine:3.20 Docker digest to beefdbd (#4287) --- docker/alpine/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/alpine/Dockerfile b/docker/alpine/Dockerfile index dfd840263e9..a099617759c 100644 --- a/docker/alpine/Dockerfile +++ b/docker/alpine/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.20@sha256:0a4eaa0eecf5f8c050e5bba433f58c052be7587ee8af3e8b3910ef9ab5fbe9f5 as common +FROM alpine:3.20@sha256:beefdbd8a1da6d2915566fde36db9db0b524eb737fc57cd1367effd16dc0d06d as common # Based on: # https://github.com/mundialis/docker-grass-gis/blob/master/Dockerfile From 83ee14690fdd465cd082a8289c5895e9bfc5c732 Mon Sep 17 00:00:00 2001 From: Vaclav Petras Date: Sat, 7 Sep 2024 14:03:11 -0400 Subject: [PATCH 193/471] grass.app: Move ISIS integration to the library (#4169) Path setup for ISIS was lost in GIS variable setup. This moves it to the library, where other paths and integrations are initialized. The variables are left as is, without further testing. --- lib/init/grass.py | 14 -------------- python/grass/app/runtime.py | 20 ++++++++++++++++++++ 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/lib/init/grass.py b/lib/init/grass.py index 87a69f5ef65..82c1ee4a812 100755 --- a/lib/init/grass.py +++ b/lib/init/grass.py @@ -1073,20 +1073,6 @@ def load_env(grass_env_file): # create a new environment variable os.environ[k] = v - # Allow for mixed ISIS-GRASS Environment - if os.getenv("ISISROOT"): - isis = os.getenv("ISISROOT") - os.environ["ISIS_LIB"] = isis + os.sep + "lib" - os.environ["ISIS_3RDPARTY"] = isis + os.sep + "3rdParty" + os.sep + "lib" - os.environ["QT_PLUGIN_PATH"] = isis + os.sep + "3rdParty" + os.sep + "plugins" - # os.environ['ISIS3DATA'] = isis + "$ISIS3DATA" - libpath = os.getenv("LD_LIBRARY_PATH", "") - isislibpath = os.getenv("ISIS_LIB") - isis3rdparty = os.getenv("ISIS_3RDPARTY") - os.environ["LD_LIBRARY_PATH"] = ( - libpath + os.pathsep + isislibpath + os.pathsep + isis3rdparty - ) - def install_notranslation(): # If locale is not supported, _ function might be missing diff --git a/python/grass/app/runtime.py b/python/grass/app/runtime.py index 5da00bae0df..3f3805c9dfd 100644 --- a/python/grass/app/runtime.py +++ b/python/grass/app/runtime.py @@ -129,6 +129,7 @@ def set_paths(install_path, grass_config_dir, ld_library_path_variable_name): # retrieving second time, but now it is always set addon_base = os.getenv("GRASS_ADDON_BASE") set_man_path(install_path=install_path, addon_base=addon_base, env=os.environ) + set_isis() def set_man_path(install_path, addon_base, env): @@ -274,6 +275,25 @@ def set_browser(install_path): os.environ["GRASS_HTML_BROWSER"] = browser +def set_isis(): + """Enable a mixed ISIS-GRASS environment + + ISIS is Integrated Software for Imagers and Spectrometers by USGS. + """ + if os.getenv("ISISROOT"): + isis = os.getenv("ISISROOT") + os.environ["ISIS_LIB"] = isis + os.sep + "lib" + os.environ["ISIS_3RDPARTY"] = isis + os.sep + "3rdParty" + os.sep + "lib" + os.environ["QT_PLUGIN_PATH"] = isis + os.sep + "3rdParty" + os.sep + "plugins" + # os.environ['ISIS3DATA'] = isis + "$ISIS3DATA" + libpath = os.getenv("LD_LIBRARY_PATH", "") + isislibpath = os.getenv("ISIS_LIB") + isis3rdparty = os.getenv("ISIS_3RDPARTY") + os.environ["LD_LIBRARY_PATH"] = ( + libpath + os.pathsep + isislibpath + os.pathsep + isis3rdparty + ) + + def ensure_home(): """Set HOME if not set on MS Windows""" if WINDOWS and not os.getenv("HOME"): From 98e3fe1f4119329fb9533e7623ea1533ccc009af Mon Sep 17 00:00:00 2001 From: ymdatta Date: Sat, 7 Sep 2024 14:03:49 -0400 Subject: [PATCH 194/471] r.out.png: reset file pointer to NULL after fclose (#4220) This patch continues the work from 917ba58. It's a good practice to immediately reset the file pointer once we do fclose on it, as it prevents using/closing descriptor allocated to another file in the future execution paths. Signed-off-by: Mohan Yelugoti --- raster/r.out.png/main.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/raster/r.out.png/main.c b/raster/r.out.png/main.c index 4d3895e5d9f..68bcc09ffef 100644 --- a/raster/r.out.png/main.c +++ b/raster/r.out.png/main.c @@ -369,8 +369,10 @@ int main(int argc, char *argv[]) /* G_free (info_ptr); */ png_destroy_write_struct(&png_ptr, &info_ptr); /* al 11/2000 */ - if (fp) + if (fp) { fclose(fp); + fp = NULL; + } if (wld_flag->answer) { if (do_stdout) From d7f4977a2443f48ea64a1df0d633eab24bdfff92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Sun, 8 Sep 2024 16:09:48 -0400 Subject: [PATCH 195/471] grass.temporal.stds_export: Use pathlib Path.read_text and Path.write_text (#4293) --- python/grass/temporal/stds_export.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/python/grass/temporal/stds_export.py b/python/grass/temporal/stds_export.py index f2fdd7ca9bf..70dbdcada0f 100644 --- a/python/grass/temporal/stds_export.py +++ b/python/grass/temporal/stds_export.py @@ -29,6 +29,7 @@ import shutil import tarfile import tempfile +from pathlib import Path import grass.script as gs from grass.exceptions import CalledModuleError @@ -402,9 +403,7 @@ def export_stds( # Write projection and metadata proj = gs.read_command("g.proj", flags="j") - proj_file = open(proj_file_name, "w") - proj_file.write(proj) - proj_file.close() + Path(proj_file_name).write_text(proj) init_file = open(init_file_name, "w") # Create the init string @@ -431,9 +430,7 @@ def export_stds( init_file.close() metadata = gs.read_command("t.info", type=type_, input=sp.get_id()) - metadata_file = open(metadata_file_name, "w") - metadata_file.write(metadata) - metadata_file.close() + Path(metadata_file_name).write_text(metadata) read_file = open(read_file_name, "w") if type_ == "strds": From 57cb4d91b3b45d6df75c380969d773d798654da4 Mon Sep 17 00:00:00 2001 From: Makiko Shukunobe Date: Mon, 9 Sep 2024 12:03:56 -0400 Subject: [PATCH 196/471] checks: flake8 F841 (local variable assigned to but never used) fixes in scripts directory part 1 (#4238) --- .flake8 | 4 +--- scripts/d.polar/d.polar.py | 9 --------- scripts/r.in.wms/wms_cap_parsers.py | 2 +- scripts/r.in.wms/wms_gdal_drv.py | 1 - 4 files changed, 2 insertions(+), 14 deletions(-) diff --git a/.flake8 b/.flake8 index 47fa3239a24..8a4bf960bf5 100644 --- a/.flake8 +++ b/.flake8 @@ -153,9 +153,7 @@ per-file-ignores = python/grass/*/*/__init__.py: F401, F403 python/grass/*/*/*/__init__.py: F401, F403 # E402 module level import not at top of file - scripts/d.polar/d.polar.py: F841 - scripts/r.in.wms/wms_gdal_drv.py: F841, E722 - scripts/r.in.wms/wms_cap_parsers.py: F841 + scripts/r.in.wms/wms_gdal_drv.py: E722 scripts/r.in.wms/wms_drv.py: E402, E722 scripts/r.in.wms/srs.py: E722 scripts/r.semantic.label/r.semantic.label.py: E501 diff --git a/scripts/d.polar/d.polar.py b/scripts/d.polar/d.polar.py index 4ef578d5676..f592dd89b63 100755 --- a/scripts/d.polar/d.polar.py +++ b/scripts/d.polar/d.polar.py @@ -202,7 +202,6 @@ def plot_eps(psout): epsscale = 0.1 frameallowance = 1.1 halfframe = 3000 - center = (halfframe, halfframe) scale = halfframe / (outerradius * frameallowance) diagramlinewidth = halfframe / 400 @@ -211,11 +210,6 @@ def plot_eps(psout): diagramfontsize = halfframe / 20 halfframe_2 = halfframe * 2 - averagedirectioncolor = 1 # (blue) - diagramcolor = 4 # (red) - circlecolor = 2 # (green) - axescolor = 0 # (black) - northjustification = 2 eastjustification = 6 southjustification = 8 @@ -287,7 +281,6 @@ def plot_eps(psout): ) outf.write(s) - sublength = len(outercircle) - 2 (x, y) = outercircle[1] outf.write("%.2f %.2f moveto\n" % (x * scale + halfframe, y * scale + halfframe)) for x, y in outercircle[2:]: @@ -343,7 +336,6 @@ def plot_eps(psout): ) outf.write(s) - sublength = len(sine_cosine_replic) - 2 (x, y) = sine_cosine_replic[1] outf.write("%.2f %.2f moveto\n" % (x * scale + halfframe, y * scale + halfframe)) for x, y in sine_cosine_replic[2:]: @@ -369,7 +361,6 @@ def plot_eps(psout): s = t.substitute(DIAGRAMLINEWIDTH=diagramlinewidth) outf.write(s) - sublength = len(vector) - 2 (x, y) = vector[1] outf.write("%.2f %.2f moveto\n" % (x * scale + halfframe, y * scale + halfframe)) for x, y in vector[2:]: diff --git a/scripts/r.in.wms/wms_cap_parsers.py b/scripts/r.in.wms/wms_cap_parsers.py index f9b0f7095fb..5483be0979f 100644 --- a/scripts/r.in.wms/wms_cap_parsers.py +++ b/scripts/r.in.wms/wms_cap_parsers.py @@ -131,7 +131,7 @@ def _checkFormats(self, capability): """!Check if format element is defined.""" request = self._find(capability, "Request") get_map = self._find(request, "GetMap") - formats = self._findall(get_map, "Format") + self._findall(get_map, "Format") def _checkLayerTree(self, parent_layer, first=True): """!Recursively check layer tree and manage inheritance in the tree""" diff --git a/scripts/r.in.wms/wms_gdal_drv.py b/scripts/r.in.wms/wms_gdal_drv.py index 6d30f5817f3..d17bac11eee 100644 --- a/scripts/r.in.wms/wms_gdal_drv.py +++ b/scripts/r.in.wms/wms_gdal_drv.py @@ -60,7 +60,6 @@ def _createXML(self): gdal_wms = ET.Element("GDAL_WMS") service = ET.SubElement(gdal_wms, "Service") - name = ET.Element("name") service.set("name", "WMS") version = ET.SubElement(service, "Version") From 7c6c12bf98f5bf405372ee692b880d8b295c2177 Mon Sep 17 00:00:00 2001 From: ymdatta Date: Mon, 9 Sep 2024 12:07:01 -0400 Subject: [PATCH 197/471] v.external: Check for valid list before passing to qsort (#4280) Currently, if 'HAVE_OGR' macro is defined, as part of execution, we order all formats by name using qsort. But, list containing all formats is assigned based on a conditional and if the conditional fails, it can be NULL. Behavior of qsort is undefined when a NULL ptr is passed as array argument. To avoid getting into that situation, check if the array is NULL before performing qsort on it. This issue was found using cppcheck tool. Signed-off-by: Mohan Yelugoti --- vector/v.external/list.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vector/v.external/list.c b/vector/v.external/list.c index 900aede55ab..6c94ea69deb 100644 --- a/vector/v.external/list.c +++ b/vector/v.external/list.c @@ -64,7 +64,8 @@ char **format_list(int *count, size_t *len) } /* order formats by name */ - qsort(list, *count, sizeof(char *), cmp); + if (list) + qsort(list, *count, sizeof(char *), cmp); #endif #if defined HAVE_POSTGRES && !defined HAVE_OGR list = G_realloc(list, ((*count) + 1) * sizeof(char *)); From b1c8d10e0870005ff87344a37eb4438f9daa5420 Mon Sep 17 00:00:00 2001 From: Ondrej Pesek Date: Mon, 9 Sep 2024 19:34:45 +0200 Subject: [PATCH 198/471] wxGUI/gmodeler: dialogs code refactoring (#3816) * g.gui.gmodeler: start refactoring * assign results from _getLabel() directly instead of using intermediate vars * do not run Getlabel() if not needed * VariableListCtrl: simplify (no need to run one loop twice) --- gui/wxpython/gmodeler/dialogs.py | 77 ++++++++++++++++++-------------- 1 file changed, 44 insertions(+), 33 deletions(-) diff --git a/gui/wxpython/gmodeler/dialogs.py b/gui/wxpython/gmodeler/dialogs.py index 0b4038b7b50..7cd7cbd1147 100644 --- a/gui/wxpython/gmodeler/dialogs.py +++ b/gui/wxpython/gmodeler/dialogs.py @@ -56,17 +56,10 @@ def __init__(self, parent, shape, title=_("Data properties")): self.parent = parent self.shape = shape - label, etype = self._getLabel() - self.etype = etype + label, self.etype = self._getLabel() SimpleDialog.__init__(self, parent, title) - self.element = Select( - parent=self.panel, - type=self.shape.GetPrompt(), - validator=SimpleValidator(callback=self.ValidatorCallback), - ) - if shape.GetValue(): - self.element.SetValue(shape.GetValue()) + self.element = self._createElementControl(shape) self.Bind(wx.EVT_BUTTON, self.OnOK, self.btnOK) self.Bind(wx.EVT_BUTTON, self.OnCancel, self.btnCancel) @@ -86,6 +79,18 @@ def __init__(self, parent, shape, title=_("Data properties")): self._layout() self.SetMinSize(self.GetSize()) + def _createElementControl(self, shape): + """Create Select element and set its value.""" + element = Select( + parent=self.panel, + type=self.shape.GetPrompt(), + validator=SimpleValidator(callback=self.ValidatorCallback), + ) + if shape.GetValue(): + element.SetValue(shape.GetValue()) + + return element + def _getLabel(self): etype = False prompt = self.shape.GetPrompt() @@ -549,6 +554,11 @@ def GetCondition(self): """Get loop condition""" return self.condText.GetValue() + def SetSizes(self): + """Set default and minimal size.""" + self.SetMinSize(self.GetSize()) + self.SetSize((500, 400)) + class ModelLoopDialog(ModelItemDialog): """Loop properties dialog""" @@ -573,8 +583,7 @@ def __init__( self.btnSeries.Bind(wx.EVT_BUTTON, self.OnSeries) self._layout() - self.SetMinSize(self.GetSize()) - self.SetSize((500, 400)) + self.SetSizes() def _layout(self): """Do layout""" @@ -664,8 +673,7 @@ def __init__( self.itemListElse.Populate(self.parent.GetModel().GetItems()) self._layout() - self.SetMinSize(self.GetSize()) - self.SetSize((500, 400)) + self.SetSizes() def _layout(self): """Do layout""" @@ -745,21 +753,27 @@ def __init__( listmix.ListCtrlAutoWidthMixin.__init__(self) listmix.TextEditMixin.__init__(self) - i = 0 - for col in columns: - self.InsertColumn(i, col) - self.SetColumnWidth(i, wx.LIST_AUTOSIZE_USEHEADER) - i += 1 + self.InsertColumns(columns) self.itemDataMap = {} # requested by sorter self.itemCount = 0 + self.BindButtons() + + def BindButtons(self): + """Bind signals to buttons.""" self.Bind(wx.EVT_LIST_BEGIN_LABEL_EDIT, self.OnBeginEdit) self.Bind(wx.EVT_LIST_END_LABEL_EDIT, self.OnEndEdit) self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick) self.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnRightUp) # wxMSW self.Bind(wx.EVT_RIGHT_UP, self.OnRightUp) # wxGTK + def InsertColumns(self, columns): + """INsert columns and set their width.""" + for i, col in enumerate(columns): + self.InsertColumn(i, col) + self.SetColumnWidth(i, wx.LIST_AUTOSIZE_USEHEADER) + def OnBeginEdit(self, event): """Editing of item started""" column = event.GetColumn() @@ -799,7 +813,7 @@ def GetData(self): def Populate(self, data): """Populate the list""" - self.itemDataMap = {} + self.DeleteAllItems() i = 0 for name, values in data.items(): self.itemDataMap[i] = [ @@ -808,20 +822,16 @@ def Populate(self, data): values.get("value", ""), values.get("description", ""), ] - i += 1 - - self.itemCount = len(self.itemDataMap.keys()) - self.DeleteAllItems() - i = 0 - for name, vtype, value, desc in self.itemDataMap.values(): index = self.InsertItem(i, name) self.SetItem(index, 0, name) - self.SetItem(index, 1, vtype) - self.SetItem(index, 2, value) - self.SetItem(index, 3, desc) + self.SetItem(index, 1, values["type"]) + self.SetItem(index, 2, values.get("value", "")) + self.SetItem(index, 3, values.get("description", "")) self.SetItemData(index, i) i += 1 + self.itemCount = len(data) + def Append(self, name, vtype, value, desc): """Append new item to the list @@ -1140,11 +1150,12 @@ def OnBeginEdit(self, event): def OnCheckItem(self, index, flag): """Item checked/unchecked""" - name = self.GetLabel() - if name == "IfBlockList" and self.window: - self.window.OnCheckItemIf(index, flag) - elif name == "ElseBlockList" and self.window: - self.window.OnCheckItemElse(index, flag) + if self.window: + name = self.GetLabel() + if name == "IfBlockList": + self.window.OnCheckItemIf(index, flag) + elif name == "ElseBlockList": + self.window.OnCheckItemElse(index, flag) def GetItems(self): """Get list of selected actions""" From cc2797c9f85afb4efb512fb755d902ecf780477f Mon Sep 17 00:00:00 2001 From: Anna Petrasova Date: Mon, 9 Sep 2024 14:38:18 -0400 Subject: [PATCH 199/471] r.univar: fix MASK check with nprocs > 1 (#4297) --- raster/r.univar/r.univar_main.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/raster/r.univar/r.univar_main.c b/raster/r.univar/r.univar_main.c index 84550a87347..805acfff27b 100644 --- a/raster/r.univar/r.univar_main.c +++ b/raster/r.univar/r.univar_main.c @@ -192,6 +192,10 @@ int main(int argc, char *argv[]) sscanf(param.nprocs->answer, "%d", &nprocs); if (nprocs < 1) G_fatal_error(_("<%d> is not valid number of nprocs."), nprocs); + if (nprocs > 1 && G_find_raster("MASK", G_mapset()) != NULL) { + G_warning(_("Parallel processing disabled due to active MASK.")); + nprocs = 1; + } #if defined(_OPENMP) omp_set_num_threads(nprocs); #else @@ -200,10 +204,6 @@ int main(int argc, char *argv[]) "threads setting.")); nprocs = 1; #endif - if (nprocs > 1 && G_find_raster("MASK", G_mapset()) != NULL) { - G_warning(_("Parallel processing disabled due to active MASK.")); - nprocs = 1; - } /* table field separator */ zone_info.sep = G_option_to_separator(param.separator); From 258752c774dc0412dd5ce9ff68088ee276c41e48 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 18:47:43 +0000 Subject: [PATCH 200/471] CI(deps): Update DeterminateSystems/nix-installer-action action to v14 (#4298) --- .github/workflows/test-nix.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-nix.yml b/.github/workflows/test-nix.yml index a73ac835414..22fa4f0061f 100644 --- a/.github/workflows/test-nix.yml +++ b/.github/workflows/test-nix.yml @@ -31,7 +31,7 @@ jobs: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Install nix - uses: DeterminateSystems/nix-installer-action@ab6bcb2d5af0e904d04aea750e2089e9dc4cbfdd # v13 + uses: DeterminateSystems/nix-installer-action@da36cb69b1c3247ad7a1f931ebfd954a1105ef14 # v14 - name: Setup cachix uses: cachix/cachix-action@ad2ddac53f961de1989924296a1f236fcfbaa4fc # v15 From b9c19ab5475c7c24b617eb75adaf5851c69fec09 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 16:53:54 -0400 Subject: [PATCH 201/471] CI(deps): Update ruff to v0.6.4 (#4279) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * CI(deps): Update ruff to v0.6.4 * style: Fix List index lookup in `enumerate()` loop (PLR1736) in __writeNvizState * style: Fix Unnecessary lookup of dictionary value by key (PLR1733) in iscatt_core.py * style: Fix `enumerate` index is unused, use `for plane in constants` instead (FURB148) in __writeNvizState --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Edouard Choinière <27212526+echoix@users.noreply.github.com> --- .github/workflows/python-code-quality.yml | 2 +- .pre-commit-config.yaml | 2 +- gui/wxpython/core/workspace.py | 8 ++++---- gui/wxpython/iscatt/iscatt_core.py | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index caa530e3b39..17cce1c09aa 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -36,7 +36,7 @@ jobs: # renovate: datasource=pypi depName=bandit BANDIT_VERSION: "1.7.9" # renovate: datasource=pypi depName=ruff - RUFF_VERSION: "0.6.3" + RUFF_VERSION: "0.6.4" runs-on: ${{ matrix.os }} permissions: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f12fde12473..a5221f29efe 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -37,7 +37,7 @@ repos: ) - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.6.3 + rev: v0.6.4 hooks: # Run the linter. - id: ruff diff --git a/gui/wxpython/core/workspace.py b/gui/wxpython/core/workspace.py index 7e5f85d6649..63f8b91cb89 100644 --- a/gui/wxpython/core/workspace.py +++ b/gui/wxpython/core/workspace.py @@ -1608,15 +1608,15 @@ def __writeNvizState(self, view, iview, light, constants): if constants: self.file.write("%s\n" % (" " * self.indent)) self.indent += 4 - for idx, plane in enumerate(constants): + for plane in constants: self.file.write("%s\n" % (" " * self.indent)) self.indent += 4 - self.__writeTagWithValue("height", constants[idx]["constant"]["value"]) + self.__writeTagWithValue("height", plane["constant"]["value"]) self.__writeTagWithValue( - "fine_resolution", constants[idx]["constant"]["resolution"] + "fine_resolution", plane["constant"]["resolution"] ) self.__writeTagWithValue( - "color", constants[idx]["constant"]["color"], format="s" + "color", plane["constant"]["color"], format="s" ) self.indent -= 4 self.file.write("%s\n" % (" " * self.indent)) diff --git a/gui/wxpython/iscatt/iscatt_core.py b/gui/wxpython/iscatt/iscatt_core.py index 97da83415e8..91ac00014a5 100644 --- a/gui/wxpython/iscatt/iscatt_core.py +++ b/gui/wxpython/iscatt/iscatt_core.py @@ -531,7 +531,7 @@ def SetData(self, cats): # if key is missing condition is always True (full scatter plor # is computed) if scatt_id in self.cats[cat_id]: - self.cats[cat_id][scatt_id]["np_vals"] = cats[cat_id][scatt_id][ + self.cats[cat_id][scatt_id]["np_vals"] = scatt_ids[scatt_id][ "np_vals" ] From 85efe798c6106a18d562799a64453787ebc5a63f Mon Sep 17 00:00:00 2001 From: ymdatta Date: Mon, 9 Sep 2024 17:03:03 -0400 Subject: [PATCH 202/471] v.out.ogr: Check for valid array before passing to qsort (#4278) Currently, filling list with GDAL driver names is based on a conditional and if the conditional fails, the list can be NULL. We pass this list to qsort to sort the names, but behavior of qsort with a NULL array is undefined. To avoid getting into this situation, check if the array is NULL before performing qsort on it. This issue was found using cppcheck tool. Signed-off-by: Mohan Yelugoti --- vector/v.out.ogr/list.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vector/v.out.ogr/list.c b/vector/v.out.ogr/list.c index 0df74cf5dac..2b80111de3e 100644 --- a/vector/v.out.ogr/list.c +++ b/vector/v.out.ogr/list.c @@ -43,7 +43,8 @@ char *OGR_list_write_drivers(void) len += strlen(buf) + 1; /* + ',' */ } - qsort(list, count, sizeof(char *), cmp); + if (list) + qsort(list, count, sizeof(char *), cmp); if (len > 0) { ret = G_malloc((len + 1) * sizeof(char)); /* \0 */ From 997624b5bfa4e8141d5b742367d51fef97273efb Mon Sep 17 00:00:00 2001 From: Chung-Yuan Liang <77927944+cyliang368@users.noreply.github.com> Date: Mon, 9 Sep 2024 20:31:43 -0400 Subject: [PATCH 203/471] r.proj: Create unit tests (#4225) * create unit tests for r.proj * change the source project * update the validation values according to the map in the project * remove shell cmd, update doc, modify precision --- raster/r.proj/testsuite/test_rproj.py | 186 ++++++++++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100644 raster/r.proj/testsuite/test_rproj.py diff --git a/raster/r.proj/testsuite/test_rproj.py b/raster/r.proj/testsuite/test_rproj.py new file mode 100644 index 00000000000..5f3834be881 --- /dev/null +++ b/raster/r.proj/testsuite/test_rproj.py @@ -0,0 +1,186 @@ +#!/usr/bin/env python + +############################################################################## +# MODULE: r.proj +# +# AUTHOR(S): Chung-Yuan Liang +# +# PURPOSE: Unit tests for r.proj +# +# COPYRIGHT: (C) 2024 Chung-Yuan Liang and the GRASS Development Team +# +# This program is free software under the GNU General Public +# License (>=v2). Read the file COPYING that comes with GRASS +# for details. +############################################################################## + +from grass.gunittest.case import TestCase +from grass.gunittest.gmodules import call_module +import shutil + +raster_info = """north=35.8096296297222 +south=35.6874074075 +east=-78.608 +west=-78.7746666666667 +nsres=0.000740740740740727 +ewres=0.000666666666666686 +rows=165 +cols=250 +cells=41250""" + +src_project = "nc_spm_full_v2alpha2" +dst_project = "nc_latlong" + + +class TestRasterreport(TestCase): + input = "elevation" + + @classmethod + def setUpClass(cls): + cls.runModule("g.proj", project=dst_project, epsg="4326", flags="c") + cls.runModule("g.mapset", mapset="PERMANENT", project=dst_project) + + @classmethod + def tearDownClass(cls): + cls.runModule("g.mapset", mapset="PERMANENT", project=src_project) + dbase = call_module("g.gisenv", get="GISDBASE") + shutil.rmtree(f"{dbase}/{dst_project}") + + def run_rproj_test(self, method, statics): + """The main function to run r.proj check rsults according to the method + + Parameters + ---------- + method : str + The method to be used for r.proj + statics : str + The expected statics of the output raster + """ + output = method + ## Get the boundary and set up region for the projected map + stdout = call_module( + "r.proj", + project=src_project, + mapset="PERMANENT", + input=self.input, + method=method, + flags="g", + ) + settings = dict([line.split("=") for line in stdout.split()]) + + call_module( + "g.region", + n=settings["n"], + s=settings["s"], + e=settings["e"], + w=settings["w"], + rows=settings["rows"], + cols=settings["cols"], + flags="a", + res=1, + ) + + ## Project the map + self.assertModule( + "r.proj", + project=src_project, + mapset="PERMANENT", + input=self.input, + output=output, + method=method, + quiet=True, + ) + + ## Validate the output + self.assertRasterFitsUnivar(output, reference=statics, precision=1e-7) + self.assertRasterFitsInfo(output, reference=raster_info, precision=1e-7) + + def test_nearest(self): + """Testing method nearest""" + ## Set up variables and validation values + method = "nearest" + statics = """n=40930 + min=55.5787925720215 + max=156.038833618164 + mean=110.377538633405 + variance=412.751942806146""" + + self.run_rproj_test(method, statics) + + def test_bilinear(self): + """Testing method bilinear""" + ## Set up variables and validation values + method = "bilinear" + statics = """n=40845 + min=56.3932914733887 + max=156.053298950195 + mean=110.389074372679 + variance=411.487781666933""" + + self.run_rproj_test(method, statics) + + def test_bicubic(self): + """Testing method bicubic""" + ## Set up variables and validation values + method = "bicubic" + statics = """n=40677 + min=56.2407836914062 + max=156.061599731445 + mean=110.41701776258 + variance=411.382636894393""" + + self.run_rproj_test(method, statics) + + def test_lanczos(self): + """Testing method lanczos""" + ## Set up variables and validation values + method = "lanczos" + statics = """n=40585 + min=56.2350921630859 + max=156.066345214844 + mean=110.421826400841 + variance=411.6875834341575""" + + self.run_rproj_test(method, statics) + + def test_bilinear_f(self): + """Testing method bilinear_f""" + ## Set up variables and validation values + method = "bilinear_f" + statics = """n=40930 + min=55.5787925720215 + max=156.053298950195 + mean=110.376211041027 + variance=412.553041205029""" + + self.run_rproj_test(method, statics) + + def test_bicubic_f(self): + """Testing method bicubic_f""" + ## Set up variables and validation values + method = "bicubic_f" + statics = """n=40930 + min=55.5787925720215 + max=156.061599731445 + mean=110.375897704515 + variance=412.693308000461""" + + self.run_rproj_test(method, statics) + + def test_lanczos_f(self): + """Testing method lanczos_f""" + ## Set up variables and validation values + method = "lanczos_f" + statics = """n=40930 + min=55.5787925720215 + max=156.066345214844 + mean=110.375715222838 + variance=412.695433658258""" + + self.run_rproj_test(method, statics) + + +if __name__ == "__main__": + from grass.gunittest.main import test + + test() From a85c7b0b828152cd0e3aaa3f56d1b438bd137863 Mon Sep 17 00:00:00 2001 From: ymdatta Date: Tue, 10 Sep 2024 05:58:23 -0400 Subject: [PATCH 204/471] v.vol.rst: Initialize structure contents before passing it around (#4282) * v.vol.rst: Initialize structure contents before passing it around `skip_point` structure is not initialized when declared. This was found using cppcheck tool. Only when `cv` is true, `skip_point` contents are filled and the structure is used. cppcheck raised the issue when 'cv' is false. Technically, we don't need skip_point in such cases and not initializing it won't affect the execution. Nevertheless, it's a good practice to initialize structure contents at the time of declaration. Signed-off-by: Mohan Yelugoti * Use correct literal for double type Co-authored-by: Nicklas Larsson --------- Signed-off-by: Mohan Yelugoti Co-authored-by: Nicklas Larsson --- vector/v.vol.rst/user2.c | 1 + 1 file changed, 1 insertion(+) diff --git a/vector/v.vol.rst/user2.c b/vector/v.vol.rst/user2.c index 64f7b4a67f2..49a36d39175 100644 --- a/vector/v.vol.rst/user2.c +++ b/vector/v.vol.rst/user2.c @@ -92,6 +92,7 @@ int interp_call(struct octtree *root, struct octtree *tree) int skip_index, segtest; double xx, yy, zz /*, ww */; + skip_point.x = skip_point.y = skip_point.z = skip_point.w = 0.0; if (tree == NULL) return -1; if (tree->data == NULL) From 76507cc9ba7ad1259569bc0730592b4860c1a3b9 Mon Sep 17 00:00:00 2001 From: ymdatta Date: Tue, 10 Sep 2024 05:59:10 -0400 Subject: [PATCH 205/471] v.generalize: Initialize all of structure contents before using it (#4281) * v.generalize: Initialize all of structure contents before using it Currently, in `head` which is a `POINT_LIST` structure, we are only initializing the next pointer to NULL, but data present in the point substructure is not initialized which implies that it would be filled with random data. To avoid such scenario, initialize point coordinates to zero. This was found using cppcheck tool. Signed-off-by: Mohan Yelugoti * Use correct literal for double type Co-authored-by: Nicklas Larsson --------- Signed-off-by: Mohan Yelugoti Co-authored-by: Nicklas Larsson --- vector/v.generalize/smoothing.c | 1 + 1 file changed, 1 insertion(+) diff --git a/vector/v.generalize/smoothing.c b/vector/v.generalize/smoothing.c index d5a817f8f2f..0b5d9a5e12f 100644 --- a/vector/v.generalize/smoothing.c +++ b/vector/v.generalize/smoothing.c @@ -422,6 +422,7 @@ int hermite(struct line_pnts *Points, double step, double angle_thresh, angle_thresh *= M_PI / 180.0; head.next = NULL; + head.p.x = head.p.y = head.p.z = 0.0; point = last = &head; if (!is_loop) { From adcdf28bb2ff1b4602bf7df898a8040ba1f1309e Mon Sep 17 00:00:00 2001 From: Anna Petrasova Date: Tue, 10 Sep 2024 11:40:14 -0400 Subject: [PATCH 206/471] t.rast.accumulate: remove printing (#4301) --- temporal/t.rast.accumulate/t.rast.accumulate.py | 1 - 1 file changed, 1 deletion(-) diff --git a/temporal/t.rast.accumulate/t.rast.accumulate.py b/temporal/t.rast.accumulate/t.rast.accumulate.py index 3ad82fe9ed3..94344f2d9cc 100644 --- a/temporal/t.rast.accumulate/t.rast.accumulate.py +++ b/temporal/t.rast.accumulate/t.rast.accumulate.py @@ -483,7 +483,6 @@ def main(): if method: accmod.inputs["method"].value = method - print(accmod) accmod.run() if accmod.returncode != 0: From e4fdf904c32f90c96c038d4cb2c5f67e6cc0da81 Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Wed, 11 Sep 2024 08:31:52 -0400 Subject: [PATCH 207/471] CI: Resolve flake8 issues in utils directory (#4303) * resolved flake8 in utils * updated .flake8 --------- Co-authored-by: Arohan Ajit --- .flake8 | 1 - utils/gitlog2changelog.py | 8 ++++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.flake8 b/.flake8 index 8a4bf960bf5..32021622d7d 100644 --- a/.flake8 +++ b/.flake8 @@ -21,7 +21,6 @@ per-file-ignores = # E741 ambiguous variable name 'l' __init__.py: F401, F403 lib/init/grass.py: E722, F821, F841 - utils/gitlog2changelog.py: E722, E712 man/build_check_rest.py: F403, F405 man/build_full_index_rest.py: F403, F405 man/parser_standard_options.py: F403, F405 diff --git a/utils/gitlog2changelog.py b/utils/gitlog2changelog.py index 2626321d6fb..758b142196b 100755 --- a/utils/gitlog2changelog.py +++ b/utils/gitlog2changelog.py @@ -66,8 +66,8 @@ author = authorList[1] author = author[0 : len(author) - 1] authorFound = True - except: - print("Could not parse authorList = '%s'" % (line)) + except Exception as e: + print(f"Could not parse authorList = '{line}'. Error: {e!s}") # Match the date line elif line.startswith("Date:"): @@ -76,8 +76,8 @@ date = dateList[1] date = date[0 : len(date) - 1] dateFound = True - except: - print("Could not parse dateList = '%s'" % (line)) + except Exception as e: + print(f"Could not parse dateList = '{line}'. Error: {e!s}") # The Fossil-IDs are ignored: elif line.startswith(" Fossil-ID:") or line.startswith(" [[SVN:"): continue From 569c730be0eb42de9f31eae1c8e78f7ee7cb7e92 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 11 Sep 2024 17:17:11 -0400 Subject: [PATCH 208/471] CI(deps): Update docker/dockerfile Docker tag to v1.10 (#4305) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Dockerfile | 2 +- docker/ubuntu/Dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 23bf11ddf27..aba1df48a1d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -# syntax=docker/dockerfile:1.9@sha256:fe40cf4e92cd0c467be2cfc30657a680ae2398318afd50b0c80585784c604f28 +# syntax=docker/dockerfile:1.10@sha256:865e5dd094beca432e8c0a1d5e1c465db5f998dca4e439981029b3b81fb39ed5 # Note: This file must be kept in sync in ./Dockerfile and ./docker/ubuntu/Dockerfile. # Changes to this file must be copied over to the other file. diff --git a/docker/ubuntu/Dockerfile b/docker/ubuntu/Dockerfile index 23bf11ddf27..aba1df48a1d 100644 --- a/docker/ubuntu/Dockerfile +++ b/docker/ubuntu/Dockerfile @@ -1,4 +1,4 @@ -# syntax=docker/dockerfile:1.9@sha256:fe40cf4e92cd0c467be2cfc30657a680ae2398318afd50b0c80585784c604f28 +# syntax=docker/dockerfile:1.10@sha256:865e5dd094beca432e8c0a1d5e1c465db5f998dca4e439981029b3b81fb39ed5 # Note: This file must be kept in sync in ./Dockerfile and ./docker/ubuntu/Dockerfile. # Changes to this file must be copied over to the other file. From 5719ba6fd51158a88fb7ce1850a4651e61c7e344 Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Wed, 11 Sep 2024 20:59:21 -0400 Subject: [PATCH 209/471] CI: Update .flake8 configuration and fix flake8 errors in lib/init/grass.py (#4289) * fixed flake8 errors in lib/init/grass.py * fixed flake8 details .flake8 --- .flake8 | 1 - lib/init/grass.py | 10 +++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/.flake8 b/.flake8 index 32021622d7d..5d3f2fca5d0 100644 --- a/.flake8 +++ b/.flake8 @@ -20,7 +20,6 @@ per-file-ignores = # F841 local variable assigned to but never used # E741 ambiguous variable name 'l' __init__.py: F401, F403 - lib/init/grass.py: E722, F821, F841 man/build_check_rest.py: F403, F405 man/build_full_index_rest.py: F403, F405 man/parser_standard_options.py: F403, F405 diff --git a/lib/init/grass.py b/lib/init/grass.py index 82c1ee4a812..5a62e65ac53 100755 --- a/lib/init/grass.py +++ b/lib/init/grass.py @@ -484,7 +484,7 @@ def create_gisrc(tmpdir, gisrcrc): if "UNKNOWN" in s: try_remove(gisrcrc) s = None - except: + except Exception: s = None # Copy the global grassrc file to the session grassrc file @@ -1162,7 +1162,7 @@ def set_language(grass_config_dir): encoding = "UTF-8" normalized = locale.normalize("%s.%s" % (language, encoding)) locale.setlocale(locale.LC_ALL, normalized) - except locale.Error as e: + except locale.Error: if language == "en": # A workaround for Python Issue30755 # https://bugs.python.org/issue30755 @@ -1188,7 +1188,7 @@ def set_language(grass_config_dir): # See bugs #3441 and #3423 try: locale.setlocale(locale.LC_ALL, "C.UTF-8") - except locale.Error as e: + except locale.Error: # All lost. Setting to C as much as possible. # We can not call locale.normalize on C as it # will transform it to en_US and we already know @@ -1571,7 +1571,7 @@ def say_hello(): revision = linerev.split(" ")[1] sys.stderr.write(" (" + revision + ")") - except: + except Exception: pass @@ -1937,7 +1937,7 @@ def print_params(params): try: revision = linerev.split(" ")[1] sys.stdout.write("%s\n" % revision[1:]) - except: + except Exception: sys.stdout.write("No SVN revision defined\n") elif arg == "version": sys.stdout.write("%s\n" % GRASS_VERSION) From dda0fb26bb4b62059cf3d7986227709cbecb279d Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Thu, 12 Sep 2024 08:22:54 -0400 Subject: [PATCH 210/471] lib/db: Fix copy into fixed size buffer issue in SQLite driver (#4255) * drivers: copy into fixed size buffer issue * Requested changes * without variable * Update db/drivers/sqlite/db.c Co-authored-by: Nicklas Larsson * Use Db statements --------- Co-authored-by: Nicklas Larsson --- db/drivers/sqlite/db.c | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/db/drivers/sqlite/db.c b/db/drivers/sqlite/db.c index 518ba6c978c..cd86e4648f6 100644 --- a/db/drivers/sqlite/db.c +++ b/db/drivers/sqlite/db.c @@ -78,7 +78,11 @@ int db__driver_open_database(dbHandle *handle) G_free_tokens(tokens); } else { - strcpy(name2, name); + if (G_strlcpy(name2, name, sizeof(name2)) >= sizeof(name2)) { + db_d_append_error(_("Database name <%s> is too long"), name); + db_d_report_error(); + return DB_FAILED; + } } G_debug(2, "name2 = '%s'", name2); @@ -114,11 +118,20 @@ int db__driver_open_database(dbHandle *handle) else { G_warning(_("The sqlite config option '%s' is not supported"), "SQLITE_CONFIG_URI"); - strcpy(name3, name2); + if (G_strlcpy(name3, name2, sizeof(name3)) >= sizeof(name3)) { + db_d_append_error(_("Database name <%s> is too long"), name2); + db_d_report_error(); + return DB_FAILED; + } + } + } + else { + if (G_strlcpy(name3, name2, sizeof(name3)) >= sizeof(name3)) { + db_d_append_error(_("Database name <%s> is too long"), name2); + db_d_report_error(); + return DB_FAILED; } } - else - strcpy(name3, name2); if (sqlite3_open(name3, &sqlite) != SQLITE_OK) { db_d_append_error("%s %s\n%s", _("Unable to open database:"), name3, (char *)sqlite3_errmsg(sqlite)); @@ -184,11 +197,20 @@ int db__driver_create_database(dbHandle *handle) else { G_warning(_("The sqlite config option '%s' is not supported"), "SQLITE_CONFIG_URI"); - strcpy(name2, name); + if (G_strlcpy(name2, name, sizeof(name2)) >= sizeof(name2)) { + db_d_append_error(_("Database name <%s> is too long"), name); + db_d_report_error(); + return DB_FAILED; + } + } + } + else { + if (G_strlcpy(name2, name, sizeof(name2)) >= sizeof(name2)) { + db_d_append_error(_("Database name <%s> is too long"), name); + db_d_report_error(); + return DB_FAILED; } } - else - strcpy(name2, name); if (sqlite3_open(name2, &sqlite) != SQLITE_OK) { db_d_append_error("%s %s\n%s", _("Unable to create database:"), name, (char *)sqlite3_errmsg(sqlite)); From 4d5184d5d59a4950ee3c608798b423775472394e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 12 Sep 2024 13:37:17 +0000 Subject: [PATCH 211/471] CI(deps): Update peter-evans/create-pull-request action to v7.0.2 (#4311) --- .github/workflows/periodic_update.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/periodic_update.yml b/.github/workflows/periodic_update.yml index 346fd12e2d2..ce03bf8e281 100644 --- a/.github/workflows/periodic_update.yml +++ b/.github/workflows/periodic_update.yml @@ -33,7 +33,7 @@ jobs: run: git status --ignored - name: Create Pull Request id: cpr - uses: peter-evans/create-pull-request@8867c4aba1b742c39f8d0ba35429c2dfa4b6cb20 # v7.0.1 + uses: peter-evans/create-pull-request@d121e62763d8cc35b5fb1710e887d6e69a52d3a4 # v7.0.2 with: commit-message: "config.guess + config.sub: updated from http://git.savannah.gnu.org/cgit/config.git/plain/" branch: periodic/update-configure From 5de8682c5bdf122f832619926010abbc811395cc Mon Sep 17 00:00:00 2001 From: rohannallamadge Date: Thu, 12 Sep 2024 20:58:38 +0530 Subject: [PATCH 212/471] GUI: fix ColumnSorterMixin regression (#4310) * fixing TypeError ColumnSorterMixin #4277 * #4277 fix ColumnSorterMixin error --- gui/wxpython/gcp/manager.py | 2 +- gui/wxpython/image2target/ii2t_manager.py | 2 +- gui/wxpython/photo2image/ip2i_manager.py | 2 +- gui/wxpython/vnet/widgets.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/gui/wxpython/gcp/manager.py b/gui/wxpython/gcp/manager.py index ac006a95cbf..749cbf79efb 100644 --- a/gui/wxpython/gcp/manager.py +++ b/gui/wxpython/gcp/manager.py @@ -1272,7 +1272,7 @@ def InitMapDisplay(self): # initialize column sorter self.itemDataMap = self.mapcoordlist ncols = self.list.GetColumnCount() - ColumnSorterMixin(self, ncols) + ColumnSorterMixin.__init__(self, ncols) # noqa: PLC2801, C2801 # init to ascending sort on first click self._colSortFlag = [1] * ncols diff --git a/gui/wxpython/image2target/ii2t_manager.py b/gui/wxpython/image2target/ii2t_manager.py index 4aec0eeba5e..17811ceceb1 100644 --- a/gui/wxpython/image2target/ii2t_manager.py +++ b/gui/wxpython/image2target/ii2t_manager.py @@ -1257,7 +1257,7 @@ def InitMapDisplay(self): # initialize column sorter self.itemDataMap = self.mapcoordlist ncols = self.list.GetColumnCount() - ColumnSorterMixin(self, ncols) + ColumnSorterMixin.__init__(self, ncols) # noqa: PLC2801, C2801 # init to ascending sort on first click self._colSortFlag = [1] * ncols diff --git a/gui/wxpython/photo2image/ip2i_manager.py b/gui/wxpython/photo2image/ip2i_manager.py index 2a9ae19d26a..f251a1fa36e 100644 --- a/gui/wxpython/photo2image/ip2i_manager.py +++ b/gui/wxpython/photo2image/ip2i_manager.py @@ -625,7 +625,7 @@ def InitMapDisplay(self): # initialize column sorter self.itemDataMap = self.mapcoordlist ncols = self.list.GetColumnCount() - ColumnSorterMixin(self, ncols) + ColumnSorterMixin.__init__(self, ncols) # noqa: PLC2801, C2801 # init to ascending sort on first click self._colSortFlag = [1] * ncols diff --git a/gui/wxpython/vnet/widgets.py b/gui/wxpython/vnet/widgets.py index 2690ba57ab4..12477cc93ea 100644 --- a/gui/wxpython/vnet/widgets.py +++ b/gui/wxpython/vnet/widgets.py @@ -133,7 +133,7 @@ def __init__( # initialize column sorter self.itemDataMap = [] ncols = self.GetColumnCount() - ColumnSorterMixin.__init__(self, ncols) + ColumnSorterMixin.__init__(self, ncols) # noqa: PLC2801, C2801 # init to ascending sort on first click self._colSortFlag = [1] * ncols From 8c23c2ea61ff96ab8d161dacf6102bcf73f49cda Mon Sep 17 00:00:00 2001 From: Makiko Shukunobe Date: Thu, 12 Sep 2024 11:49:18 -0400 Subject: [PATCH 213/471] Checks: flake8 F841 (local variable assigned to but never used) fixes in the temporal directory (#4229) --- .flake8 | 7 ++----- temporal/t.rast.what/t.rast.what.py | 1 - temporal/t.vect.algebra/t.vect.algebra.py | 1 - 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/.flake8 b/.flake8 index 5d3f2fca5d0..4afa5809c80 100644 --- a/.flake8 +++ b/.flake8 @@ -169,13 +169,10 @@ per-file-ignores = scripts/v.what.strds/v.what.strds.py: E722, E501 # Line too long (esp. module interface definitions) scripts/*/*.py: E501 - # local variable 'column' is assigned to but never used temporal/t.rast.to.vect/t.rast.to.vect.py: E501 - # local variable 'stdstype' is assigned to but never used - temporal/t.vect.algebra/t.vect.algebra.py: F841, E501 + temporal/t.vect.algebra/t.vect.algebra.py: E501 # ## used (##% key: r etc) - # local variable 'map_list' is assigned to but never used - temporal/t.rast.what/t.rast.what.py: E265, E266, F841, E501 + temporal/t.rast.what/t.rast.what.py: E265, E266, E501 # Line too long (esp. module interface definitions) temporal/*/*.py: E501 diff --git a/temporal/t.rast.what/t.rast.what.py b/temporal/t.rast.what/t.rast.what.py index 9fa4ad5d793..6a4efbb2b94 100755 --- a/temporal/t.rast.what/t.rast.what.py +++ b/temporal/t.rast.what/t.rast.what.py @@ -466,7 +466,6 @@ def one_point_per_col_output( for count in range(len(output_files)): file_name = output_files[count] gs.verbose(_("Transforming r.what output file %s" % (file_name))) - map_list = output_time_list[count] in_file = open(file_name, "r") lines = in_file.readlines() diff --git a/temporal/t.vect.algebra/t.vect.algebra.py b/temporal/t.vect.algebra/t.vect.algebra.py index 5da83398404..852df8c3afe 100644 --- a/temporal/t.vect.algebra/t.vect.algebra.py +++ b/temporal/t.vect.algebra/t.vect.algebra.py @@ -64,7 +64,6 @@ def main(): expression = options["expression"] basename = options["basename"] spatial = flags["s"] - stdstype = "stvds" # Check for PLY istallation try: From b7bcd066e99893a521019f3ef6a497163f304d9e Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Thu, 12 Sep 2024 12:59:10 -0400 Subject: [PATCH 214/471] CI: Fix Flake8 linter errors in man/ directory (#4292) * resolved some more flake8 warnings * fixed all man flake8 errors except buildhtml * Update .flake8 --------- Co-authored-by: Arohan Ajit --- .flake8 | 11 ----------- man/build_check.py | 2 +- man/build_check_rest.py | 2 +- man/build_class.py | 15 ++++++++++++++- man/build_class_rest.py | 14 +++++++++++++- man/build_full_index.py | 16 +++++++++++++++- man/build_full_index_rest.py | 15 ++++++++++++++- man/build_index.py | 9 ++++++++- man/build_index_rest.py | 9 ++++++++- man/build_keywords.py | 18 ++++++++++++------ man/build_topics.py | 16 ++++++++++++---- 11 files changed, 98 insertions(+), 29 deletions(-) diff --git a/.flake8 b/.flake8 index 4afa5809c80..3182812dd1d 100644 --- a/.flake8 +++ b/.flake8 @@ -20,17 +20,6 @@ per-file-ignores = # F841 local variable assigned to but never used # E741 ambiguous variable name 'l' __init__.py: F401, F403 - man/build_check_rest.py: F403, F405 - man/build_full_index_rest.py: F403, F405 - man/parser_standard_options.py: F403, F405 - man/build_class.py: F403, F405 - man/build_class_rest.py: F403, F405 - man/build_check.py: F403, F405 - man/build_full_index.py: F403, F405 - man/build_index.py: F403, F405 - man/build_index_rest.py: F403, F405 - man/build_keywords.py: F403, F405, E722 - man/build_topics.py: F403, F405, E722 man/build_html.py: E501 imagery/i.atcorr/create_iwave.py: F632, F821, W293 doc/python/raster_example_ctypes.py: F403, F405 diff --git a/man/build_check.py b/man/build_check.py index 6111a4dbe9f..9d9675fb7ac 100644 --- a/man/build_check.py +++ b/man/build_check.py @@ -9,7 +9,7 @@ import sys import os -from build_html import * +from build_html import html_dir, message_tmpl, html_files, read_file os.chdir(html_dir) diff --git a/man/build_check_rest.py b/man/build_check_rest.py index 159be8453db..f9868dfd697 100644 --- a/man/build_check_rest.py +++ b/man/build_check_rest.py @@ -9,7 +9,7 @@ import sys import os -from build_rest import * +from build_rest import rest_dir, message_tmpl, rest_files, read_file os.chdir(rest_dir) diff --git a/man/build_class.py b/man/build_class.py index 8300c62079f..564fb5c20e6 100644 --- a/man/build_class.py +++ b/man/build_class.py @@ -9,7 +9,20 @@ import sys import os -from build_html import * +from build_html import ( + html_dir, + write_html_header, + grass_version, + modclass_intro_tmpl, + modclass_tmpl, + to_title, + html_files, + check_for_desc_override, + get_desc, + desc2_tmpl, + write_html_footer, + replace_file, +) no_intro_page_classes = ["display", "general", "miscellaneous", "postscript"] diff --git a/man/build_class_rest.py b/man/build_class_rest.py index c1de3d623d0..05aec125d04 100644 --- a/man/build_class_rest.py +++ b/man/build_class_rest.py @@ -9,7 +9,19 @@ import sys import os -from build_rest import * +from build_rest import ( + rest_dir, + grass_version, + modclass_intro_tmpl, + modclass_tmpl, + desc2_tmpl, + write_rest_header, + write_rest_footer, + rest_files, + check_for_desc_override, + get_desc, + replace_file, +) os.chdir(rest_dir) diff --git a/man/build_full_index.py b/man/build_full_index.py index 76379787905..ead5167cbb1 100644 --- a/man/build_full_index.py +++ b/man/build_full_index.py @@ -9,7 +9,21 @@ import sys import os -from build_html import * +from build_html import ( + html_dir, + grass_version, + html_files, + write_html_header, + write_html_footer, + check_for_desc_override, + get_desc, + replace_file, + to_title, + full_index_header, + toc, + cmd2_tmpl, + desc1_tmpl, +) year = None if len(sys.argv) > 1: diff --git a/man/build_full_index_rest.py b/man/build_full_index_rest.py index 14167df0f84..26183505393 100644 --- a/man/build_full_index_rest.py +++ b/man/build_full_index_rest.py @@ -8,7 +8,20 @@ import os -from build_rest import * +from build_rest import ( + rest_dir, + rest_files, + write_rest_header, + grass_version, + full_index_header, + sections, + cmd2_tmpl, + check_for_desc_override, + get_desc, + desc1_tmpl, + write_rest_footer, + replace_file, +) os.chdir(rest_dir) diff --git a/man/build_index.py b/man/build_index.py index 5a01787afe3..12de7f01162 100644 --- a/man/build_index.py +++ b/man/build_index.py @@ -9,7 +9,14 @@ import sys import os -from build_html import * +from build_html import ( + html_dir, + grass_version, + write_html_header, + write_html_cmd_overview, + write_html_footer, + replace_file, +) os.chdir(html_dir) diff --git a/man/build_index_rest.py b/man/build_index_rest.py index 68463bbb753..b919042e4af 100644 --- a/man/build_index_rest.py +++ b/man/build_index_rest.py @@ -9,7 +9,14 @@ import os -from build_rest import * +from build_rest import ( + rest_dir, + grass_version, + write_rest_header, + write_rest_cmd_overview, + write_rest_footer, + replace_file, +) os.chdir(rest_dir) diff --git a/man/build_keywords.py b/man/build_keywords.py index 477c1a131ff..6070fad1a28 100644 --- a/man/build_keywords.py +++ b/man/build_keywords.py @@ -21,7 +21,13 @@ import os import sys import glob -from build_html import * +from build_html import ( + grass_version, + header1_tmpl, + headerkeywords_tmpl, + write_html_footer, +) + blacklist = [ "Display", @@ -81,17 +87,17 @@ def get_module_man_html_file_path(module): try: index_keys = lines.index("

KEYWORDS

\n") + 1 index_desc = lines.index("

NAME

\n") + 1 - except: + except Exception: continue try: keys = lines[index_keys].split(",") - except: + except Exception: continue for key in keys: key = key.strip() try: key = key.split(">")[1].split("<")[0] - except: + except Exception: pass if not key: sys.exit("Empty keyword from file %s line: %s" % (fname, lines[index_keys])) @@ -104,10 +110,10 @@ def get_module_man_html_file_path(module): for black in blacklist: try: del keywords[black] - except: + except Exception: try: del keywords[black.lower()] - except: + except Exception: continue for key in sorted(keywords.keys()): diff --git a/man/build_topics.py b/man/build_topics.py index bd0606d1aeb..be5f2962d2c 100644 --- a/man/build_topics.py +++ b/man/build_topics.py @@ -6,7 +6,15 @@ import os import sys import glob -from build_html import * +from build_html import ( + grass_version, + header1_tmpl, + headertopics_tmpl, + headerkey_tmpl, + desc1_tmpl, + moduletopics_tmpl, + write_html_footer, +) path = sys.argv[1] year = os.getenv("VERSION_DATE") @@ -24,16 +32,16 @@ try: index_keys = lines.index("

KEYWORDS

\n") + 1 index_desc = lines.index("

NAME

\n") + 1 - except: + except Exception: continue try: key = lines[index_keys].split(",")[1].strip().replace(" ", "_") key = key.split(">")[1].split("<")[0] - except: + except Exception: continue try: desc = lines[index_desc].split("-", 1)[1].strip() - except: + except Exception: desc.strip() if key not in keywords.keys(): keywords[key] = {} From 9aeabc14fe862f5b7dd10ea4ca831c123eeb737c Mon Sep 17 00:00:00 2001 From: Anna Petrasova Date: Thu, 12 Sep 2024 16:04:01 -0400 Subject: [PATCH 215/471] contributing: update style guide for C (#4312) --- doc/development/style_guide.md | 137 ++++++++++++++++++++++++++++++++- 1 file changed, 136 insertions(+), 1 deletion(-) diff --git a/doc/development/style_guide.md b/doc/development/style_guide.md index 8268114ad5d..8f1d5ab5245 100644 --- a/doc/development/style_guide.md +++ b/doc/development/style_guide.md @@ -75,7 +75,7 @@ flake8 --ignore=E203,E266,E501 --max-line-length=88 python_file.py C and C++ code is formatted with [ClangFormat](https://clang.llvm.org/docs/ClangFormat.html). Contributions are -expected to be formatted with `clang-format` (currently with version 15+). The +expected to be formatted with `clang-format` (currently with version 18+). The most convenient method to install clang-format and format files is [using pre-commit](#using-pre-commit). @@ -92,6 +92,38 @@ If using pre-commit is not an option, for whatever reason, there is a helper script [grass_clang_format.sh](./utils/grass_clang_format.sh), which simplifies bulk reformatting. +#### Order of include headers + +In general, headers should be included in the order: + +1. Core system headers (stdio.h, ctype.h, ...) +2. Headers for non-core system components (X11, libraries). +3. GRASS headers (grass/gis.h, grass/glocale.h, ...) +4. Headers for the specific library/program (geodesic.h, ...) + +Each class of headers has an obligation to be compatible with those above it in +the list, but not those below it. The header groups should be alphabetically +sorted and separated by a newline. + +```c +#include +#include +#include + +#include +#include +#include + +#include "local_proto.h" +#include "mask.h" +``` + +#### Naming conventions + +Use function names which fulfill the official [GNU naming +convention](https://www.gnu.org/prep/standards/html_node/Names.html). Instead of +naming a function like: MyNewFunction() use snake case: my_new_function()`. + ### Using pre-commit It is highly recommended to install and use [pre-commit](https://pre-commit.com) @@ -652,6 +684,30 @@ standard tool output if it has one. ### Developing GRASS Addons +To streamline the development of a GRASS addon in python, you can use [this +template](https://github.com/OSGeo/grass-addon-cookiecutter) powered by +Cookiecutter. + +#### Copyright header + +Use the following header in your source code. + +```python +############################################################################## +# MODULE: r.foo +# +# AUTHOR(S): John Doe +# +# PURPOSE: Provide short description of module here... +# +# COPYRIGHT: (C) 2024 by John Doe and the GRASS Development Team +# +# This program is free software under the GNU General Public +# License (>=v2). Read the file COPYING that comes with GRASS +# for details. +############################################################################## +``` + #### Use Standard Options in Interface GRASS tools must use the GRASS parser to handle its command line parameters. To @@ -887,3 +943,82 @@ self.bwizard = wx.Button(..., # GTC %s will be replaced with name of current shell gs.message(_("Running through {}").format(shellname)) ``` + +### Developing C tools + +Refer to the [online GRASS Programmer's +Manual](​https://grass.osgeo.org/programming8/) or generate it with `make +htmldocs` or `make pdfdocs`. + +#### Use GRASS library functions + +Use the GRASS library functions, when available, instead of the standard C +functions. The reason for this is that the following functions ensure good +programming practice (e.g. always checking if memory was allocated) and/or +improves portability. + +- Memory management: `G_malloc()`, `G_calloc()`, `G_realloc()`, `G_free()` +- Environmental variables: `G_getenv()`, `G_setenv()`, `G_unsetenv()` +- File seek: `G_fseek()`, `G_ftell()` +- Printing: `G_asprintf()`, `G_vsaprintf()`, `G_vfaprintf()`, ... + +Please refer to [the programmers manual](https://grass.osgeo.org/programming8/) +for the proper use (e.g., determining if any casts are needed for arguments or +return values) of these library functions. They may perform a task slightly +different from their corresponding C library function, and thus, their use may +not be the same. + +#### Returning value of main function + +Tool exit status is defined as `EXIT_SUCCESS` or `EXIT_FAILURE` (declared in +`stdlib.h`), e.g. + +```c + { + ... + if (G_parser (argc, argv)) + exit (EXIT_FAILURE); + + ... + exit (EXIT_SUCCESS); + } +``` + +#### Messages and data output + +See rules for [messages in Python scripts](#messages) for proper usage of +`G_fatal_error()`, `G_warning()`, etc. Message output is not expected to be sent +to pipe or file. + +For data output redirected to pipe or file, please use `fprintf()` and specify +the stdout stream as follows: + +```c + fprintf(stdout, ...); + fflush(stdout); + + fflush(stdout) /* always required when using fprintf(stdout, ...). */ +``` + +#### Header section + +Add a header section to file main.c of your tool and make sure you include the +copyright. If you are modifying an existing file you may under no circumstances +remove prior copyright or licensing text that is not your own, even for a major +rewrite. If any original code or code that is in part derived from another's +original work remains, it must be properly cited. + +```c +/**************************************************************************** + * + * MODULE: g.foo + * AUTHOR(S): John Doe + * PURPOSE: Provide short description of module here... + * COPYRIGHT: (C) 2010 by John Doe, and the GRASS Development Team + * + * This program is free software under the GNU General Public + * License (>=v2). Read the COPYING file that comes with GRASS + * for details. + * + *****************************************************************************/ +``` From d77f4575f36c686005f0a786b0d13bf0542be7bb Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Thu, 12 Sep 2024 18:12:59 -0400 Subject: [PATCH 216/471] ps.map: Fix copy into fixed size buffer issue in do_scalebar.c (#4307) --- ps/ps.map/do_scalebar.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/ps/ps.map/do_scalebar.c b/ps/ps.map/do_scalebar.c index a53325172f3..760a6825288 100644 --- a/ps/ps.map/do_scalebar.c +++ b/ps/ps.map/do_scalebar.c @@ -2,6 +2,7 @@ #include #include +#include #include #include "local_proto.h" #include "distance.h" @@ -182,17 +183,17 @@ int do_scalebar(void) /* draw units label */ if (sb.units == SB_UNITS_AUTO) - strcpy(num, G_database_unit_name(TRUE)); + (void)G_strlcpy(num, G_database_unit_name(TRUE), sizeof(num)); else if (sb.units == SB_UNITS_METERS) - strcpy(num, _("meters")); + (void)G_strlcpy(num, _("meters"), sizeof(num)); else if (sb.units == SB_UNITS_KM) - strcpy(num, _("kilometers")); + (void)G_strlcpy(num, _("kilometers"), sizeof(num)); else if (sb.units == SB_UNITS_FEET) - strcpy(num, _("feet")); + (void)G_strlcpy(num, _("feet"), sizeof(num)); else if (sb.units == SB_UNITS_MILES) - strcpy(num, _("miles")); + (void)G_strlcpy(num, _("miles"), sizeof(num)); else if (sb.units == SB_UNITS_NMILES) - strcpy(num, _("nautical miles")); + (void)G_strlcpy(num, _("nautical miles"), sizeof(num)); text_box_path(72.0 * (x + length / 2), 72.0 * (PS.page_height - (sb.y + 0.075)), CENTER, UPPER, num, From 2c1df9f89b308b6f0bb3df45763d92ff00c986a6 Mon Sep 17 00:00:00 2001 From: Martin Landa Date: Fri, 13 Sep 2024 13:13:33 +0200 Subject: [PATCH 217/471] GUI: gmodeler fix adding tool (#4309) * revert 2cf98da * fix regression introduced in f99780c --- gui/wxpython/gmodeler/model.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/gui/wxpython/gmodeler/model.py b/gui/wxpython/gmodeler/model.py index b243f0beb5c..41fcfe6614f 100644 --- a/gui/wxpython/gmodeler/model.py +++ b/gui/wxpython/gmodeler/model.py @@ -941,8 +941,9 @@ def GetRelations(self, fdir=None): result = [] for rel in self.rels: - if fdir == "from" and rel.GetFrom() == self: - result.append(rel) + if fdir == "from": + if rel.GetFrom() == self: + result.append(rel) elif rel.GetTo() == self: result.append(rel) @@ -1577,7 +1578,7 @@ def _defineShape(self, width, height, x, y): :param width, height: dimension of the shape :param x, y: position of the shape """ - ogl.EllipseShape(self, width, height) + ogl.EllipseShape.__init__(self, width, height) # noqa: PLC2801, C2801 if self.parent.GetCanvas(): self.SetCanvas(self.parent.GetCanvas()) @@ -1592,7 +1593,7 @@ def _defineShape(self, width, height, x, y): :param width, height: dimension of the shape :param x, y: position of the shape """ - ogl.CompositeShape(self) + ogl.CompositeShape.__init__(self) # noqa: PLC2801, C2801 if self.parent.GetCanvas(): self.SetCanvas(self.parent.GetCanvas()) From cd5f1bd277c1beb7e5fdfa7f3b4db0caf26f6c51 Mon Sep 17 00:00:00 2001 From: Nicklas Larsson Date: Fri, 13 Sep 2024 14:00:45 +0200 Subject: [PATCH 218/471] lib/gis: Add portable G_strlcat function (#4304) Add wrapper function for strlcat(), using system function if available, otherwise uses implementation by Todd C. Miller originated from https://github.com/openbsd/src/blob/e291b8af02e5c2b53d7ddb1f0c9c0fd608b97d45/lib/libc/string/strlcat.c. G_strlcat() is a safer alternative to strcat(). --- configure | 17 +++++++- configure.ac | 3 ++ include/grass/config.h.in | 9 +++-- include/grass/defs/gis.h | 3 ++ lib/gis/strlcat.c | 84 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 112 insertions(+), 4 deletions(-) create mode 100644 lib/gis/strlcat.c diff --git a/configure b/configure index 7d8b9b1e5e7..bc60ae7c41c 100755 --- a/configure +++ b/configure @@ -765,8 +765,9 @@ SOCKLIB ICONVLIB DLLIB MATHLIB -HAVE_ASPRINTF HAVE_STRLCPY +HAVE_STRLCAT +HAVE_ASPRINTF DBMIEXTRALIB USE_X11 XTLIB @@ -8312,6 +8313,19 @@ then : fi + + +ac_fn_c_check_func "$LINENO" "strlcat" "ac_cv_func_strlcat" +if test "x$ac_cv_func_strlcat" = xyes +then : + printf "%s\n" "#define HAVE_STRLCAT 1" >>confdefs.h + +fi + + + +# Test if strlcpy exists +# This is a function part of *BSD libc (optionally available on Linux via libbsd) ac_fn_c_check_func "$LINENO" "strlcpy" "ac_cv_func_strlcpy" if test "x$ac_cv_func_strlcpy" = xyes then : @@ -8320,6 +8334,7 @@ then : fi + # Test if mathlib needs -lm flag or is included with libc ac_fn_c_check_func "$LINENO" "atan" "ac_cv_func_atan" if test "x$ac_cv_func_atan" = xyes diff --git a/configure.ac b/configure.ac index 8ef6e1b9f7c..b38a2a5936c 100644 --- a/configure.ac +++ b/configure.ac @@ -595,6 +595,9 @@ AC_SUBST(DBMIEXTRALIB) AC_CHECK_FUNCS(asprintf) AC_SUBST(HAVE_ASPRINTF) +AC_CHECK_FUNCS(strlcat) +AC_SUBST(HAVE_STRLCAT) + # Test if strlcpy exists # This is a function part of *BSD libc (optionally available on Linux via libbsd) AC_CHECK_FUNCS(strlcpy) diff --git a/include/grass/config.h.in b/include/grass/config.h.in index f070b4ccc0d..95dc2bc034b 100644 --- a/include/grass/config.h.in +++ b/include/grass/config.h.in @@ -14,9 +14,6 @@ /* Define to 1 if you have the `asprintf' function. */ #undef HAVE_ASPRINTF -/* Define to 1 if you have the `strlcpy' function. */ -#undef HAVE_STRLCPY - /* Define to 1 if you have the header file. */ #undef HAVE_BZLIB_H @@ -239,6 +236,12 @@ /* Define to 1 if you have the header file. */ #undef HAVE_STRING_H +/* Define to 1 if you have the `strlcat' function. */ +#undef HAVE_STRLCAT + +/* Define to 1 if you have the `strlcpy' function. */ +#undef HAVE_STRLCPY + /* Define to 1 if you have the header file. */ #undef HAVE_SVM_H diff --git a/include/grass/defs/gis.h b/include/grass/defs/gis.h index 4125a3aeed3..dc5ad2499f0 100644 --- a/include/grass/defs/gis.h +++ b/include/grass/defs/gis.h @@ -152,6 +152,9 @@ int G_vfaprintf(FILE *, const char *, va_list); int G_vsaprintf(char *, const char *, va_list); int G_vsnaprintf(char *, size_t, const char *, va_list); +/* strlcat.c */ +size_t G_strlcat(char *, const char *, size_t); + /* strlcpy.c */ size_t G_strlcpy(char *, const char *, size_t); diff --git a/lib/gis/strlcat.c b/lib/gis/strlcat.c new file mode 100644 index 00000000000..ed6c3887e72 --- /dev/null +++ b/lib/gis/strlcat.c @@ -0,0 +1,84 @@ +/*! + * \file lib/gis/strlcat.c + * + * \brief GIS Library - GRASS implementation of strlcat(). + * + * If available, G_strlcat() calls system strlcat(), otherwise it uses + * implementation by Todd C. Miller of OpenBSD. + * + * Addition to GRASS GIS by Nicklas Larsson, 2024 + * + * Original OpenBSD implementation notes: + * + * Copyright (c) 1998, 2015 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +/** + * \brief Size-bounded string concatenation + * + * Appends string src to the end of dst. It will append at most + * dstsize - strlen(dst) - 1 characters. It will then NUL-terminate, unless + * dstsize is 0 or the original dst string was longer than dstsize (in practice + * this should not happen as it means that either dstsize is incorrect or that + * dst is not a proper string). + * + * If the src and dst strings overlap, the behavior is undefined. + * This function is a safer alternative to strncat. + * + * \param[out] dst Pointer to the destination buffer. Must be a NUL-terminated + * C string. + * \param[in] src Pointer to the source string, which will be appended. Must + * be a NUL-terminated C string. + * \param[in] dsize The size of the destination buffer. + * + * \return The total length of the string src, which was attempted to be + * created (the initial length of dst plus the length of src, not + * including the terminating NUL character). If the return value + * is >= dsize, truncation occurred. + */ + +size_t G_strlcat(char *restrict dst, const char *restrict src, size_t dsize) +{ +#ifdef HAVE_STRLCAT + return strlcat(dst, src, dsize); +#else + const char *odst = dst; + const char *osrc = src; + size_t n = dsize; + size_t dlen; + + /* Find the end of dst and adjust bytes left but don't go past end. */ + while (n-- != 0 && *dst != '\0') + dst++; + dlen = dst - odst; + n = dsize - dlen; + + if (n-- == 0) + return (dlen + strlen(src)); + while (*src != '\0') { + if (n != 0) { + *dst++ = *src; + n--; + } + src++; + } + *dst = '\0'; + + return (dlen + (src - osrc)); /* count does not include NUL */ +#endif +} From 7dbb49bc701c06a2c39b1d5a7b48e85b71b914c2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 13 Sep 2024 17:25:31 +0000 Subject: [PATCH 219/471] CI(deps): Update github/codeql-action action to v3.26.7 (#4314) --- .github/workflows/codeql-analysis.yml | 4 ++-- .github/workflows/python-code-quality.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 05d4581b689..b4562dd75c8 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -56,7 +56,7 @@ jobs: if: ${{ matrix.language == 'c-cpp' }} - name: Initialize CodeQL - uses: github/codeql-action/init@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6 + uses: github/codeql-action/init@8214744c546c1e5c8f03dde8fab3a7353211988d # v3.26.7 with: languages: ${{ matrix.language }} config-file: ./.github/codeql/codeql-config.yml @@ -81,6 +81,6 @@ jobs: run: .github/workflows/build_ubuntu-22.04.sh "${HOME}/install" - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6 + uses: github/codeql-action/analyze@8214744c546c1e5c8f03dde8fab3a7353211988d # v3.26.7 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index 17cce1c09aa..f5810fbe7c2 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -135,7 +135,7 @@ jobs: path: bandit.sarif - name: Upload SARIF File into Security Tab - uses: github/codeql-action/upload-sarif@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6 + uses: github/codeql-action/upload-sarif@8214744c546c1e5c8f03dde8fab3a7353211988d # v3.26.7 with: sarif_file: bandit.sarif From d2f347782adee4f9a8f7e3a3c1dd48fd65884145 Mon Sep 17 00:00:00 2001 From: Vaclav Petras Date: Fri, 13 Sep 2024 13:47:31 -0400 Subject: [PATCH 220/471] lib: Accept more newline styles in G_getl (#3853) Use G_getl2 in G_getl to have everywhere the same behavior of supporting all newlines. Originally, G_getl2 was created to keep the behavior of G_getl. However, now, we want G_getl2 behavior everywhere. Keeping G_getl2 for compatibility. Code can later be clean up to use only G_getl, probably at the time when G_getl2 is removed (v9). The new test fails without the change in the library for CRLF for G_getl, but passes with the change. CR fails because it is not supported by fgets used since 88090da (#3850), so expecting failure for CR. Return and argument types for ctypes fopen need to be set for the code to work everywhere reliably. --------- Co-authored-by: Nicklas Larsson --- lib/gis/getl.c | 16 ++--- lib/gis/testsuite/test_gis_lib_getl.py | 81 ++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 11 deletions(-) create mode 100644 lib/gis/testsuite/test_gis_lib_getl.py diff --git a/lib/gis/getl.c b/lib/gis/getl.c index 7daae816a70..34df9b04ad0 100644 --- a/lib/gis/getl.c +++ b/lib/gis/getl.c @@ -19,8 +19,7 @@ * \brief Gets a line of text from a file * * This routine runs fgets() to fetch a line of text from a file - * (advancing file pointer) and removes trailing newline. fgets() does - * not recognize '\\r' as an EOL and will read past * it. + * (advancing file pointer) and removes trailing newline. * * \param buf string buffer to receive read data * \param n maximum number of bytes to read @@ -28,23 +27,18 @@ * * \return 1 on success * \return 0 EOF + * + * \see G_getl2() */ int G_getl(char *buf, int n, FILE *fd) { - if (!fgets(buf, n, fd)) - return 0; - - for (; *buf && *buf != '\n'; buf++) - ; - *buf = 0; - - return 1; + return G_getl2(buf, n, fd); } /*! * \brief Gets a line of text from a file of any pedigree * - * This routine is like G_getl() but is more portable. It supports + * This routine supports * text files created on various platforms (UNIX, MacOS9, DOS), * i.e. \\n (\\012), \\r (\\015), and * \\r\\n (\\015\\012) style newlines. diff --git a/lib/gis/testsuite/test_gis_lib_getl.py b/lib/gis/testsuite/test_gis_lib_getl.py new file mode 100644 index 00000000000..98092955ab2 --- /dev/null +++ b/lib/gis/testsuite/test_gis_lib_getl.py @@ -0,0 +1,81 @@ +"""Test of gis library line reading functions + +@author Vaclav Petras +""" + +import ctypes +import pathlib +import platform +import unittest + +import grass.lib.gis as libgis +from grass.gunittest.case import TestCase +from grass.gunittest.main import test + + +class TestNewlinesWithGetlFunctions(TestCase): + """Test C functions G_getl() and G_getl2() from gis library""" + + @classmethod + def setUpClass(cls): + cls.libc = ctypes.cdll.LoadLibrary(ctypes.util.find_library("c")) + cls.libc.fopen.restype = ctypes.POINTER(libgis.FILE) + cls.libc.fopen.argtypes = [ctypes.c_char_p, ctypes.c_char_p] + cls.file_path = pathlib.Path("test.txt") + + def tearDown(self): + self.file_path.unlink() + + def read_lines_and_assert(self, get_line_function, newline): + """Write and read lines and then assert they are as expected""" + lines = ["Line 1", "Line 2", "Line 3"] + with open(self.file_path, mode="w", newline=newline) as stream: + for line in lines: + # Python text newline here. + # The specific newline is added by the stream. + stream.write(f"{line}\n") + + file_ptr = self.libc.fopen(str(self.file_path).encode("utf-8"), b"r") + if not file_ptr: + raise FileNotFoundError(f"Could not open file: {self.file_path}") + + try: + buffer_size = 50 + buffer = ctypes.create_string_buffer(buffer_size) + + for line in lines: + get_line_function(buffer, ctypes.sizeof(buffer), file_ptr) + result = buffer.value.decode("utf-8") if buffer else None + self.assertEqual(line, result) + finally: + self.libc.fclose(file_ptr) + + def test_getl_lf(self): + r"""Check G_getl() with LF (\n)""" + self.read_lines_and_assert(libgis.G_getl, "\n") + + @unittest.expectedFailure + def test_getl_cr(self): + r"""Check G_getl() with CR (\r)""" + self.read_lines_and_assert(libgis.G_getl, "\r") + + def test_getl_crlf(self): + r"""Check G_getl() with CRLF (\r\n)""" + self.read_lines_and_assert(libgis.G_getl, "\r\n") + + def test_getl2_lf(self): + r"""Check G_getl2() with LF (\n)""" + self.read_lines_and_assert(libgis.G_getl2, "\n") + + @unittest.expectedFailure + def test_getl2_cr(self): + r"""Check G_getl2() with CR (\r)""" + self.read_lines_and_assert(libgis.G_getl2, "\r") + + def test_getl2_crlf(self): + r"""Check G_getl2() with CRLF (\r\n)""" + self.read_lines_and_assert(libgis.G_getl2, "\r\n") + + +if __name__ == "__main__": + test() From 95da3efa40fec6dc6058479cadca524f79b34dcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Sat, 14 Sep 2024 10:34:03 -0400 Subject: [PATCH 221/471] style: Fix all flake8-gettext (INT) errors (INT001, INT002, INT003) (#4052) * style: Fix all flake8-gettext (INT) errors (INT001, INT002, INT003) Ruff rules: https://docs.astral.sh/ruff/rules/f-string-in-get-text-func-call/ https://docs.astral.sh/ruff/rules/format-in-get-text-func-call/ https://docs.astral.sh/ruff/rules/printf-in-get-text-func-call/ * Revert source string changes when unneeded. * Update univar_statistics.py Co-authored-by: Vaclav Petras * Update panels.py for Python script type string * Update pyedit.py to keep path keyword in changed string * Update univar_statistics.py to make already translated strings match again * Update pyedit.py * Update model.py --------- Co-authored-by: Vaclav Petras --- general/g.parser/test.py | 6 +- gui/wxpython/animation/dialogs.py | 6 +- gui/wxpython/animation/temporal_manager.py | 2 +- gui/wxpython/core/debug.py | 4 +- gui/wxpython/core/render.py | 2 +- gui/wxpython/gcp/manager.py | 9 +- gui/wxpython/gmodeler/model.py | 11 +-- gui/wxpython/gmodeler/panels.py | 28 +++--- gui/wxpython/gui_core/ghelp.py | 12 +-- gui/wxpython/gui_core/gselect.py | 68 ++++++------- gui/wxpython/gui_core/pyedit.py | 29 +++--- gui/wxpython/gui_core/pystc.py | 6 +- gui/wxpython/gui_core/query.py | 4 +- gui/wxpython/history/browser.py | 4 +- gui/wxpython/iclass/dialogs.py | 8 +- gui/wxpython/iclass/frame.py | 11 ++- gui/wxpython/iclass/plots.py | 9 +- gui/wxpython/image2target/ii2t_gis_set.py | 2 +- gui/wxpython/iscatt/controllers.py | 45 ++++----- gui/wxpython/iscatt/dialogs.py | 8 +- gui/wxpython/iscatt/frame.py | 2 +- gui/wxpython/iscatt/iscatt_core.py | 4 +- gui/wxpython/lmgr/frame.py | 4 +- gui/wxpython/lmgr/workspace.py | 5 +- gui/wxpython/location_wizard/dialogs.py | 4 +- gui/wxpython/location_wizard/wizard.py | 2 +- gui/wxpython/main_window/frame.py | 4 +- gui/wxpython/mapdisp/frame.py | 5 +- gui/wxpython/mapdisp/test_mapdisp.py | 2 +- gui/wxpython/mapwin/analysis.py | 6 +- gui/wxpython/rdigit/g.gui.rdigit.py | 13 +-- gui/wxpython/rlisetup/frame.py | 5 +- gui/wxpython/rlisetup/functions.py | 3 +- gui/wxpython/rlisetup/wizard.py | 4 +- gui/wxpython/startup/locdownload.py | 12 +-- gui/wxpython/timeline/frame.py | 2 +- gui/wxpython/tplot/frame.py | 19 ++-- gui/wxpython/vnet/vnet_data.py | 2 +- gui/wxpython/web_services/dialogs.py | 13 ++- gui/wxpython/web_services/widgets.py | 11 +-- gui/wxpython/wxgui.py | 8 +- lib/init/grass.py | 7 +- pyproject.toml | 96 ++---------------- python/grass/pygrass/raster/category.py | 2 +- python/grass/pygrass/vector/__init__.py | 4 +- python/grass/script/raster.py | 2 +- .../temporal/abstract_space_time_dataset.py | 2 +- python/grass/temporal/aggregation.py | 13 ++- .../grass/temporal/c_libraries_interface.py | 12 +-- python/grass/temporal/core.py | 25 ++--- python/grass/temporal/datetime_math.py | 2 +- python/grass/temporal/mapcalc.py | 99 +++++-------------- python/grass/temporal/space_time_datasets.py | 72 +++++--------- python/grass/temporal/stds_export.py | 17 ++-- python/grass/temporal/stds_import.py | 6 +- python/grass/temporal/temporal_algebra.py | 4 +- .../temporal/temporal_raster_base_algebra.py | 2 +- python/grass/temporal/univar_statistics.py | 9 +- scripts/d.polar/d.polar.py | 8 +- scripts/g.extension.all/g.extension.all.py | 34 +++---- scripts/g.extension/g.extension.py | 12 +-- scripts/i.oif/i.oif.py | 2 +- scripts/i.pansharpen/i.pansharpen.py | 6 +- scripts/i.spectral/i.spectral.py | 14 ++- scripts/r.in.wms/wms_base.py | 12 +-- scripts/r.in.wms/wms_cap_parsers.py | 2 +- scripts/r.in.wms/wms_drv.py | 2 +- scripts/r.in.wms/wms_gdal_drv.py | 2 +- scripts/r.pack/r.pack.py | 2 +- scripts/r.tileset/r.tileset.py | 6 +- scripts/r.unpack/r.unpack.py | 19 ++-- scripts/v.rast.stats/v.rast.stats.py | 12 +-- scripts/v.to.lines/v.to.lines.py | 2 +- scripts/v.unpack/v.unpack.py | 5 +- scripts/v.what.strds/v.what.strds.py | 16 +-- temporal/t.rast.accdetect/t.rast.accdetect.py | 2 +- .../t.rast.accumulate/t.rast.accumulate.py | 2 +- temporal/t.rast.export/t.rast.export.py | 4 +- temporal/t.rast.gapfill/t.rast.gapfill.py | 3 +- temporal/t.rast.list/t.rast.list.py | 22 ++++- temporal/t.rast.out.vtk/t.rast.out.vtk.py | 2 +- temporal/t.rast.series/t.rast.series.py | 4 +- temporal/t.rast.to.rast3/t.rast.to.rast3.py | 2 +- temporal/t.rast.what/t.rast.what.py | 22 ++--- temporal/t.remove/t.remove.py | 19 ++-- temporal/t.unregister/t.unregister.py | 8 +- .../t.vect.observe.strds.py | 12 +-- utils/mkhtml.py | 27 +++-- 88 files changed, 441 insertions(+), 622 deletions(-) diff --git a/general/g.parser/test.py b/general/g.parser/test.py index 3b56230eb14..8166b354a37 100755 --- a/general/g.parser/test.py +++ b/general/g.parser/test.py @@ -53,10 +53,10 @@ def main(): # test if parameter present: if option1: - gs.message(_("Value of option1 option: '%s'" % option1)) + gs.message(_("Value of option1 option: '%s'") % option1) - gs.message(_("Value of raster option: '%s'" % raster)) - gs.message(_("Value of vector option: '%s'" % vector)) + gs.message(_("Value of raster option: '%s'") % raster) + gs.message(_("Value of vector option: '%s'") % vector) # End of your main code here diff --git a/gui/wxpython/animation/dialogs.py b/gui/wxpython/animation/dialogs.py index 154bdc9fce6..3d70dbd2639 100644 --- a/gui/wxpython/animation/dialogs.py +++ b/gui/wxpython/animation/dialogs.py @@ -1565,9 +1565,9 @@ def _export_file_validation(self, filebrowsebtn, file_path, file_postfix): self.GetParent(), message=_( "Exported animation file <{file}> exists. " - "Do you want to overwrite it?".format( - file=file_path, - ), + "Do you want to overwrite it?" + ).format( + file=file_path, ), caption=_("Overwrite?"), style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION, diff --git a/gui/wxpython/animation/temporal_manager.py b/gui/wxpython/animation/temporal_manager.py index 9f9eafd41d0..150eeab510f 100644 --- a/gui/wxpython/animation/temporal_manager.py +++ b/gui/wxpython/animation/temporal_manager.py @@ -342,7 +342,7 @@ def _gatherInformation(self, timeseries, etype, timeseriesList, infoDict): maps = sp.get_registered_maps_as_objects() if not sp.check_temporal_topology(maps): - raise GException(_("Topology of Space time dataset %s is invalid." % id)) + raise GException(_("Topology of Space time dataset %s is invalid.") % id) timeseriesList.append(id) infoDict[id] = {} diff --git a/gui/wxpython/core/debug.py b/gui/wxpython/core/debug.py index cf642d1df4c..b6c12a60b62 100644 --- a/gui/wxpython/core/debug.py +++ b/gui/wxpython/core/debug.py @@ -49,8 +49,8 @@ def SetLevel(self): sys.stderr.write( _( "WARNING: Ignoring unsupported wx debug level (must be >=0 and " - "<=5). {0}\n".format(e) - ) + "<=5). {0}\n" + ).format(e) ) def msg(self, level, message, *args): diff --git a/gui/wxpython/core/render.py b/gui/wxpython/core/render.py index 021f3aa6500..3095cb47fc8 100644 --- a/gui/wxpython/core/render.py +++ b/gui/wxpython/core/render.py @@ -706,7 +706,7 @@ def OnRenderDone(self, env): self._rendering = False if wx.IsBusy(): wx.EndBusyCursor() - raise GException(_("Rendering failed: %s" % msg)) + raise GException(_("Rendering failed: %s") % msg) stop = time.time() Debug.msg( diff --git a/gui/wxpython/gcp/manager.py b/gui/wxpython/gcp/manager.py index 749cbf79efb..696bc53609f 100644 --- a/gui/wxpython/gcp/manager.py +++ b/gui/wxpython/gcp/manager.py @@ -1785,11 +1785,10 @@ def _getOverWriteDialog(self, maptype, overwrite): return wx.MessageDialog( self.GetParent(), message=_( - "The {map_type} map {map_name} exists. " - "Do you want to overwrite?".format( - map_type=maptype, - map_name=map_name, - ), + "The {map_type} map {map_name} exists. Do you want to overwrite?" + ).format( + map_type=maptype, + map_name=map_name, ), caption=_("Overwrite?"), style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION, diff --git a/gui/wxpython/gmodeler/model.py b/gui/wxpython/gmodeler/model.py index 41fcfe6614f..a609a1c8bd8 100644 --- a/gui/wxpython/gmodeler/model.py +++ b/gui/wxpython/gmodeler/model.py @@ -2819,12 +2819,11 @@ def _getPythonActionCmd(self, item, task, cmdIndent, variables={}): dlg = wx.MessageDialog( self.model.canvas, message=_( - f"Module {task.get_name()} in your model contains " - f"parameterized flags. actinia does not support " - f"parameterized flags. The following flags are therefore " - f"not being written in the generated json: " - f"{itemParameterizedFlags}" - ), + "Module {task_name} in your model contains " + "parameterized flags. Actinia does not support " + "parameterized flags. The following flags are therefore " + "not being written in the generated JSON: {flags}" + ).format(task_name=task.get_name(), flags=itemParameterizedFlags), caption=_("Warning"), style=wx.OK_DEFAULT | wx.ICON_WARNING, ) diff --git a/gui/wxpython/gmodeler/panels.py b/gui/wxpython/gmodeler/panels.py index 300220b9f7c..93ef770efe1 100644 --- a/gui/wxpython/gmodeler/panels.py +++ b/gui/wxpython/gmodeler/panels.py @@ -268,19 +268,15 @@ def OnPageChanged(self, event): if self.pythonPanel.IsModified(): self.SetStatusText( - _( - "{} script contains local modifications".format( - self.pythonPanel.body.script_type - ) + _("{} script contains local modifications").format( + self.pythonPanel.body.script_type ), 0, ) else: self.SetStatusText( - _( - "{} script is up-to-date".format( - self.pythonPanel.body.script_type - ) + _("{} script is up-to-date").format( + self.pythonPanel.body.script_type ), 0, ) @@ -1261,7 +1257,7 @@ def OnDeleteData(self, event): dlg = wx.MessageDialog( parent=self, - message=_("Do you want to permanently delete data?%s" % msg), + message=_("Do you want to permanently delete data?%s") % msg, caption=_("Delete intermediate data?"), style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION, ) @@ -1645,9 +1641,7 @@ def _layout(self): bodySizer.Add(self.body, proportion=1, flag=wx.EXPAND | wx.ALL, border=3) btnSizer.Add( - StaticText( - parent=self, id=wx.ID_ANY, label="%s:" % _("Python script type") - ), + StaticText(parent=self, id=wx.ID_ANY, label=_("Python script type:")), flag=wx.ALIGN_CENTER_VERTICAL, ) btnSizer.Add(self.script_type_box, proportion=0, flag=wx.RIGHT, border=5) @@ -1676,7 +1670,7 @@ def GetScriptExt(self): return ext def SetWriteObject(self, script_type): - """Set correct self.write_object dependng on the script type. + """Set correct self.write_object depending on the script type. :param script_type: script type name as a string """ if script_type == "PyWPS": @@ -1704,8 +1698,8 @@ def RefreshScript(self): message=_( "{} script is locally modified. " "Refresh will discard all changes. " - "Do you really want to continue?".format(self.body.script_type) - ), + "Do you really want to continue?" + ).format(self.body.script_type), caption=_("Update"), style=wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION | wx.CENTRE, ) @@ -1835,7 +1829,7 @@ def OnChangeScriptType(self, event): if self.RefreshScript(): self.body.script_type = new_script_type self.parent.SetStatusText( - _("{} script is up-to-date".format(self.body.script_type)), + _("{} script is up-to-date").format(self.body.script_type), 0, ) @@ -1854,7 +1848,7 @@ def OnRefresh(self, event): """Refresh the script.""" if self.RefreshScript(): self.parent.SetStatusText( - _("{} script is up-to-date".format(self.body.script_type)), + _("{} script is up-to-date").format(self.body.script_type), 0, ) event.Skip() diff --git a/gui/wxpython/gui_core/ghelp.py b/gui/wxpython/gui_core/ghelp.py index 0602aeaea0c..93879998133 100644 --- a/gui/wxpython/gui_core/ghelp.py +++ b/gui/wxpython/gui_core/ghelp.py @@ -564,15 +564,15 @@ def _langString(self, k, v): """Return string for the status of translation""" allStr = "%s :" % k.upper() try: - allStr += _(" %d translated" % v["good"]) + allStr += _(" %d translated") % v["good"] except: pass try: - allStr += _(" %d fuzzy" % v["fuzzy"]) + allStr += _(" %d fuzzy") % v["fuzzy"] except: pass try: - allStr += _(" %d untranslated" % v["bad"]) + allStr += _(" %d untranslated") % v["bad"] except: pass return allStr @@ -584,7 +584,7 @@ def _langBox(self, par, k, v): langBox.Add(tkey) try: tgood = StaticText( - parent=par, id=wx.ID_ANY, label=_("%d translated" % v["good"]) + parent=par, id=wx.ID_ANY, label=_("%d translated") % v["good"] ) tgood.SetForegroundColour(wx.Colour(35, 142, 35)) langBox.Add(tgood) @@ -593,7 +593,7 @@ def _langBox(self, par, k, v): langBox.Add(tgood) try: tfuzzy = StaticText( - parent=par, id=wx.ID_ANY, label=_(" %d fuzzy" % v["fuzzy"]) + parent=par, id=wx.ID_ANY, label=_(" %d fuzzy") % v["fuzzy"] ) tfuzzy.SetForegroundColour(wx.Colour(255, 142, 0)) langBox.Add(tfuzzy) @@ -602,7 +602,7 @@ def _langBox(self, par, k, v): langBox.Add(tfuzzy) try: tbad = StaticText( - parent=par, id=wx.ID_ANY, label=_(" %d untranslated" % v["bad"]) + parent=par, id=wx.ID_ANY, label=_(" %d untranslated") % v["bad"] ) tbad.SetForegroundColour(wx.Colour(255, 0, 0)) langBox.Add(tbad) diff --git a/gui/wxpython/gui_core/gselect.py b/gui/wxpython/gui_core/gselect.py index 7dc51b75935..eae12085f92 100644 --- a/gui/wxpython/gui_core/gselect.py +++ b/gui/wxpython/gui_core/gselect.py @@ -921,7 +921,7 @@ def GetTable(self, layer): :param layer: vector layer number """ if layer not in self.layers: - raise GException(_("No table linked to layer <{}>.".format(layer))) + raise GException(_("No table linked to layer <{}>.").format(layer)) return self.layers[layer]["table"] def GetDbSettings(self, layer): @@ -2259,18 +2259,18 @@ def hasRastSameProjAsLocation(dsn, table=None): message=_( "Getting raster <{table}> SRID from PostgreSQL" " DB <{db}>, host <{host}> failed." - " {error}.".format( - table=table, - db=self._getPDDBConnectionParam( - dsn, - conn_param="dbname", - ), - host=self._getPDDBConnectionParam( - dsn, - conn_param="host", - ), - error=gs.utils.decode(error), + " {error}." + ).format( + table=table, + db=self._getPDDBConnectionParam( + dsn, + conn_param="dbname", ), + host=self._getPDDBConnectionParam( + dsn, + conn_param="host", + ), + error=gs.utils.decode(error), ), ) if ret: @@ -2522,17 +2522,17 @@ def _getPGDBtables(self, dsn): parent=self, message=_( "Getting list of tables from PostgreSQL DB <{db}>," - " host <{host}> failed. {error}.".format( - db=self._getPGDBConnectionParam( - dsn, - conn_param="dbname", - ), - host=self._getPGDBConnectionParam( - dsn, - conn_param="host", - ), - error=gs.utils.decode(error), + " host <{host}> failed. {error}." + ).format( + db=self._getPGDBConnectionParam( + dsn, + conn_param="dbname", ), + host=self._getPGDBConnectionParam( + dsn, + conn_param="host", + ), + error=gs.utils.decode(error), ), ) if ret: @@ -2614,17 +2614,17 @@ def _getPGDBRasters(self, dsn): message=_( "Getting list of tables columns data types" " from PostGIS DB <{db}>, host <{host}> failed." - " {error}.".format( - db=self._getPGDBConnectionParam( - dsn, - conn_param="dbname", - ), - host=self._getPGDBConnectionParam( - dsn, - conn_param="host", - ), - error=gs.utils.decode(error), + " {error}." + ).format( + db=self._getPGDBConnectionParam( + dsn, + conn_param="dbname", ), + host=self._getPGDBConnectionParam( + dsn, + conn_param="host", + ), + error=gs.utils.decode(error), ), ) if ret: @@ -2644,8 +2644,8 @@ def _getPGDBRasters(self, dsn): parent=self, message=_( "PostgreSQL DB <{psql}> program was not found." - " Please, install it.".format(psql=self._psql) - ), + " Please, install it." + ).format(psql=self._psql), ) Debug.msg(3, f"GdalSelect._getPGDBRasters(): return {rasters}") return rasters diff --git a/gui/wxpython/gui_core/pyedit.py b/gui/wxpython/gui_core/pyedit.py index 9fa75fa74c1..4974e0ce8f5 100644 --- a/gui/wxpython/gui_core/pyedit.py +++ b/gui/wxpython/gui_core/pyedit.py @@ -306,14 +306,14 @@ def _openFile(self, file_path): GError( message=_( "Permission denied <{}>. Please change file " - "permission for reading.".format(file_path) - ), + "permission for reading." + ).format(file_path), parent=self.guiparent, showTraceback=False, ) except OSError: GError( - message=_("Couldn't read file <{}>.".format(file_path)), + message=_("Couldn't read file <{}>.").format(file_path), parent=self.guiparent, ) @@ -333,20 +333,18 @@ def _writeFile(self, file_path, content, additional_err_message=""): GError( message=_( "Permission denied <{}>. Please change file " - "permission for writing.{}".format( - file_path, - additional_err_message, - ), + "permission for writing.{}" + ).format( + file_path, + additional_err_message, ), parent=self.guiparent, showTraceback=False, ) except OSError: GError( - message=_( - "Couldn't write file <{}>.{}".format( - file_path, additional_err_message - ), + message=_("Couldn't write file <{}>.{}").format( + file_path, additional_err_message ), parent=self.guiparent, ) @@ -359,7 +357,7 @@ def OnRun(self, event): file_is_written = self._writeFile( file_path=self.filename, content=self.body.GetText(), - additional_err_message=" Unable to launch Python script.", + additional_err_message=_(" Unable to launch Python script."), ) if file_is_written: mode = stat.S_IMODE(os.lstat(self.filename)[stat.ST_MODE]) @@ -369,7 +367,7 @@ def OnRun(self, event): file_is_written = self._writeFile( file_path=self.filename, content=self.body.GetText(), - additional_err_message=" Unable to launch Python script.", + additional_err_message=_(" Unable to launch Python script."), ) if file_is_written: # set executable file @@ -509,9 +507,8 @@ def OpenRecentFile(self, path, file_exists, file_history): if not file_exists: GError( _( - "File <{}> doesn't exist." - "It was probably moved or deleted.".format(path) - ), + "File <{path}> doesn't exist. It was probably moved or deleted." + ).format(path=path), parent=self.guiparent, ) return diff --git a/gui/wxpython/gui_core/pystc.py b/gui/wxpython/gui_core/pystc.py index a1dfc543320..33d5f03b5f9 100644 --- a/gui/wxpython/gui_core/pystc.py +++ b/gui/wxpython/gui_core/pystc.py @@ -262,10 +262,8 @@ def OnKeyPressed(self, event): self.modified = True if self.statusbar: self.statusbar.SetStatusText( - _( - "{} script contains local modifications".format( - self.script_type - ) + _("{} script contains local modifications").format( + self.script_type ), 0, ) diff --git a/gui/wxpython/gui_core/query.py b/gui/wxpython/gui_core/query.py index 6250095cb29..5e0f734fa6a 100644 --- a/gui/wxpython/gui_core/query.py +++ b/gui/wxpython/gui_core/query.py @@ -136,11 +136,11 @@ def ShowContextMenu(self, node): ) else: label1 = nodes[0].label - texts.append((_("Copy '%s'" % self._cutLabel(label1)), label1)) + texts.append((_("Copy '%s'") % self._cutLabel(label1), label1)) col1 = self._colNames[1] if nodes[0].data and col1 in nodes[0].data and nodes[0].data[col1]: label2 = nodes[0].data[col1] - texts.insert(0, (_("Copy '%s'" % self._cutLabel(label2)), label2)) + texts.insert(0, (_("Copy '%s'") % self._cutLabel(label2), label2)) texts.append((_("Copy line"), label1 + ": " + label2)) ids = [] diff --git a/gui/wxpython/history/browser.py b/gui/wxpython/history/browser.py index 6a31523d51f..422d6dcc9c2 100644 --- a/gui/wxpython/history/browser.py +++ b/gui/wxpython/history/browser.py @@ -60,7 +60,7 @@ def get_translated_value(key, value): exec_datetime = datetime.fromisoformat(value) return exec_datetime.strftime("%Y-%m-%d %H:%M:%S") elif key == "runtime": - return _("{} sec".format(value)) + return _("{} sec").format(value) elif key == "status": return _(value.capitalize()) elif key in {"mask2d", "mask3d"}: @@ -480,7 +480,7 @@ def OnCmdExportHistory(self, event): try: history.copy(history_path, target_path) self.showNotification.emit( - message=_("Command history saved to '{}'".format(target_path)) + message=_("Command history saved to '{}'").format(target_path) ) except OSError as e: GError(str(e)) diff --git a/gui/wxpython/iclass/dialogs.py b/gui/wxpython/iclass/dialogs.py index 8aaac14c4c8..322bfcf5721 100644 --- a/gui/wxpython/iclass/dialogs.py +++ b/gui/wxpython/iclass/dialogs.py @@ -835,10 +835,10 @@ def OnOK(self, event): qdlg = wx.MessageDialog( parent=self, message=_( - "Vector map <%s> already exists." - " Do you want to overwrite it?" % vName - ), - caption=_("Vector <%s> exists" % vName), + "Vector map <%s> already exists. Do you want to overwrite it?" + ) + % vName, + caption=_("Vector <%s> exists") % vName, style=wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION | wx.CENTRE, ) if qdlg.ShowModal() == wx.ID_YES: diff --git a/gui/wxpython/iclass/frame.py b/gui/wxpython/iclass/frame.py index 52af10d85e0..69c18780249 100644 --- a/gui/wxpython/iclass/frame.py +++ b/gui/wxpython/iclass/frame.py @@ -646,11 +646,14 @@ def _checkImportedTopo(self, vector): warning = "" if topo["areas"] == 0: - warning = _("No areas in vector map <%s>.\n" % vector) + warning = _("No areas in vector map <%s>.\n") % vector if topo["points"] or topo["lines"]: - warning += _( - "Vector map <%s> contains points or lines, " - "these features are ignored." % vector + warning += ( + _( + "Vector map <%s> contains points or lines, " + "these features are ignored." + ) + % vector ) return warning diff --git a/gui/wxpython/iclass/plots.py b/gui/wxpython/iclass/plots.py index 786fa920d17..510953bb5b1 100644 --- a/gui/wxpython/iclass/plots.py +++ b/gui/wxpython/iclass/plots.py @@ -88,9 +88,12 @@ def _createScatterPlotPanel(self): ) self.iscatt_panel.Hide() except ImportError as e: - self.scatt_error = _( - "Scatter plot functionality is disabled.\n\nReason: " - "Unable to import packages needed for scatter plot.\n%s" % e + self.scatt_error = ( + _( + "Scatter plot functionality is disabled.\n\nReason: " + "Unable to import packages needed for scatter plot.\n%s" + ) + % e ) wx.CallAfter(GError, self.scatt_error, showTraceback=False, parent=self) self.iscatt_panel = None diff --git a/gui/wxpython/image2target/ii2t_gis_set.py b/gui/wxpython/image2target/ii2t_gis_set.py index e3cb0257af1..0f51d969e93 100644 --- a/gui/wxpython/image2target/ii2t_gis_set.py +++ b/gui/wxpython/image2target/ii2t_gis_set.py @@ -547,7 +547,7 @@ def _readGisRC(self): key, val = line.split(":", 1) except ValueError as e: sys.stderr.write( - _("Invalid line in GISRC file (%s):%s\n" % (e, line)) + _("Invalid line in GISRC file (%s):%s\n") % (e, line) ) grassrc[key.strip()] = DecodeString(val.strip()) finally: diff --git a/gui/wxpython/iscatt/controllers.py b/gui/wxpython/iscatt/controllers.py index 38e61e34133..e8884f4f78b 100644 --- a/gui/wxpython/iscatt/controllers.py +++ b/gui/wxpython/iscatt/controllers.py @@ -234,8 +234,9 @@ def AddScattPlot(self): "Number of cells (rows*cols) <%d> in current region" "is higher than maximum limit <%d>.\n\n" "You can reduce number of cells in current region using " - " command." % (ncells, MAX_NCELLS) - ), + " command." + ) + % (ncells, MAX_NCELLS), ) ) return @@ -250,8 +251,8 @@ def AddScattPlot(self): "It can be done by command.\n\n" "Do you want to continue using " "Interactive Scatter Plot Tool with this region?" - % (ncells, WARN_NCELLS) - ), + ) + % (ncells, WARN_NCELLS), style=wx.YES_NO | wx.NO_DEFAULT | wx.ICON_WARNING, ) ret = dlg.ShowModal() @@ -318,11 +319,11 @@ def CheckBands(self, b_1, b_2): err = "" for b in [b_1_name, b_2_name]: if self.bands_info[b] is None: - err += _("Band <%s> is not CELL (integer) type.\n" % b) + err += _("Band <%s> is not CELL (integer) type.\n") % b if err: GMessage( parent=self.guiparent, - message=_("Scatter plot cannot be added.\n" + err), + message=_("Scatter plot cannot be added.\n") + err, ) return False @@ -334,14 +335,14 @@ def CheckBands(self, b_1, b_2): "Scatter plot cannot be added.\n" "Multiple of bands ranges <%s:%d * %s:%d = %d> " "is higher than maximum limit <%d>.\n" - % ( - b_1_name, - b_1_i["range"], - b_1_name, - b_2_i["range"], - mrange, - MAX_SCATT_SIZE, - ) + ) + % ( + b_1_name, + b_1_i["range"], + b_1_name, + b_2_i["range"], + mrange, + MAX_SCATT_SIZE, ), ) return False @@ -354,14 +355,14 @@ def CheckBands(self, b_1, b_2): "It is strongly advised to reduce range extend of bands" "(e. g. using r.rescale) below recommended threshold.\n\n" "Do you really want to add this scatter plot?" - % ( - b_1_name, - b_1_i["range"], - b_1_name, - b_2_i["range"], - mrange, - WARN_SCATT_SIZE, - ) + ) + % ( + b_1_name, + b_1_i["range"], + b_1_name, + b_2_i["range"], + mrange, + WARN_SCATT_SIZE, ), style=wx.YES_NO | wx.NO_DEFAULT | wx.ICON_WARNING, ) diff --git a/gui/wxpython/iscatt/dialogs.py b/gui/wxpython/iscatt/dialogs.py index 4129015e88d..3e2d7fa7a59 100644 --- a/gui/wxpython/iscatt/dialogs.py +++ b/gui/wxpython/iscatt/dialogs.py @@ -327,10 +327,10 @@ def OnOK(self, event): qdlg = wx.MessageDialog( parent=self, message=_( - "Raster map <%s> already exists." - " Do you want to overwrite it?" % rast_name - ), - caption=_("Raster <%s> exists" % rast_name), + "Raster map <%s> already exists. Do you want to overwrite it?" + ) + % rast_name, + caption=_("Raster <%s> exists") % rast_name, style=wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION | wx.CENTRE, ) if qdlg.ShowModal() == wx.ID_YES: diff --git a/gui/wxpython/iscatt/frame.py b/gui/wxpython/iscatt/frame.py index 6d87a90e273..45f50622d2f 100644 --- a/gui/wxpython/iscatt/frame.py +++ b/gui/wxpython/iscatt/frame.py @@ -681,7 +681,7 @@ def OnPopupOpacityLevel(self, event): name = cat_attrs["name"] dlg = SetOpacityDialog( - self, opacity=value, title=_("Change opacity of class <%s>" % name) + self, opacity=value, title=_("Change opacity of class <%s>") % name ) dlg.applyOpacity.connect( diff --git a/gui/wxpython/iscatt/iscatt_core.py b/gui/wxpython/iscatt/iscatt_core.py index 91ac00014a5..a881fab5845 100644 --- a/gui/wxpython/iscatt/iscatt_core.py +++ b/gui/wxpython/iscatt/iscatt_core.py @@ -295,7 +295,7 @@ def _rasterize(self, grass_region, layer, cat, out_rast): ) if ret != 0: - GException(_("v.build failed:\n%s" % msg)) + GException(_("v.build failed:\n%s") % msg) environs = os.environ.copy() environs["GRASS_REGION"] = grass_region["GRASS_REGION"] @@ -315,7 +315,7 @@ def _rasterize(self, grass_region, layer, cat, out_rast): ) if ret != 0: - GException(_("v.to.rast failed:\n%s" % msg)) + GException(_("v.to.rast failed:\n{messages}").format(messages=msg)) def _create_grass_region_env(self, bbox): r = self.an_data.GetRegion() diff --git a/gui/wxpython/lmgr/frame.py b/gui/wxpython/lmgr/frame.py index de06305086f..a17b168f747 100644 --- a/gui/wxpython/lmgr/frame.py +++ b/gui/wxpython/lmgr/frame.py @@ -1251,8 +1251,8 @@ def OnRunScript(self, event): "Do you want to set the permissions " "that allows you to run this script " "(note that you must be the owner of the file)?" - % os.path.basename(filename) - ), + ) + % os.path.basename(filename), caption=_("Set permission?"), style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION, ) diff --git a/gui/wxpython/lmgr/workspace.py b/gui/wxpython/lmgr/workspace.py index febb3922dd7..bb40469fa56 100644 --- a/gui/wxpython/lmgr/workspace.py +++ b/gui/wxpython/lmgr/workspace.py @@ -548,9 +548,8 @@ def OpenRecentFile(self, path, file_exists, file_history): """ if not file_exists: GError( - _( - "File <{}> doesn't exist." - " It was probably moved or deleted.".format(path) + _("File <{}> doesn't exist. It was probably moved or deleted.").format( + path ), parent=self.lmgr, ) diff --git a/gui/wxpython/location_wizard/dialogs.py b/gui/wxpython/location_wizard/dialogs.py index 16325ddfbe1..079fca1f6d4 100644 --- a/gui/wxpython/location_wizard/dialogs.py +++ b/gui/wxpython/location_wizard/dialogs.py @@ -594,8 +594,8 @@ def __UpdateInfo(self): self.lcols.SetLabel(_("Cols: %d") % self.cols) self.lcells.SetLabel(_("Cells: %d") % self.cells) # 3D - self.ldepth.SetLabel(_("Depth: %d" % self.depth)) - self.lcells3.SetLabel(_("3D Cells: %d" % self.cells3)) + self.ldepth.SetLabel(_("Depth: %d") % self.depth) + self.lcells3.SetLabel(_("3D Cells: %d") % self.cells3) def OnSetButton(self, event=None): """Set default region""" diff --git a/gui/wxpython/location_wizard/wizard.py b/gui/wxpython/location_wizard/wizard.py index 155346b3467..7ba03ebee04 100644 --- a/gui/wxpython/location_wizard/wizard.py +++ b/gui/wxpython/location_wizard/wizard.py @@ -2723,7 +2723,7 @@ def OnWizFinished(self): if not self.filepage.georeffile or not os.path.isfile( self.filepage.georeffile ): - return _("File <%s> not found." % self.filepage.georeffile) + return _("File <%s> not found.") % self.filepage.georeffile grass.create_location( dbase=self.startpage.grassdatabase, diff --git a/gui/wxpython/main_window/frame.py b/gui/wxpython/main_window/frame.py index facb3f8195a..3b9b7f50261 100644 --- a/gui/wxpython/main_window/frame.py +++ b/gui/wxpython/main_window/frame.py @@ -1402,8 +1402,8 @@ def OnRunScript(self, event): "Do you want to set the permissions " "that allows you to run this script " "(note that you must be the owner of the file)?" - % os.path.basename(filename) - ), + ) + % os.path.basename(filename), caption=_("Set permission?"), style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION, ) diff --git a/gui/wxpython/mapdisp/frame.py b/gui/wxpython/mapdisp/frame.py index 8cb8a51d407..0b948f63794 100644 --- a/gui/wxpython/mapdisp/frame.py +++ b/gui/wxpython/mapdisp/frame.py @@ -411,8 +411,9 @@ def AddNviz(self): message=_( "Unable to switch to 3D display mode.\nThe Nviz python extension " "was not found or loaded properly.\n" - "Switching back to 2D display mode.\n\nDetails: %s" % errorMsg - ), + "Switching back to 2D display mode.\n\nDetails: %s" + ) + % errorMsg, ) return diff --git a/gui/wxpython/mapdisp/test_mapdisp.py b/gui/wxpython/mapdisp/test_mapdisp.py index 7a61e196ba9..1494988fafb 100755 --- a/gui/wxpython/mapdisp/test_mapdisp.py +++ b/gui/wxpython/mapdisp/test_mapdisp.py @@ -356,7 +356,7 @@ def main(): tester.testMapWindowRlisetup(map_) else: # TODO: this should not happen but happens - gs.fatal(_("Unknown value %s of test parameter." % test)) + gs.fatal(_("Unknown value %s of test parameter.") % test) app.MainLoop() diff --git a/gui/wxpython/mapwin/analysis.py b/gui/wxpython/mapwin/analysis.py index 490a9d7e866..d0a7be1bc0c 100644 --- a/gui/wxpython/mapwin/analysis.py +++ b/gui/wxpython/mapwin/analysis.py @@ -262,11 +262,7 @@ def Start(self): self._useCtypes = True except ImportError as e: self._giface.WriteWarning( - _( - "Geodesic distance calculation " - "is not available.\n" - "Reason: %s" % e - ) + _("Geodesic distance calculation is not available.\nReason: %s") % e ) def MeasureDist(self, beginpt, endpt): diff --git a/gui/wxpython/rdigit/g.gui.rdigit.py b/gui/wxpython/rdigit/g.gui.rdigit.py index d740b1c3e78..ae8d9ed41d8 100755 --- a/gui/wxpython/rdigit/g.gui.rdigit.py +++ b/gui/wxpython/rdigit/g.gui.rdigit.py @@ -193,10 +193,8 @@ def OnMapCreated(self, name, ltype, add: bool | None = None): if not edit_map: gs.fatal( - _( - "Raster map <{}> not found in current mapset.".format( - options["edit"], - ), + _("Raster map <{}> not found in current mapset.").format( + options["edit"], ), ) else: @@ -209,11 +207,8 @@ def OnMapCreated(self, name, ltype, add: bool | None = None): )["fullname"] if not base_map: gs.fatal( - _( - "Base raster map <{}> not found in " - "current mapset.".format( - options["base"], - ), + _("Base raster map <{}> not found in current mapset.").format( + options["base"], ), ) kwargs["base_map"] = base_map diff --git a/gui/wxpython/rlisetup/frame.py b/gui/wxpython/rlisetup/frame.py index 00e35f66110..3905fa9b09d 100644 --- a/gui/wxpython/rlisetup/frame.py +++ b/gui/wxpython/rlisetup/frame.py @@ -40,9 +40,8 @@ def __init__( self.confilesBox = StaticBox( parent=self.panel, id=wx.ID_ANY, - label=_( - "View and modify the " - "configuration file '{name}'".format(name=self.confile) + label=_("View and modify the configuration file '{name}'").format( + name=self.confile ), ) self.textCtrl = TextCtrl( diff --git a/gui/wxpython/rlisetup/functions.py b/gui/wxpython/rlisetup/functions.py index 28f186fa396..2ce0b747dcd 100644 --- a/gui/wxpython/rlisetup/functions.py +++ b/gui/wxpython/rlisetup/functions.py @@ -153,8 +153,9 @@ def sampleAreaVector( "The raster map <%s> already exists." " Please remove or rename the maps " "with the prefix '%s' or select the " - "option to overwrite existing maps" % (rast_name, outpref) + "option to overwrite existing maps" ) + % (rast_name, outpref) ) return None convertFeature(vect, rast_name, cat, rast, layer, overwrite) diff --git a/gui/wxpython/rlisetup/wizard.py b/gui/wxpython/rlisetup/wizard.py index 54c6dbcd78a..4ecbb60e188 100644 --- a/gui/wxpython/rlisetup/wizard.py +++ b/gui/wxpython/rlisetup/wizard.py @@ -1859,8 +1859,8 @@ def newCat(self): "The raster map <%s> already exists." " Please remove or rename the maps " "with the prefix '%s' or select the " - "option to overwrite existing maps" % (self.outname, self.outpref) - ), + "option to overwrite existing maps" + ).format(self.outname, self.outpref), ) self.parent.wizard.ShowPage(self.parent.samplingareapage) return diff --git a/gui/wxpython/startup/locdownload.py b/gui/wxpython/startup/locdownload.py index f0a1ab13fd2..75eae560244 100644 --- a/gui/wxpython/startup/locdownload.py +++ b/gui/wxpython/startup/locdownload.py @@ -156,12 +156,12 @@ def reporthook(count, block_size, total_size): sys.stdout.write( _( "Download in progress, wait until it is finished " - "{0}%, {1} MB, {2} KB/s, {3:.0f} seconds passed".format( - percent, - progress_size / (1024 * 1024), - speed, - duration, - ), + "{0}%, {1} MB, {2} KB/s, {3:.0f} seconds passed" + ).format( + percent, + progress_size / (1024 * 1024), + speed, + duration, ), ) diff --git a/gui/wxpython/timeline/frame.py b/gui/wxpython/timeline/frame.py index f69dcc89aef..3982034619c 100644 --- a/gui/wxpython/timeline/frame.py +++ b/gui/wxpython/timeline/frame.py @@ -527,7 +527,7 @@ def _checkDatasets(self, datasets): elif len(indices) >= 2: dlg = wx.SingleChoiceDialog( self, - message=_("Please specify the space time dataset <%s>." % dataset), + message=_("Please specify the space time dataset <%s>.") % dataset, caption=_("Ambiguous dataset name"), choices=[ ( diff --git a/gui/wxpython/tplot/frame.py b/gui/wxpython/tplot/frame.py index 62147f54ad6..db21da4cd14 100755 --- a/gui/wxpython/tplot/frame.py +++ b/gui/wxpython/tplot/frame.py @@ -669,8 +669,8 @@ def _getSTVDData(self, timeseries): showTraceback=False, message=_( "No connection between vector map {vmap} " - "and layer {la}".format(vmap=row["name"], la=lay) - ), + "and layer {la}" + ).format(vmap=row["name"], la=lay), ) return vals = gs.vector_db_select( @@ -740,7 +740,7 @@ def _setLabels(self, x): if self.drawX != "": self.axes2d.set_xlabel(self.drawX) elif self.temporalType == "absolute": - self.axes2d.set_xlabel(_("Temporal resolution: %s" % x)) + self.axes2d.set_xlabel(_("Temporal resolution: %s") % x) else: self.axes2d.set_xlabel(_("Time [%s]") % self.unit) if self.drawY != "": @@ -917,8 +917,8 @@ def drawVCats(self): message=_( "Problem getting data from vector temporal" " dataset. Empty list of values for cat " - "{ca}.".format(ca=name_cat[1].replace("cat", "")) - ), + "{ca}." + ).format(ca=name_cat[1].replace("cat", "")), ) continue self.lookUp.AddDataset(yranges=ydata, xranges=xdata, datasetName=name) @@ -1007,9 +1007,8 @@ def OnRedraw(self, event=None): if os.path.exists(self.csvpath) and not self.overwrite: dlg = wx.MessageDialog( self, - _( - "{pa} already exists, do you want " - "to overwrite?".format(pa=self.csvpath) + _("{pa} already exists, do you want to overwrite?").format( + pa=self.csvpath ), _("File exists"), wx.OK | wx.CANCEL | wx.ICON_QUESTION, @@ -1185,7 +1184,7 @@ def _checkDatasets(self, datasets, typ): elif len(indices) >= 2: dlg = wx.SingleChoiceDialog( self, - message=_("Please specify the space time dataset <%s>." % dataset), + message=_("Please specify the space time dataset <%s>.") % dataset, caption=_("Ambiguous dataset name"), choices=[ ( @@ -1368,7 +1367,7 @@ def InfoFormat(timeData, values): text.append(_("Space time 3D raster dataset: %s") % key) text.extend( - (_("Value for {date} is {val}".format(date=val[0], val=val[1])), "\n") + (_("Value for {date} is {val}").format(date=val[0], val=val[1]), "\n") ) text.append(_("Press Del to dismiss.")) diff --git a/gui/wxpython/vnet/vnet_data.py b/gui/wxpython/vnet/vnet_data.py index a1332398a1e..6404fcac5c0 100644 --- a/gui/wxpython/vnet/vnet_data.py +++ b/gui/wxpython/vnet/vnet_data.py @@ -154,7 +154,7 @@ def InputsErrorMsgs( if flags["t"] and "turn_layer" not in relevant_params: GMessage( parent=self.guiparent, - message=_("Module <%s> does not support turns costs." % analysis), + message=_("Module <%s> does not support turns costs.") % analysis, ) return False diff --git a/gui/wxpython/web_services/dialogs.py b/gui/wxpython/web_services/dialogs.py index d67a2b34f7d..e47400cae70 100644 --- a/gui/wxpython/web_services/dialogs.py +++ b/gui/wxpython/web_services/dialogs.py @@ -331,7 +331,6 @@ def OnSettingsChanged(self, data): def OnClose(self, event): """Close the dialog""" - """Close dialog""" if not self.IsModal(): self.Destroy() event.Skip() @@ -378,7 +377,7 @@ def OnConnect(self, event): self.Fit() self.statusbar.SetStatusText( - _("Connecting to <%s>..." % self.server.GetValue().strip()) + _("Connecting to <$s>...") % self.server.GetValue().strip() ) # number of panels already connected @@ -464,14 +463,14 @@ def UpdateDialogAfterConnection(self): ) self._showWsPanel(self.web_service_sel[self.choose_ws_rb.GetSelection()]) self.statusbar.SetStatusText( - _("Connected to <%s>" % self.server.GetValue().strip()) + _("Connected to <%s>") % self.server.GetValue().strip() ) for btn in self.run_btns: btn.Enable(True) # no web service found on server else: self.statusbar.SetStatusText( - _("Unable to connect to <%s>" % self.server.GetValue().strip()) + _("Unable to connect to <%s>") % self.server.GetValue().strip() ) for btn in self.run_btns: btn.Enable(False) @@ -1029,7 +1028,7 @@ def OnSave(self, event): not self.overwrite.IsChecked() and gs.find_file(self.output, "cell", ".")["fullname"] ): - msg = _("Output map <%s> already exists" % self.output) + msg = _("Output map <%s> already exists") % self.output if msg: GMessage(parent=self, message=msg) @@ -1047,8 +1046,8 @@ def OnSave(self, event): if self.region_types["named"].GetValue(): if not gs.find_file(reg_spl[0], "windows", reg_mapset)["fullname"]: - msg = _( - "Region <%s> does not exist." % self.params["region"].GetValue() + msg = ( + _("Region <%s> does not exist.") % self.params["region"].GetValue() ) GWarning(parent=self, message=msg) return diff --git a/gui/wxpython/web_services/widgets.py b/gui/wxpython/web_services/widgets.py index 83fe7a5706c..3cb3af18068 100644 --- a/gui/wxpython/web_services/widgets.py +++ b/gui/wxpython/web_services/widgets.py @@ -538,10 +538,8 @@ def OnCapDownloadDone(self, event): if event.returncode != 0: if self.cmd_err_str: self.cmd_err_str = ( - _( - "Unable to download %s capabilities file\nfrom <%s>:\n" - % (self.ws.replace("_", " "), self.conn["url"]) - ) + _("Unable to download %s capabilities file\nfrom <%s>:\n") + % (self.ws.replace("_", " "), self.conn["url"]) + self.cmd_err_str ) self._postCapParsedEvt(error_msg=self.cmd_err_str) @@ -559,8 +557,9 @@ def _parseCapFile(self, cap_file): except (OSError, ParseError) as error: error_msg = _( "%s web service was not found in fetched capabilities file from " - "<%s>:\n%s\n" % (self.ws, self.conn["url"], str(error)) - ) + "<%s>:\n%s\n" + ) % (self.ws, self.conn["url"], str(error)) + if Debug.GetLevel() != 0: Debug.msg(1, error_msg) self._postCapParsedEvt(None) diff --git a/gui/wxpython/wxgui.py b/gui/wxpython/wxgui.py index 201eb0fe738..447821c6724 100644 --- a/gui/wxpython/wxgui.py +++ b/gui/wxpython/wxgui.py @@ -100,10 +100,10 @@ def show_main_gui(): warning( _( "Current version of wxPython {} is lower than " - "minimum required version {}".format( - wx.__version__, - ".".join(map(str, min_required_wx_version)), - ) + "minimum required version {}" + ).format( + wx.__version__, + ".".join(map(str, min_required_wx_version)), ) ) else: diff --git a/lib/init/grass.py b/lib/init/grass.py index 5a62e65ac53..28537f957b1 100755 --- a/lib/init/grass.py +++ b/lib/init/grass.py @@ -1314,16 +1314,17 @@ def lock_mapset(mapset_path, force_gislock_removal, user): "You can force launching GRASS using -f flag" " (note that you need permission for this operation)." " Have another look in the processor " - "manager just to be sure..." % {"user": user, "file": lockfile} - ) + "manager just to be sure..." + ) % {"user": user, "file": lockfile} + else: try_remove(lockfile) message( _( "%(user)s is currently running GRASS in selected mapset" " (file %(file)s found). Forcing to launch GRASS..." - % {"user": user, "file": lockfile} ) + % {"user": user, "file": lockfile} ) elif ret != 0: msg = ( diff --git a/pyproject.toml b/pyproject.toml index eb866cb0e08..f6d37ac3bea 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -280,61 +280,19 @@ ignore = [ [tool.ruff.lint.per-file-ignores] # See https://docs.astral.sh/ruff/settings/#lint_per-file-ignores # "A005", # builtin-module-shadowing -# "INT002", # f-string-in-get-text-func-call -# "INT001", # format-in-get-text-func-call -# "INT003", # printf-in-get-text-func-call # "PLW0108", # unnecessary-lambda # Ignore `E402` (import violations) in all `__init__.py` files "*/testsuite/**.py" = ["PT009", "PT027"] "__init__.py" = ["E402"] -"general/g.parser/test.py" = ["INT003"] "gui/**" = ["PLW0108"] # See https://github.com/OSGeo/grass/issues/4124 -"gui/wxpython/animation/dialogs.py" = ["INT002"] -"gui/wxpython/animation/temporal_manager.py" = ["INT003"] -"gui/wxpython/core/debug.py" = ["INT002"] -"gui/wxpython/core/render.py" = ["INT003"] -"gui/wxpython/gcp/manager.py" = ["INT002"] -"gui/wxpython/gmodeler/model.py" = ["INT001"] -"gui/wxpython/gmodeler/panels.py" = ["INT002", "INT003"] -"gui/wxpython/gui_core/ghelp.py" = ["INT003"] -"gui/wxpython/gui_core/gselect.py" = ["INT002"] -"gui/wxpython/gui_core/pyedit.py" = ["INT002"] -"gui/wxpython/gui_core/pystc.py" = ["INT002"] -"gui/wxpython/gui_core/query.py" = ["INT003"] -"gui/wxpython/history/browser.py" = ["INT002"] -"gui/wxpython/iclass/dialogs.py" = ["INT003"] -"gui/wxpython/iclass/frame.py" = ["FLY002", "INT003"] -"gui/wxpython/iclass/plots.py" = ["INT003"] +"gui/wxpython/iclass/frame.py" = ["FLY002"] "gui/wxpython/iclass/statistics.py" = ["A005"] -"gui/wxpython/image2target/ii2t_gis_set.py" = ["INT003"] -"gui/wxpython/iscatt/controllers.py" = ["INT003"] -"gui/wxpython/iscatt/dialogs.py" = ["INT003"] -"gui/wxpython/iscatt/frame.py" = ["INT003"] -"gui/wxpython/iscatt/iscatt_core.py" = ["INT003"] "gui/wxpython/iscatt/plots.py" = ["PLW0108"] -"gui/wxpython/lmgr/frame.py" = ["INT003"] -"gui/wxpython/lmgr/workspace.py" = ["INT002"] -"gui/wxpython/location_wizard/dialogs.py" = ["INT003"] -"gui/wxpython/location_wizard/wizard.py" = ["INT003"] -"gui/wxpython/main_window/frame.py" = ["INT003"] -"gui/wxpython/mapdisp/frame.py" = ["INT003"] -"gui/wxpython/mapdisp/test_mapdisp.py" = ["INT003"] -"gui/wxpython/mapwin/analysis.py" = ["INT003"] "gui/wxpython/psmap/utils.py" = ["PGH004"] -"gui/wxpython/rdigit/g.gui.rdigit.py" = ["INT002"] -"gui/wxpython/rlisetup/frame.py" = ["INT002"] -"gui/wxpython/rlisetup/functions.py" = ["INT003"] -"gui/wxpython/rlisetup/wizard.py" = ["INT003"] -"gui/wxpython/startup/locdownload.py" = ["INT002"] -"gui/wxpython/timeline/frame.py" = ["FLY002", "INT003"] -"gui/wxpython/tplot/frame.py" = ["FLY002", "INT002", "INT003"] -"gui/wxpython/vnet/vnet_data.py" = ["INT003"] -"gui/wxpython/web_services/dialogs.py" = ["INT003"] -"gui/wxpython/web_services/widgets.py" = ["INT003"] -"gui/wxpython/wxgui.py" = ["INT002"] +"gui/wxpython/timeline/frame.py" = ["FLY002"] +"gui/wxpython/tplot/frame.py" = ["FLY002"] "gui/wxpython/wxplot/profile.py" = ["A005"] "lib/imagery/testsuite/test_imagery_sigsetfile.py" = ["FURB152"] -"lib/init/grass.py" = ["INT003"] "python/grass/__init__.py" = ["PYI056"] "python/grass/exp*/tests/grass_script_mapset_session_test.py" = ["SIM117"] "python/grass/exp*/tests/grass_script_tmp_mapset_session_test.py" = ["SIM117"] @@ -346,64 +304,22 @@ ignore = [ "python/grass/jupyter/testsuite/interactivemap_test.py" = ["PGH004"] "python/grass/jupyter/testsuite/map_test.py" = ["PGH004"] "python/grass/pydispatch/signal.py" = ["A005"] -"python/grass/pygrass/raster/category.py" = ["INT002"] -"python/grass/pygrass/vector/__init__.py" = ["INT003"] "python/grass/pygrass/vector/geometry.py" = ["PYI024"] "python/grass/pygrass/vector/sql.py" = ["FLY002"] "python/grass/pygrass/vector/testsuite/test_table.py" = ["PLW0108"] "python/grass/script/array.py" = ["A005"] -"python/grass/script/raster.py" = ["INT003"] -"python/grass/temporal/abstract_space_time_dataset.py" = ["INT003"] -"python/grass/temporal/aggregation.py" = ["INT003"] -"python/grass/temporal/c_libraries_interface.py" = ["INT003"] -"python/grass/temporal/core.py" = ["INT002", "INT003"] -"python/grass/temporal/datetime_math.py" = ["INT003"] -"python/grass/temporal/mapcalc.py" = ["INT003"] -"python/grass/temporal/space_time_datasets.py" = ["INT003"] -"python/grass/temporal/stds_export.py" = ["INT003"] -"python/grass/temporal/stds_import.py" = ["INT003"] -"python/grass/temporal/temporal_algebra.py" = ["INT003"] -"python/grass/temporal/temporal_raster_base_algebra.py" = ["INT003"] -"python/grass/temporal/univar_statistics.py" = ["INT002"] "raster3d/r3.flow/testsuite/r3flow_test.py" = ["FLY002"] "raster3d/r3.gradient/testsuite/r3gradient_test.py" = ["FLY002"] -"scripts/d.polar/d.polar.py" = ["FURB154", "INT002"] -"scripts/g.extension.all/g.extension.all.py" = ["INT002"] -"scripts/g.extension/g.extension.py" = ["INT002"] -"scripts/i.oif/i.oif.py" = ["INT003"] -"scripts/i.pansharpen/i.pansharpen.py" = ["FLY002", "INT003"] -"scripts/i.spectral/i.spectral.py" = ["FLY002", "INT002"] +"scripts/d.polar/d.polar.py" = ["FURB154"] +"scripts/i.pansharpen/i.pansharpen.py" = ["FLY002"] +"scripts/i.spectral/i.spectral.py" = ["FLY002"] "scripts/r.in.srtm/r.in.srtm.py" = ["FLY002"] -"scripts/r.in.wms/wms_base.py" = ["INT003"] -"scripts/r.in.wms/wms_cap_parsers.py" = ["INT003"] -"scripts/r.in.wms/wms_drv.py" = ["INT003"] -"scripts/r.in.wms/wms_gdal_drv.py" = ["INT003"] -"scripts/r.pack/r.pack.py" = ["INT003"] -"scripts/r.tileset/r.tileset.py" = ["INT003"] -"scripts/r.unpack/r.unpack.py" = ["INT002"] -"scripts/v.rast.stats/v.rast.stats.py" = ["INT002"] -"scripts/v.to.lines/v.to.lines.py" = ["INT003"] -"scripts/v.unpack/v.unpack.py" = ["INT002", "INT003"] -"scripts/v.what.strds/v.what.strds.py" = ["INT003"] -"temporal/t.rast.accdetect/t.rast.accdetect.py" = ["INT003"] -"temporal/t.rast.accumulate/t.rast.accumulate.py" = ["INT003"] "temporal/t.rast.algebra/testsu*/*_algebra_arithmetic.py" = ["FLY002"] -"temporal/t.rast.export/t.rast.export.py" = ["INT002"] -"temporal/t.rast.gapfill/t.rast.gapfill.py" = ["INT003"] -"temporal/t.rast.list/t.rast.list.py" = ["INT002"] -"temporal/t.rast.out.vtk/t.rast.out.vtk.py" = ["INT003"] -"temporal/t.rast.series/t.rast.series.py" = ["INT002"] -"temporal/t.rast.to.rast3/t.rast.to.rast3.py" = ["INT003"] -"temporal/t.rast.what/t.rast.what.py" = ["INT003"] "temporal/t.register/testsu*/*_raster_different_local.py" = ["FLY002"] "temporal/t.register/testsu*/*_raster_mapmetadata.py" = ["FLY002"] "temporal/t.register/testsuite/test_t_register_raster.py" = ["FLY002"] "temporal/t.register/testsuite/test_t_register_raster_file.py" = ["FLY002"] -"temporal/t.remove/t.remove.py" = ["INT002", "INT003"] -"temporal/t.unregister/t.unregister.py" = ["INT003"] -"temporal/t.vect.observe.strds/t.vect.observe.strds.py" = ["INT003"] "utils/generate_release_notes.py" = ["PGH004"] -"utils/mkhtml.py" = ["INT002"] "vector/v.fill.holes/examples.ipynb" = ["PTH201"] [tool.ruff.lint.flake8-import-conventions.extend-aliases] diff --git a/python/grass/pygrass/raster/category.py b/python/grass/pygrass/raster/category.py index 00381f91130..cfeb84ddde2 100644 --- a/python/grass/pygrass/raster/category.py +++ b/python/grass/pygrass/raster/category.py @@ -68,7 +68,7 @@ def _get_mtype(self): def _set_mtype(self, mtype): if mtype.upper() not in {"CELL", "FCELL", "DCELL"}: - raise ValueError(_("Raster type: {0} not supported".format(mtype))) + raise ValueError(_("Raster type: {0} not supported").format(mtype)) self._mtype = mtype self._gtype = RTYPE[self.mtype]["grass type"] diff --git a/python/grass/pygrass/vector/__init__.py b/python/grass/pygrass/vector/__init__.py index 6f29a167b2e..b5a24e8d9c1 100644 --- a/python/grass/pygrass/vector/__init__.py +++ b/python/grass/pygrass/vector/__init__.py @@ -855,7 +855,7 @@ def features_to_wkb_list(self, bbox=None, feature_type="point", field=1): if not barray: if error == -1: raise GrassError( - _("Unable to read line of feature %i" % (f_id)) + _("Unable to read line of feature %i") % (f_id) ) if error == -2: print("Empty feature %i" % (f_id)) @@ -947,7 +947,7 @@ def areas_to_wkb_list(self, bbox=None, field=1): self.c_mapinfo, a_id, ctypes.byref(size) ) if not barray: - raise GrassError(_("Unable to read area with id %i" % (a_id))) + raise GrassError(_("Unable to read area with id %i") % (a_id)) pcat = None c_ok = libvect.Vect_get_area_cats( diff --git a/python/grass/script/raster.py b/python/grass/script/raster.py index f9937902dab..32c0639b32f 100644 --- a/python/grass/script/raster.py +++ b/python/grass/script/raster.py @@ -65,8 +65,8 @@ def raster_history(map, overwrite=False, env=None): _( "Unable to write history for <%(map)s>. " "Raster map <%(map)s> not found in current mapset." - % {"map": map, "map": map} ) + % {"map": map, "map": map} ) return False diff --git a/python/grass/temporal/abstract_space_time_dataset.py b/python/grass/temporal/abstract_space_time_dataset.py index 515502ff3f1..cc68f92af2b 100644 --- a/python/grass/temporal/abstract_space_time_dataset.py +++ b/python/grass/temporal/abstract_space_time_dataset.py @@ -2025,7 +2025,7 @@ def shift(self, gran, dbif=None): ) if not check_granularity_string(gran, self.get_temporal_type()): - self.msgr.error(_("Wrong granularity format: %s" % (gran))) + self.msgr.error(_("Wrong granularity format: %s") % (gran)) return False dbif, connection_state_changed = init_dbif(dbif) diff --git a/python/grass/temporal/aggregation.py b/python/grass/temporal/aggregation.py index 1ea60eaeea1..354600855e0 100644 --- a/python/grass/temporal/aggregation.py +++ b/python/grass/temporal/aggregation.py @@ -154,16 +154,14 @@ def aggregate_raster_maps( _( "Raster map <%(name)s> is already in temporal " "database, use overwrite flag to overwrite" - % ({"name": new_map.get_name()}) ) + % ({"name": new_map.get_name()}) ) return msgr.verbose( - _( - "Computing aggregation of maps between %(st)s - %(end)s" - % {"st": str(start), "end": str(end)} - ) + _("Computing aggregation of maps between %(st)s - %(end)s") + % {"st": str(start), "end": str(end)} ) # Create the r.series input file @@ -356,8 +354,9 @@ def aggregate_by_topology( _( "Unable to perform aggregation. Output raster " "map <%(name)s> exists and overwrite flag was " - "not set" % ({"name": output_name}) + "not set" ) + % ({"name": output_name}) ) output_list.append(map_layer) @@ -380,8 +379,8 @@ def aggregate_by_topology( "reached (%i). The module r.series will " "be run with flag z, to avoid open " "files limit exceeding." - % (int(file_limit), len(aggregation_list)) ) + % (int(file_limit), len(aggregation_list)) ) mod(flags="z") process_queue.put(mod) diff --git a/python/grass/temporal/c_libraries_interface.py b/python/grass/temporal/c_libraries_interface.py index 6b67dfb9869..27e0ff62238 100644 --- a/python/grass/temporal/c_libraries_interface.py +++ b/python/grass/temporal/c_libraries_interface.py @@ -815,7 +815,7 @@ def _read_raster3d_info(name, mapset): ) if not g3map: - logging.error(_("Unable to open 3D raster map <%s>" % (name))) + logging.error(_("Unable to open 3D raster map <%s>"), (name)) return None maptype = libraster3d.Rast3d_file_type_map(g3map) @@ -830,7 +830,7 @@ def _read_raster3d_info(name, mapset): max = libgis.DCELL() ret = libraster3d.Rast3d_range_load(g3map) if not ret: - logging.error(_("Unable to load range of 3D raster map <%s>" % (name))) + logging.error(_("Unable to load range of 3D raster map <%s>"), (name)) return None libraster3d.Rast3d_range_min_max(g3map, byref(min), byref(max)) @@ -844,7 +844,7 @@ def _read_raster3d_info(name, mapset): kvp["max"] = float(max.value) if not libraster3d.Rast3d_close(g3map): - logging.error(_("Unable to close 3D raster map <%s>" % (name))) + logging.error(_("Unable to close 3D raster map <%s>"), (name)) return None return kvp @@ -887,10 +887,8 @@ def _read_vector_info(name, mapset): with_topo = False if libvector.Vect_open_old2(byref(Map), name, mapset, "1") < 1: logging.error( - _( - "Unable to open vector map <%s>" - % (libvector.Vect_get_full_name(byref(Map))) - ) + _("Unable to open vector map <%s>"), + (libvector.Vect_get_full_name(byref(Map))), ) return None diff --git a/python/grass/temporal/core.py b/python/grass/temporal/core.py index dc74bff151d..0448ff2371d 100644 --- a/python/grass/temporal/core.py +++ b/python/grass/temporal/core.py @@ -769,12 +769,12 @@ def init(raise_fatal_error=False, skip_db_version_check=False): "Temporal database version mismatch detected.\n{backup}" "Supported temporal database version is: {tdb}\n" "Your existing temporal database version: {ctdb}\n" - "Current temporal database info: {info}".format( - backup=backup_howto, - tdb=tgis_db_version, - ctdb=tgis_db_version_meta, - info=get_database_info_string(), - ) + "Current temporal database info: {info}" + ).format( + backup=backup_howto, + tdb=tgis_db_version, + ctdb=tgis_db_version_meta, + info=get_database_info_string(), ) if tgis_db_version_meta == 2 and tgis_db_version == 3: @@ -784,8 +784,8 @@ def init(raise_fatal_error=False, skip_db_version_check=False): msgr.fatal( _( "The format of your actual temporal database is " - "not supported any more. {m}".format(m=message) - ) + "not supported any more. {m}" + ).format(m=message) ) return @@ -862,7 +862,7 @@ def create_temporal_database(dbif): stvds_tables_sql = stds_tables_template_sql.replace("STDS", "stvds") str3ds_tables_sql = stds_tables_template_sql.replace("STDS", "str3ds") - msgr.message(_("Creating temporal database: %s" % (str(tgis_database_string)))) + msgr.message(_("Creating temporal database: %s") % (str(tgis_database_string))) if tgis_backend == "sqlite": # We need to create the sqlite3 database path if it does not exist @@ -875,8 +875,9 @@ def create_temporal_database(dbif): _( "Unable to create SQLite temporal database\n" "Exception: %s\nPlease use t.connect to set a " - "read- and writable temporal database path" % (e) + "read- and writable temporal database path" ) + % (e) ) # Set up the trigger that takes care of @@ -1502,7 +1503,7 @@ def execute(self, statement, args=None): except: if connected: self.close() - self.msgr.error(_("Unable to execute :\n %(sql)s" % {"sql": statement})) + self.msgr.error(_("Unable to execute :\n %(sql)s") % {"sql": statement}) raise if connected: @@ -1546,7 +1547,7 @@ def execute_transaction(self, statement, mapset=None): if connected: self.close() self.msgr.error( - _("Unable to execute transaction:\n %(sql)s" % {"sql": statement}) + _("Unable to execute transaction:\n %(sql)s") % {"sql": statement} ) raise diff --git a/python/grass/temporal/datetime_math.py b/python/grass/temporal/datetime_math.py index 9b680760775..1031582fe23 100644 --- a/python/grass/temporal/datetime_math.py +++ b/python/grass/temporal/datetime_math.py @@ -828,7 +828,7 @@ def check_datetime_string(time_string, use_dateutil=True): try: return datetime.strptime(time_string, time_format) except: - return _("Unable to parse time string: %s" % time_string) + return _("Unable to parse time string: %s") % time_string ############################################################################### diff --git a/python/grass/temporal/mapcalc.py b/python/grass/temporal/mapcalc.py index 5f29cd32747..8761e86160f 100644 --- a/python/grass/temporal/mapcalc.py +++ b/python/grass/temporal/mapcalc.py @@ -508,90 +508,72 @@ def _parse_start_operators(expr, is_time_absolute, current): if expr.find("start_year()") >= 0: if not is_time_absolute: msgr.fatal( - _( - "The temporal operators <%s> support only absolute " - "time." % ("start_*") - ) + _("The temporal operators <%s> support only absolute time.") + % ("start_*") ) expr = expr.replace("start_year()", str(start.year)) if expr.find("start_month()") >= 0: if not is_time_absolute: msgr.fatal( - _( - "The temporal operators <%s> support only absolute " - "time." % ("start_*") - ) + _("The temporal operators <%s> support only absolute time.") + % ("start_*") ) expr = expr.replace("start_month()", str(start.month)) if expr.find("start_week()") >= 0: if not is_time_absolute: msgr.fatal( - _( - "The temporal operators <%s> support only absolute " - "time." % ("start_*") - ) + _("The temporal operators <%s> support only absolute time.") + % ("start_*") ) expr = expr.replace("start_week()", str(start.isocalendar()[1])) if expr.find("start_day()") >= 0: if not is_time_absolute: msgr.fatal( - _( - "The temporal operators <%s> support only absolute " - "time." % ("start_*") - ) + _("The temporal operators <%s> support only absolute time.") + % ("start_*") ) expr = expr.replace("start_day()", str(start.day)) if expr.find("start_hour()") >= 0: if not is_time_absolute: msgr.fatal( - _( - "The temporal operators <%s> support only absolute " - "time." % ("start_*") - ) + _("The temporal operators <%s> support only absolute time.") + % ("start_*") ) expr = expr.replace("start_hour()", str(start.hour)) if expr.find("start_minute()") >= 0: if not is_time_absolute: msgr.fatal( - _( - "The temporal operators <%s> support only absolute " - "time." % ("start_*") - ) + _("The temporal operators <%s> support only absolute time.") + % ("start_*") ) expr = expr.replace("start_minute()", str(start.minute)) if expr.find("start_second()") >= 0: if not is_time_absolute: msgr.fatal( - _( - "The temporal operators <%s> support only absolute " - "time." % ("start_*") - ) + _("The temporal operators <%s> support only absolute time.") + % ("start_*") ) expr = expr.replace("start_second()", str(start.second)) if expr.find("start_dow()") >= 0: if not is_time_absolute: msgr.fatal( - _( - "The temporal operators <%s> support only absolute " - "time." % ("start_*") - ) + _("The temporal operators <%s> support only absolute time.") + % ("start_*") ) expr = expr.replace("start_dow()", str(start.isoweekday())) if expr.find("start_doy()") >= 0: if not is_time_absolute: msgr.fatal( - _( - "The temporal operators <%s> support only absolute " - "time." % ("start_*") - ) + _("The temporal operators <%s> support only absolute time.") + % ("start_*") ) year = datetime(start.year, 1, 1) delta = start - year @@ -628,10 +610,7 @@ def _parse_end_operators(expr, is_time_absolute, current): if expr.find("end_year()") >= 0: if not is_time_absolute: msgr.fatal( - _( - "The temporal operators <%s> support only absolute " - "time." % ("end_*") - ) + _("The temporal operators <%s> support only absolute time.") % ("end_*") ) if not end: expr = expr.replace("end_year()", "null()") @@ -641,10 +620,7 @@ def _parse_end_operators(expr, is_time_absolute, current): if expr.find("end_month()") >= 0: if not is_time_absolute: msgr.fatal( - _( - "The temporal operators <%s> support only absolute " - "time." % ("end_*") - ) + _("The temporal operators <%s> support only absolute time.") % ("end_*") ) if not end: expr = expr.replace("end_month()", "null()") @@ -654,10 +630,7 @@ def _parse_end_operators(expr, is_time_absolute, current): if expr.find("end_week()") >= 0: if not is_time_absolute: msgr.fatal( - _( - "The temporal operators <%s> support only absolute " - "time." % ("end_*") - ) + _("The temporal operators <%s> support only absolute time.") % ("end_*") ) if not end: expr = expr.replace("end_week()", "null()") @@ -667,10 +640,7 @@ def _parse_end_operators(expr, is_time_absolute, current): if expr.find("end_day()") >= 0: if not is_time_absolute: msgr.fatal( - _( - "The temporal operators <%s> support only absolute " - "time." % ("end_*") - ) + _("The temporal operators <%s> support only absolute time.") % ("end_*") ) if not end: expr = expr.replace("end_day()", "null()") @@ -680,10 +650,7 @@ def _parse_end_operators(expr, is_time_absolute, current): if expr.find("end_hour()") >= 0: if not is_time_absolute: msgr.fatal( - _( - "The temporal operators <%s> support only absolute " - "time." % ("end_*") - ) + _("The temporal operators <%s> support only absolute time.") % ("end_*") ) if not end: expr = expr.replace("end_hour()", "null()") @@ -693,10 +660,7 @@ def _parse_end_operators(expr, is_time_absolute, current): if expr.find("end_minute()") >= 0: if not is_time_absolute: msgr.fatal( - _( - "The temporal operators <%s> support only absolute " - "time." % ("end_*") - ) + _("The temporal operators <%s> support only absolute time.") % ("end_*") ) if not end: expr = expr.replace("end_minute()", "null()") @@ -706,10 +670,7 @@ def _parse_end_operators(expr, is_time_absolute, current): if expr.find("end_second()") >= 0: if not is_time_absolute: msgr.fatal( - _( - "The temporal operators <%s> support only absolute " - "time." % ("end_*") - ) + _("The temporal operators <%s> support only absolute time.") % ("end_*") ) if not end: expr = expr.replace("end_second()", "null()") @@ -719,10 +680,7 @@ def _parse_end_operators(expr, is_time_absolute, current): if expr.find("end_dow()") >= 0: if not is_time_absolute: msgr.fatal( - _( - "The temporal operators <%s> support only absolute " - "time." % ("end_*") - ) + _("The temporal operators <%s> support only absolute time.") % ("end_*") ) if not end: expr = expr.replace("end_dow()", "null()") @@ -732,10 +690,7 @@ def _parse_end_operators(expr, is_time_absolute, current): if expr.find("end_doy()") >= 0: if not is_time_absolute: msgr.fatal( - _( - "The temporal operators <%s> support only absolute " - "time." % ("end_*") - ) + _("The temporal operators <%s> support only absolute time.") % ("end_*") ) if not end: expr = expr.replace("end_doy()", "null()") diff --git a/python/grass/temporal/space_time_datasets.py b/python/grass/temporal/space_time_datasets.py index a73e03fff46..ec01bf45baa 100644 --- a/python/grass/temporal/space_time_datasets.py +++ b/python/grass/temporal/space_time_datasets.py @@ -288,10 +288,8 @@ def read_timestamp_from_grass(self): if check < 1: self.msgr.error( - _( - "Unable to read timestamp file " - "for raster map <%s>" % (self.get_map_id()) - ) + _("Unable to read timestamp file for raster map <%s>") + % (self.get_map_id()) ) return False @@ -316,19 +314,15 @@ def write_timestamp_to_grass(self): if check == -1: self.msgr.error( - _( - "Unable to create timestamp file " - "for raster map <%s>" % (self.get_map_id()) - ) + _("Unable to create timestamp file for raster map <%s>") + % (self.get_map_id()) ) return False if check == -2: self.msgr.error( - _( - "Invalid datetime in timestamp for raster map " - "<%s>" % (self.get_map_id()) - ) + _("Invalid datetime in timestamp for raster map <%s>") + % (self.get_map_id()) ) return False @@ -350,7 +344,7 @@ def remove_timestamp_from_grass(self): if check == -1: self.msgr.error( - _("Unable to remove timestamp for raster map <%s>" % (self.get_name())) + _("Unable to remove timestamp for raster map <%s>") % (self.get_name()) ) return False @@ -390,10 +384,8 @@ def write_semantic_label_to_grass(self): ) if check == -1: self.msgr.error( - _( - "Unable to write semantic label for raster map <%s>" - % (self.get_name()) - ) + _("Unable to write semantic label for raster map <%s>") + % (self.get_name()) ) return False @@ -741,10 +733,8 @@ def read_timestamp_from_grass(self): if check < 1: self.msgr.error( - _( - "Unable to read timestamp file " - "for 3D raster map <%s>" % (self.get_map_id()) - ) + _("Unable to read timestamp file for 3D raster map <%s>") + % (self.get_map_id()) ) return False @@ -769,19 +759,15 @@ def write_timestamp_to_grass(self): if check == -1: self.msgr.error( - _( - "Unable to create timestamp file " - "for 3D raster map <%s>" % (self.get_map_id()) - ) + _("Unable to create timestamp file for 3D raster map <%s>") + % (self.get_map_id()) ) return False if check == -2: self.msgr.error( - _( - "Invalid datetime in timestamp for 3D raster " - "map <%s>" % (self.get_map_id()) - ) + _("Invalid datetime in timestamp for 3D raster map <%s>") + % (self.get_map_id()) ) return False @@ -802,10 +788,7 @@ def remove_timestamp_from_grass(self): if check == -1: self.msgr.error( - _( - "Unable to remove timestamp for raster map " - "<%s>" % (self.get_name()) - ) + _("Unable to remove timestamp for raster map <%s>") % (self.get_name()) ) return False @@ -1091,10 +1074,8 @@ def read_timestamp_from_grass(self): if check < 1: self.msgr.error( - _( - "Unable to read timestamp file " - "for vector map <%s>" % (self.get_map_id()) - ) + _("Unable to read timestamp file for vector map <%s>") + % (self.get_map_id()) ) return False @@ -1120,19 +1101,15 @@ def write_timestamp_to_grass(self): if check == -1: self.msgr.error( - _( - "Unable to create timestamp file " - "for vector map <%s>" % (self.get_map_id()) - ) + _("Unable to create timestamp file for vector map <%s>") + % (self.get_map_id()) ) return False if check == -2: self.msgr.error( - _( - "Invalid datetime in timestamp for vector " - "map <%s>" % (self.get_map_id()) - ) + _("Invalid datetime in timestamp for vector map <%s>") + % (self.get_map_id()) ) return False @@ -1148,9 +1125,8 @@ def remove_timestamp_from_grass(self): if check == -1: self.msgr.error( - _( - "Unable to remove timestamp for vector " - "map <%s>" % (self.get_name()) + _("Unable to remove timestamp for vector map <%s>").format( + self.get_name() ) ) return False diff --git a/python/grass/temporal/stds_export.py b/python/grass/temporal/stds_export.py index 70dbdcada0f..ff2731b102a 100644 --- a/python/grass/temporal/stds_export.py +++ b/python/grass/temporal/stds_export.py @@ -127,7 +127,7 @@ def _export_raster_maps_as_gdal( except CalledModuleError: shutil.rmtree(new_cwd) tar.close() - gs.fatal(_("Unable to export raster map <%s>" % name)) + gs.fatal(_("Unable to export raster map <%s>") % name) tar.add(out_name) @@ -139,10 +139,7 @@ def _export_raster_maps_as_gdal( shutil.rmtree(new_cwd) tar.close() gs.fatal( - _( - "Unable to export color rules for raster " - "map <%s> r.out.gdal" % name - ) + _("Unable to export color rules for raster map <%s> r.out.gdal") % name ) tar.add(out_name) @@ -168,7 +165,7 @@ def _export_raster_maps(rows, tar, list_file, new_cwd, fs): except CalledModuleError: shutil.rmtree(new_cwd) tar.close() - gs.fatal(_("Unable to export raster map <%s> with r.pack" % name)) + gs.fatal(_("Unable to export raster map <%s> with r.pack") % name) tar.add(name + ".pack") @@ -201,7 +198,7 @@ def _export_vector_maps_as_gml(rows, tar, list_file, new_cwd, fs): except CalledModuleError: shutil.rmtree(new_cwd) tar.close() - gs.fatal(_("Unable to export vector map <%s> as GML with v.out.ogr" % name)) + gs.fatal(_("Unable to export vector map <%s> as GML with v.out.ogr") % name) tar.add(name + ".xml") tar.add(name + ".xsd") @@ -236,7 +233,7 @@ def _export_vector_maps_as_gpkg(rows, tar, list_file, new_cwd, fs): shutil.rmtree(new_cwd) tar.close() gs.fatal( - _("Unable to export vector map <%s> as GPKG with v.out.ogr" % name) + _("Unable to export vector map <%s> as GPKG with v.out.ogr") % name ) tar.add(name + ".gpkg") @@ -269,7 +266,7 @@ def _export_vector_maps(rows, tar, list_file, new_cwd, fs): except CalledModuleError: shutil.rmtree(new_cwd) tar.close() - gs.fatal(_("Unable to export vector map <%s> with v.pack" % name)) + gs.fatal(_("Unable to export vector map <%s> with v.pack") % name) tar.add(name + ".pack") @@ -295,7 +292,7 @@ def _export_raster3d_maps(rows, tar, list_file, new_cwd, fs): except CalledModuleError: shutil.rmtree(new_cwd) tar.close() - gs.fatal(_("Unable to export raster map <%s> with r3.pack" % name)) + gs.fatal(_("Unable to export raster map <%s> with r3.pack") % name) tar.add(name + ".pack") diff --git a/python/grass/temporal/stds_import.py b/python/grass/temporal/stds_import.py index 03810e49b89..ae587cadb0f 100644 --- a/python/grass/temporal/stds_import.py +++ b/python/grass/temporal/stds_import.py @@ -254,10 +254,8 @@ def import_stds( # Check for important files msgr = get_tgis_message_interface() msgr.message( - _( - "Checking validity of input file (size: %0.1f MB). Make take a while..." - % (os.path.getsize(input) / (1024 * 1024.0)) - ) + _("Checking validity of input file (size: %0.1f MB). Make take a while...") + % (os.path.getsize(input) / (1024 * 1024.0)) ) members = tar.getnames() # Make sure that the basenames of the files are used for comparison diff --git a/python/grass/temporal/temporal_algebra.py b/python/grass/temporal/temporal_algebra.py index 3736a15315a..1b9a126c635 100644 --- a/python/grass/temporal/temporal_algebra.py +++ b/python/grass/temporal/temporal_algebra.py @@ -1195,7 +1195,7 @@ def remove_maps(self): for key, value in map_names.items(): if value: - self.msgr.message(_("Removing un-needed or empty %s maps" % (key))) + self.msgr.message(_("Removing un-needed or empty %s maps") % (key)) self._remove_maps(value, key) def _remove_maps(self, namelist, map_type): @@ -2392,8 +2392,8 @@ def p_statement_assign(self, t): _( "The resulting space time dataset type <%(a)s> " "is different from the requested type <%(b)s>" - % ({"a": maps_stds_type, "b": self.stdstype}) ) + % ({"a": maps_stds_type, "b": self.stdstype}) ) else: map_type_2 = map_i.get_type() diff --git a/python/grass/temporal/temporal_raster_base_algebra.py b/python/grass/temporal/temporal_raster_base_algebra.py index 76517db30cc..3424110803f 100644 --- a/python/grass/temporal/temporal_raster_base_algebra.py +++ b/python/grass/temporal/temporal_raster_base_algebra.py @@ -873,7 +873,7 @@ def p_statement_assign(self, t): process_queue.put(m) else: - self.msgr.error(_("Error computing map <%s>" % map_i.get_id())) + self.msgr.error(_("Error computing map <%s>") % map_i.get_id()) count += 1 if self.dry_run is False: diff --git a/python/grass/temporal/univar_statistics.py b/python/grass/temporal/univar_statistics.py index 9bf29f74eba..96a9f65474d 100755 --- a/python/grass/temporal/univar_statistics.py +++ b/python/grass/temporal/univar_statistics.py @@ -65,12 +65,9 @@ def compute_univar_stats(registered_map_info, stats_module, fs, rast_region=Fals if not univar_stats: gs.warning( - _( - "Unable to get statistics for {voxel}raster map " - "<{rmap}>".format( - rmap=id, voxel="" if stats_module.name == "r.univar" else "3d " - ) - ) + _("Unable to get statistics for raster map <%s>") % id + if stats_module.name == "r.univar" + else _("Unable to get statistics for 3d raster map <%s>") % id ) return None eol = "" diff --git a/scripts/d.polar/d.polar.py b/scripts/d.polar/d.polar.py index f592dd89b63..915177e94d4 100755 --- a/scripts/d.polar/d.polar.py +++ b/scripts/d.polar/d.polar.py @@ -425,8 +425,8 @@ def main(): gcore.fatal( _( "EPS output file path <{}>, doesn't exists. " - "Set new output file path.".format(eps) - ) + "Set new output file path." + ).format(eps) ) else: eps = basename(eps, "eps") + ".eps" @@ -436,8 +436,8 @@ def main(): gcore.fatal( _( "option : <{}> exists. To overwrite, " - "use the --overwrite flag.".format(eps) - ) + "use the --overwrite flag." + ).format(eps) ) # check if we have xgraph (if no EPS output requested) diff --git a/scripts/g.extension.all/g.extension.all.py b/scripts/g.extension.all/g.extension.all.py index 026589cca10..7f0b36f4dc6 100644 --- a/scripts/g.extension.all/g.extension.all.py +++ b/scripts/g.extension.all/g.extension.all.py @@ -111,11 +111,11 @@ def download_modules_xml_file(url, response_format, *args, **kwargs): _( "Download file from <{url}>, " "return status code {code}, " - "{desc}".format( - url=url, - code=response.code, - desc=desc, - ), + "{desc}" + ).format( + url=url, + code=response.code, + desc=desc, ), ) if response_format not in response.getheader("Content-Type"): @@ -123,10 +123,10 @@ def download_modules_xml_file(url, response_format, *args, **kwargs): _( "Wrong file format downloaded. " "Check url <{url}>. Allowed file format is " - "{response_format}.".format( - url=url, - response_format=response_format, - ), + "{response_format}." + ).format( + url=url, + response_format=response_format, ), ) return response @@ -138,8 +138,8 @@ def download_modules_xml_file(url, response_format, *args, **kwargs): "The download of the modules.xml file " "from the server was not successful. " "File on the server <{url}> doesn't " - "exists.".format(url=url), - ), + "exists." + ).format(url=url), ) else: return download_modules_xml_file( @@ -148,11 +148,8 @@ def download_modules_xml_file(url, response_format, *args, **kwargs): ) except URLError: gs.fatal( - _( - "Download file from <{url}>, " - "failed. Check internet connection.".format( - url=url, - ), + _("Download file from <{url}>, failed. Check internet connection.").format( + url=url, ), ) @@ -199,9 +196,8 @@ def find_addon_name(addons): gs.warning( _( "The <{}> addon cannot be reinstalled. " - "Addon wasn't found among the official " - "addons.".format(addon) - ), + "Addon wasn't found among the official addons." + ).format(addon), ) return set(result) diff --git a/scripts/g.extension/g.extension.py b/scripts/g.extension/g.extension.py index 3b0c15c0bfc..bf8cd183135 100644 --- a/scripts/g.extension/g.extension.py +++ b/scripts/g.extension/g.extension.py @@ -541,10 +541,8 @@ def get_default_branch(full_url): organization, repository = url_parts.path.split("/")[1:3] except URLError: gs.fatal( - _( - "Cannot retrieve organization and repository from URL: <{}>.".format( - full_url - ) + _("Cannot retrieve organization and repository from URL: <{}>.").format( + full_url ) ) # Construct API call and retrieve default branch @@ -583,10 +581,8 @@ def etree_fromurl(url): _( "Download file from <{url}>," " failed. File is not on the server or" - " check your internet connection.".format( - url=url, - ), - ), + " check your internet connection." + ).format(url=url), ) return ET.fromstring(file_.read()) diff --git a/scripts/i.oif/i.oif.py b/scripts/i.oif/i.oif.py index cfee7890120..aea65197954 100755 --- a/scripts/i.oif/i.oif.py +++ b/scripts/i.oif/i.oif.py @@ -47,7 +47,7 @@ def oifcalc(sdev, corr, k1, k2, k3): - grass.debug(_("Calculating OIF for combination: %s, %s, %s" % (k1, k2, k3)), 1) + grass.debug(_("Calculating OIF for combination: %s, %s, %s") % (k1, k2, k3), 1) # calculate SUM of Stddeviations: ssdev = [sdev[k1], sdev[k2], sdev[k3]] numer = sum(ssdev) diff --git a/scripts/i.pansharpen/i.pansharpen.py b/scripts/i.pansharpen/i.pansharpen.py index f344c5ac7fd..c62a53a536a 100755 --- a/scripts/i.pansharpen/i.pansharpen.py +++ b/scripts/i.pansharpen/i.pansharpen.py @@ -402,7 +402,7 @@ def main(): gs.run_command("g.region", res=panres, align=pan) # Select sharpening method - gs.message(_("Performing pan sharpening with hi res pan image: %f" % panres)) + gs.message(_("Performing pan sharpening with hi res pan image: %f") % panres) if sharpen == "brovey": brovey(pan, ms1, ms2, ms3, out, pid, sproc) elif sharpen == "ihs": @@ -437,8 +437,8 @@ def main(): for ch in ["red", "green", "blue"]: gs.verbose(_("%s_%s") % (out, ch)) - gs.verbose(_("To visualize output, run: g.region -p raster=%s_red" % out)) - gs.verbose(_("d.rgb r=%s_red g=%s_green b=%s_blue" % (out, out, out))) + gs.verbose(_("To visualize output, run: g.region -p raster=%s_red") % out) + gs.verbose(_("d.rgb r=%s_red g=%s_green b=%s_blue") % (out, out, out)) gs.verbose( _("If desired, combine channels into a single RGB map with 'r.composite'.") ) diff --git a/scripts/i.spectral/i.spectral.py b/scripts/i.spectral/i.spectral.py index d40c1076169..171eac7359c 100755 --- a/scripts/i.spectral/i.spectral.py +++ b/scripts/i.spectral/i.spectral.py @@ -197,8 +197,8 @@ def draw_linegraph(what): gcore.fatal( _( "Supported monitor isn't running. Please launch one of the" - " monitors {}.".format(", ".join(supported_monitors)) - ) + " monitors {}." + ).format(", ".join(supported_monitors)) ) selected_monitor = gcore.read_command("d.mon", flags="p", quiet=True).replace( "\n", "" @@ -207,17 +207,15 @@ def draw_linegraph(what): gcore.fatal( _( "Supported monitor isn't selected. Please select one of the" - " monitors {}.".format(", ".join(supported_monitors)) - ) + " monitors {}." + ).format(", ".join(supported_monitors)) ) with open(gcore.parse_command("d.mon", flags="g", quiet=True)["env"]) as f: for line in f: if "GRASS_RENDER_FILE=" in line: gcore.info( - _( - "{} monitor is used, output file {}".format( - selected_monitor.capitalize(), line.split("=")[-1] - ) + _("{} monitor is used, output file {}").format( + selected_monitor.capitalize(), line.split("=")[-1] ) ) break diff --git a/scripts/r.in.wms/wms_base.py b/scripts/r.in.wms/wms_base.py index a14e15dc786..234456f1518 100644 --- a/scripts/r.in.wms/wms_base.py +++ b/scripts/r.in.wms/wms_base.py @@ -78,10 +78,8 @@ def _initializeParameters(self, options, flags): self.params["password"] == "" and self.params["username"] ): gs.fatal( - _( - "Please insert both %s and %s parameters or none of them." - % ("password", "username") - ) + _("Please insert both %s and %s parameters or none of them.") + % ("password", "username") ) self.params["bgcolor"] = options["bgcolor"].strip() @@ -202,8 +200,8 @@ def _checkIgnoeredParams(self, options, flags, driver_props): _( "These parameter are ignored: %s\n\ %s driver does not support the parameters." - % (",".join(not_relevant_params), options["driver"]) ) + % (",".join(not_relevant_params), options["driver"]) ) not_relevant_flags = [] @@ -216,8 +214,8 @@ def _checkIgnoeredParams(self, options, flags, driver_props): _( "These flags are ignored: %s\n\ %s driver does not support the flags." - % (",".join(not_relevant_flags), options["driver"]) ) + % (",".join(not_relevant_flags), options["driver"]) ) def GetMap(self, options, flags): @@ -308,7 +306,7 @@ def GetCapabilities(self, options): Path(capfile_output).write_text(cap) return except OSError as error: - gs.fatal(_("Unable to open file '%s'.\n%s\n" % (capfile_output, error))) + gs.fatal(_("Unable to open file '%s'.\n%s\n") % (capfile_output, error)) # print to output print(cap) diff --git a/scripts/r.in.wms/wms_cap_parsers.py b/scripts/r.in.wms/wms_cap_parsers.py index 5483be0979f..56b1d0ce48c 100644 --- a/scripts/r.in.wms/wms_cap_parsers.py +++ b/scripts/r.in.wms/wms_cap_parsers.py @@ -45,7 +45,7 @@ def __init__(self, cap_file): raise ParseError(_("Unable to parse XML file")) except OSError as error: raise ParseError( - _("Unable to open XML file '%s'.\n%s\n" % (cap_file, error)) + _("Unable to open XML file '%s'.\n%s\n") % (cap_file, error) ) else: try: diff --git a/scripts/r.in.wms/wms_drv.py b/scripts/r.in.wms/wms_drv.py index 1e7a90e9938..186157ad6db 100644 --- a/scripts/r.in.wms/wms_drv.py +++ b/scripts/r.in.wms/wms_drv.py @@ -286,7 +286,7 @@ def _pct2rgb(self, src_filename, dst_filename): # open source file src_ds = gdal.Open(src_filename) if src_ds is None: - gs.fatal(_("Unable to open %s " % src_filename)) + gs.fatal(_("Unable to open %s ") % src_filename) src_band = src_ds.GetRasterBand(band_number) diff --git a/scripts/r.in.wms/wms_gdal_drv.py b/scripts/r.in.wms/wms_gdal_drv.py index d17bac11eee..69a49ca5429 100644 --- a/scripts/r.in.wms/wms_gdal_drv.py +++ b/scripts/r.in.wms/wms_gdal_drv.py @@ -172,7 +172,7 @@ def _download(self): driver = gdal.GetDriverByName(self.gdal_drv_format) if driver is None: - gs.fatal(_("Unable to find %s driver" % format)) + gs.fatal(_("Unable to find %s driver") % self.gdal_drv_format) metadata = driver.GetMetadata() if ( diff --git a/scripts/r.pack/r.pack.py b/scripts/r.pack/r.pack.py index cd7d6c39282..eb11547322c 100644 --- a/scripts/r.pack/r.pack.py +++ b/scripts/r.pack/r.pack.py @@ -180,7 +180,7 @@ def main(): os.chdir(olddir) - grass.verbose(_("Raster map saved to '%s'" % outfile)) + grass.verbose(_("Raster map saved to '%s'") % outfile) if __name__ == "__main__": diff --git a/scripts/r.tileset/r.tileset.py b/scripts/r.tileset/r.tileset.py index f5e7ab17b13..c3fba2c173e 100644 --- a/scripts/r.tileset/r.tileset.py +++ b/scripts/r.tileset/r.tileset.py @@ -246,16 +246,16 @@ def main(): _( "It is not possible to set 'maxcols=%s' and " "'overlap=%s'. Please set maxcols>overlap" - % (options["maxcols"], options["overlap"]) ) + % (options["maxcols"], options["overlap"]) ) elif max_rows == 0: gcore.fatal( _( "It is not possible to set 'maxrows=%s' and " "'overlap=%s'. Please set maxrows>overlap" - % (options["maxrows"], options["overlap"]) ) + % (options["maxrows"], options["overlap"]) ) # destination projection if not options["destproj"]: @@ -397,7 +397,7 @@ def main(): if errors_dest > 0: gcore.warning( - _("During computation %i tiles could not be created" % errors_dest) + _("During computation %i tiles could not be created") % errors_dest ) while xi < ximax: diff --git a/scripts/r.unpack/r.unpack.py b/scripts/r.unpack/r.unpack.py index 053ac96988b..575d465ebb5 100644 --- a/scripts/r.unpack/r.unpack.py +++ b/scripts/r.unpack/r.unpack.py @@ -63,7 +63,7 @@ def main(): grass.debug("tmp_dir = {tmpdir}".format(tmpdir=tmp_dir)) if not os.path.exists(infile): - grass.fatal(_("File {name} not found.".format(name=infile))) + grass.fatal(_("File {name} not found.").format(name=infile)) gisenv = grass.gisenv() mset_dir = os.path.join( @@ -87,7 +87,7 @@ def main(): f = tar.extractfile("{}/{}".format(data_names[0], fname)) sys.stdout.write(f.read().decode()) except KeyError: - grass.fatal(_("Pack file unreadable: file '{}' missing".format(fname))) + grass.fatal(_("Pack file unreadable: file '{}' missing").format(fname)) tar.close() return 0 @@ -100,13 +100,11 @@ def main(): gfile = grass.find_file(name=map_name, element="cell", mapset=".") if gfile["file"]: if os.environ.get("GRASS_OVERWRITE", "0") != "1": - grass.fatal(_("Raster map <{name}> already exists".format(name=map_name))) + grass.fatal(_("Raster map <{name}> already exists").format(name=map_name)) else: grass.warning( - _( - "Raster map <{name}> already exists and will be overwritten".format( - name=map_name - ) + _("Raster map <{name}> already exists and will be overwritten").format( + name=map_name ) ) @@ -132,9 +130,10 @@ def main(): grass.fatal( _( "This GRASS GIS pack file contains vector data. Use " - "v.unpack to unpack <{name}>".format(name=map_name) - ) + "v.unpack to unpack <{name}>" + ).format(name=map_name) ) + else: grass.fatal(_("Pack file unreadable")) @@ -246,7 +245,7 @@ def main(): files = "\n".join(maps) Path(vrt_file).write_text(files) - grass.message(_("Raster map <{name}> unpacked".format(name=map_name))) + grass.message(_("Raster map <{name}> unpacked").format(name=map_name)) if __name__ == "__main__": diff --git a/scripts/v.rast.stats/v.rast.stats.py b/scripts/v.rast.stats/v.rast.stats.py index 1e1451f6b84..803fc8691ad 100644 --- a/scripts/v.rast.stats/v.rast.stats.py +++ b/scripts/v.rast.stats/v.rast.stats.py @@ -143,10 +143,8 @@ def main(): gs.fatal( _( "Number of raster maps ({0}) different from \ - number of column prefixes ({1})".format( - len(rasters), len(colprefixes) - ) - ) + number of column prefixes ({1})" + ).format(len(rasters), len(colprefixes)) ) vector = vs[0] @@ -340,10 +338,8 @@ def get_nr_of_categories( gs.warning( _( "Not all vector categories converted to raster. \ - Converted {0} of {1}.".format( - number, vect_cats_n - ) - ) + Converted {0} of {1}." + ).format(number, vect_cats_n) ) return number diff --git a/scripts/v.to.lines/v.to.lines.py b/scripts/v.to.lines/v.to.lines.py index bfcb2e45bc2..0630ad86401 100644 --- a/scripts/v.to.lines/v.to.lines.py +++ b/scripts/v.to.lines/v.to.lines.py @@ -189,7 +189,7 @@ def main(): gs.fatal(_("Error removing table from layer 1")) # TODO: when this except is happaning, it seems that never, so it seems wrong except Exception: - gs.warning(_("No table for layer %d" % 1)) + gs.warning(_("No table for layer %d") % 1) try: gs.run_command( "v.category", diff --git a/scripts/v.unpack/v.unpack.py b/scripts/v.unpack/v.unpack.py index 0507e9eb400..419e133b195 100644 --- a/scripts/v.unpack/v.unpack.py +++ b/scripts/v.unpack/v.unpack.py @@ -87,7 +87,7 @@ def main(): f = tar.extractfile(fname) sys.stdout.write(f.read().decode()) except KeyError: - grass.fatal(_("Pack file unreadable: file '{}' missing".format(fname))) + grass.fatal(_("Pack file unreadable: file '{}' missing").format(fname)) tar.close() return 0 @@ -139,8 +139,9 @@ def main(): grass.fatal( _( "This GRASS GIS pack file contains raster data. Use " - "r.unpack to unpack <%s>" % map_name + "r.unpack to unpack <%s>" ) + % map_name ) else: grass.fatal(_("Pack file unreadable")) diff --git a/scripts/v.what.strds/v.what.strds.py b/scripts/v.what.strds/v.what.strds.py index 2bd6cd72131..0e1bf893f98 100644 --- a/scripts/v.what.strds/v.what.strds.py +++ b/scripts/v.what.strds/v.what.strds.py @@ -169,13 +169,13 @@ def main(): "datasets must be equal\n<%(a)s> of type " "%(type_a)s do not match <%(b)s> of type " "%(type_b)s" - % { - "a": first_strds.get_id(), - "type_a": first_strds.get_temporal_type(), - "b": dataset.get_id(), - "type_b": dataset.get_temporal_type(), - } ) + % { + "a": first_strds.get_id(), + "type_a": first_strds.get_temporal_type(), + "b": dataset.get_id(), + "type_b": dataset.get_temporal_type(), + } ) mapmatrizes = tgis.sample_stds_by_stds_topology( @@ -227,7 +227,7 @@ def main(): pymap.open("r") except: dbif.close() - gs.fatal(_("Unable to create vector map <%s>" % output)) + gs.fatal(_("Unable to create vector map <%s>") % output) if len(pymap.dblinks) == 0: try: @@ -235,7 +235,7 @@ def main(): gs.run_command("v.db.addtable", map=output) except CalledModuleError: dbif.close() - gs.fatal(_("Unable to add table <%s> to vector map <%s>" % output)) + gs.fatal(_("Unable to add table <%s> to vector map <%s>") % output) if pymap.is_open(): pymap.close() diff --git a/temporal/t.rast.accdetect/t.rast.accdetect.py b/temporal/t.rast.accdetect/t.rast.accdetect.py index 887616f7a1c..1588b44d4b9 100644 --- a/temporal/t.rast.accdetect/t.rast.accdetect.py +++ b/temporal/t.rast.accdetect/t.rast.accdetect.py @@ -341,7 +341,7 @@ def main(): if len(input_maps) == 0: continue - gs.message(_("Processing cycle %s - %s" % (str(start), str(end)))) + gs.message(_("Processing cycle %s - %s") % (str(start), str(end))) count = compute_occurrence( occurrence_maps, diff --git a/temporal/t.rast.accumulate/t.rast.accumulate.py b/temporal/t.rast.accumulate/t.rast.accumulate.py index 94344f2d9cc..6fe067e02bc 100644 --- a/temporal/t.rast.accumulate/t.rast.accumulate.py +++ b/temporal/t.rast.accumulate/t.rast.accumulate.py @@ -323,7 +323,7 @@ def main(): where = "start_time >= '%s' AND start_time < '%s'" % (str(start), str(end)) input_maps = input_strds.get_registered_maps_as_objects(where=where, dbif=dbif) - gs.message(_("Processing cycle %s - %s" % (str(start), str(end)))) + gs.message(_("Processing cycle %s - %s") % (str(start), str(end))) if len(input_maps) == 0: continue diff --git a/temporal/t.rast.export/t.rast.export.py b/temporal/t.rast.export/t.rast.export.py index 675040bf5aa..fdf19541ee4 100755 --- a/temporal/t.rast.export/t.rast.export.py +++ b/temporal/t.rast.export/t.rast.export.py @@ -127,10 +127,10 @@ def main(): } if not directory or not os.path.exists(directory): - gs.fatal(_("Directory {} not found".format(directory))) + gs.fatal(_("Directory {} not found").format(directory)) if not os.access(directory, os.W_OK): - gs.fatal(_("Directory {} is not writable".format(directory))) + gs.fatal(_("Directory {} is not writable").format(directory)) if _type and _format in {"pack", "AAIGrid"}: gs.warning( diff --git a/temporal/t.rast.gapfill/t.rast.gapfill.py b/temporal/t.rast.gapfill/t.rast.gapfill.py index e2ffb149513..84395da6295 100755 --- a/temporal/t.rast.gapfill/t.rast.gapfill.py +++ b/temporal/t.rast.gapfill/t.rast.gapfill.py @@ -208,8 +208,9 @@ def main(): gs.fatal( _( "Map with name <%s> already exists. " - "Please use another base name." % (_id) + "Please use another base name." ) + % (_id) ) elif new_map.is_in_db(dbif): overwrite_flags[new_id] = True diff --git a/temporal/t.rast.list/t.rast.list.py b/temporal/t.rast.list/t.rast.list.py index 651bc69b956..0209d0aeee6 100755 --- a/temporal/t.rast.list/t.rast.list.py +++ b/temporal/t.rast.list/t.rast.list.py @@ -108,7 +108,13 @@ def message_option_value_excludes_option_value( return _( "Combining {option_name}={option_value} and " "{excluded_option_name}={excluded_option_value} is not allowed. {reason}" - ).format(**locals()) + ).format( + option_name=option_name, + option_value=option_value, + excluded_option_name=excluded_option_name, + excluded_option_value=excluded_option_value, + reason=reason, + ) def message_option_value_excludes_option( @@ -117,13 +123,23 @@ def message_option_value_excludes_option( return _( "The option {excluded_option_name} is not allowed with " "{option_name}={option_value}. {reason}" - ).format(**locals()) + ).format( + excluded_option_name=excluded_option_name, + option_name=option_name, + option_value=option_value, + reason=reason, + ) def message_option_value_excludes_flag(option_name, option_value, flag_name, reason): return _( "The flag -{flag_name} is not allowed with {option_name}={option_value}." - " {reason}".format(**locals()) + " {reason}" + ).format( + flag_name=flag_name, + option_name=option_name, + option_value=option_value, + reason=reason, ) diff --git a/temporal/t.rast.out.vtk/t.rast.out.vtk.py b/temporal/t.rast.out.vtk/t.rast.out.vtk.py index a042168499a..876770e4c4e 100755 --- a/temporal/t.rast.out.vtk/t.rast.out.vtk.py +++ b/temporal/t.rast.out.vtk/t.rast.out.vtk.py @@ -160,7 +160,7 @@ def main(): overwrite=gs.overwrite(), ) except CalledModuleError: - gs.fatal(_("Unable to export raster map <%s>" % map_name)) + gs.fatal(_("Unable to export raster map <%s>") % map_name) count += 1 diff --git a/temporal/t.rast.series/t.rast.series.py b/temporal/t.rast.series/t.rast.series.py index 074f35a00a0..57907f463e4 100755 --- a/temporal/t.rast.series/t.rast.series.py +++ b/temporal/t.rast.series/t.rast.series.py @@ -149,8 +149,8 @@ def main(): gs.warning( _( "Processing over {} maps: activating -z flag of r.series which " - "slows down processing.".format(max_files_open) - ) + "slows down processing." + ).format(max_files_open) ) flag += "z" if nulls: diff --git a/temporal/t.rast.to.rast3/t.rast.to.rast3.py b/temporal/t.rast.to.rast3/t.rast.to.rast3.py index e3bdd9567ad..7776d7426ee 100755 --- a/temporal/t.rast.to.rast3/t.rast.to.rast3.py +++ b/temporal/t.rast.to.rast3/t.rast.to.rast3.py @@ -162,7 +162,7 @@ def main(): overwrite=gs.overwrite(), ) except CalledModuleError: - gs.fatal(_("Unable to create 3D raster map <%s>" % output)) + gs.fatal(_("Unable to create 3D raster map <%s>") % output) gs.run_command("g.remove", flags="f", type="raster", name=null_map) diff --git a/temporal/t.rast.what/t.rast.what.py b/temporal/t.rast.what/t.rast.what.py index 6a4efbb2b94..39e63180abf 100755 --- a/temporal/t.rast.what/t.rast.what.py +++ b/temporal/t.rast.what/t.rast.what.py @@ -387,7 +387,7 @@ def one_point_per_row_output( for count in range(len(output_files)): file_name = output_files[count] - gs.verbose(_("Transforming r.what output file %s" % (file_name))) + gs.verbose(_("Transforming r.what output file %s") % (file_name)) map_list = output_time_list[count] in_file = open(file_name, "r") for line in in_file: @@ -465,7 +465,7 @@ def one_point_per_col_output( first = True for count in range(len(output_files)): file_name = output_files[count] - gs.verbose(_("Transforming r.what output file %s" % (file_name))) + gs.verbose(_("Transforming r.what output file %s") % (file_name)) in_file = open(file_name, "r") lines = in_file.readlines() @@ -607,7 +607,7 @@ def one_point_per_timerow_output( if write_header: out_file.write(header + "\n") - gs.verbose(_("Writing the output file <%s>" % (output))) + gs.verbose(_("Writing the output file <%s>") % (output)) for row in matrix: first = True for col in row: @@ -663,15 +663,13 @@ def process_loop( output_time_list.append(map_list) gs.verbose( - _( - "Process maps %(samp_start)i to %(samp_end)i (of %(total)i)" - % ( - { - "samp_start": count - len(map_names) + 1, - "samp_end": count, - "total": len(maps), - } - ) + _("Process maps %(samp_start)i to %(samp_end)i (of %(total)i)") + % ( + { + "samp_start": count - len(map_names) + 1, + "samp_end": count, + "total": len(maps), + } ) ) mod = copy.deepcopy(r_what) diff --git a/temporal/t.remove/t.remove.py b/temporal/t.remove/t.remove.py index fd32f8972d5..b5c1164cef4 100755 --- a/temporal/t.remove/t.remove.py +++ b/temporal/t.remove/t.remove.py @@ -127,22 +127,25 @@ def main(): sp = tgis.open_old_stds(name, type, dbif) if not force: gs.message( - _("{stds}: {gid}".format(stds=sp.get_type().upper(), gid=sp.get_id())) + _("{stds}: {gid}").format(stds=sp.get_type().upper(), gid=sp.get_id()) ) if recursive or clean: if not force: if recursive: - msg = ( + msg = _( "The following maps of {stds} {gid} will be " "unregistered from temporal database:" ) elif clean: - msg = ( + msg = _( "The following maps of {stds} {gid} will be " "unregistered from temporal database and removed " "from spatial database:" ) - gs.message(_(msg.format(stds=sp.get_type(), gid=sp.get_id()))) + + if recursive or clean: + gs.message(msg.format(stds=sp.get_type(), gid=sp.get_id())) + maps = sp.get_registered_maps_as_objects(dbif=dbif) map_statement = "" count = 1 @@ -150,10 +153,10 @@ def main(): for map in maps: map.select(dbif) # We may have multiple layer for a single map, hence we need - # to avoid multiple deletation of the same map, + # to avoid multiple deletions of the same map, # but the database entries are still present and must be removed if not force: - gs.message(_("- %s" % map.get_name())) + gs.message(_("- %s") % map.get_name()) continue if clean and force: if map.get_name() not in name_list: @@ -190,8 +193,8 @@ def main(): gs.message( _( "Nothing removed. You must use the force flag (-{flag}) to actually " - "remove them.".format(flag="f") - ) + "remove them." + ).format(flag="f") ) else: # Execute the collected SQL statenents diff --git a/temporal/t.unregister/t.unregister.py b/temporal/t.unregister/t.unregister.py index e7e234320c0..7aef44ed12b 100755 --- a/temporal/t.unregister/t.unregister.py +++ b/temporal/t.unregister/t.unregister.py @@ -154,10 +154,8 @@ def main(): statement += map.delete(dbif=dbif, update=False, execute=False) else: gs.warning( - _( - "Unable to find %s map <%s> in temporal database" - % (map.get_type(), map.get_id()) - ) + _("Unable to find %s map <%s> in temporal database") + % (map.get_type(), map.get_id()) ) count += 1 @@ -170,7 +168,7 @@ def main(): # Update space time datasets if input: - gs.message(_("Unregister maps from space time dataset <%s>" % (input))) + gs.message(_("Unregister maps from space time dataset <%s>") % (input)) else: gs.message(_("Unregister maps from the temporal database")) diff --git a/temporal/t.vect.observe.strds/t.vect.observe.strds.py b/temporal/t.vect.observe.strds/t.vect.observe.strds.py index 8b3850fb2ef..9c60a3d026c 100755 --- a/temporal/t.vect.observe.strds/t.vect.observe.strds.py +++ b/temporal/t.vect.observe.strds/t.vect.observe.strds.py @@ -152,13 +152,13 @@ def main(): "Temporal type of space time raster datasets must be equal\n" "<%(a)s> of type %(type_a)s do not match <%(b)s> of type " "%(type_b)s" - % { - "a": first_strds.get_id(), - "type_a": first_strds.get_temporal_type(), - "b": dataset.get_id(), - "type_b": dataset.get_temporal_type(), - } ) + % { + "a": first_strds.get_id(), + "type_a": first_strds.get_temporal_type(), + "b": dataset.get_id(), + "type_b": dataset.get_temporal_type(), + } ) mapmatrizes = tgis.sample_stds_by_stds_topology( diff --git a/utils/mkhtml.py b/utils/mkhtml.py index 210e83b3fd6..d36ea78663d 100644 --- a/utils/mkhtml.py +++ b/utils/mkhtml.py @@ -164,12 +164,11 @@ def download_git_commit(url, response_format, *args, **kwargs): desc = HTTP_STATUS_CODES[index].description gs.fatal( _( - "Download commit from <{url}>, return status code " - "{code}, {desc}".format( - url=url, - code=response.code, - desc=desc, - ), + "Download commit from <{url}>, return status code {code}, {desc}" + ).format( + url=url, + code=response.code, + desc=desc, ), ) if response_format not in response.getheader("Content-Type"): @@ -177,10 +176,10 @@ def download_git_commit(url, response_format, *args, **kwargs): _( "Wrong downloaded commit file format. " "Check url <{url}>. Allowed file format is " - "{response_format}.".format( - url=url, - response_format=response_format, - ), + "{response_format}." + ).format( + url=url, + response_format=response_format, ), ) return response @@ -190,16 +189,16 @@ def download_git_commit(url, response_format, *args, **kwargs): "The download of the commit from the GitHub API " "server wasn't successful, <{}>. Commit and commit " "date will not be included in the <{}> addon html manual " - "page.".format(err.msg, pgm) - ), + "page." + ).format(err.msg, pgm), ) except URLError: gs.warning( _( "Download file from <{url}>, failed. Check internet " "connection. Commit and commit date will not be included " - "in the <{pgm}> addon manual page.".format(url=url, pgm=pgm) - ), + "in the <{pgm}> addon manual page." + ).format(url=url, pgm=pgm), ) From 1a893014aa15e616715828f0d97306e7bf847d16 Mon Sep 17 00:00:00 2001 From: Mohan Yelugoti Date: Sun, 15 Sep 2024 14:25:29 -0400 Subject: [PATCH 222/471] v.in.dwg: Avoid using same variable as parameter and destination in sprintf (#4262) v.in.dwg: Avoid using same variable as parameter and dest in sprintf Currently, one instance of sprintf has the same variable as parameter and destination in sprintf. This scenario leads to undefined behavior in C. Modify the code to: 1. Write initial error string using snprintf() onto the buffer. Using snprintf() makes sure that we stay within the buffer size and avoid overflow errors. 2. Use snprintf() again to write another error string at the end of previous error string in the same buffer. We again use snprintf() to make sure we are not overflowing the buffer with data. Signed-off-by: Mohan Yelugoti --- vector/v.in.dwg/main.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/vector/v.in.dwg/main.c b/vector/v.in.dwg/main.c index e21602ecb90..cc7b9a217b1 100644 --- a/vector/v.in.dwg/main.c +++ b/vector/v.in.dwg/main.c @@ -66,7 +66,8 @@ int main(int argc, char *argv[]) struct GModule *module; struct Option *out_opt, *in_opt; struct Flag *z_flag, *circle_flag, *l_flag, *int_flag; - char buf[2000]; + const size_t BUFSIZE = 2000; + char buf[BUFSIZE]; /* DWG */ char path[2000]; @@ -135,10 +136,13 @@ int main(int argc, char *argv[]) /* Init OpenDWG */ sprintf(path, "%s/etc/adinit.dat", G_gisbase()); if (!adInitAd2(path, &initerror)) { - sprintf(buf, _("Unable to initialize OpenDWG Toolkit, error: %d: %s."), - initerror, adErrorStr(initerror)); + snprintf(buf, BUFSIZE, + _("Unable to initialize OpenDWG Toolkit, error: %d: %s."), + initerror, adErrorStr(initerror)); + size_t buflen = strlen(buf); if (initerror == AD_UNABLE_TO_OPEN_INIT_FILE) - sprintf(buf, _("%s Cannot open %s"), buf, path); + snprintf(buf + buflen, BUFSIZE - buflen, _(" Cannot open %s"), + path); G_fatal_error(buf); } adSetupDwgRead(); From 213f024ac4fc6275ad565688c7706a611c8ce56a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Mon, 16 Sep 2024 02:13:26 -0400 Subject: [PATCH 223/471] CI(macOS): Use micromamba-shell for steps to load PATH changes (#4325) Fixes problems with loading environment variables and path on the newest GitHub Actions macOS 20240911.3 runner image, that makes all builds fail. * CI(macOS): Use micromamba-shell for build and install * CI(macOS): Use micromamba-shell for printing versions * CI(macOS): Use micromamba-shell for pytest and gunittest --- .github/workflows/macos.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index eb99b6ff7a7..b74b2b042c9 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -59,17 +59,17 @@ jobs: - name: Create installation directory run: mkdir $HOME/install - name: Build and install - shell: bash -l {0} + shell: micromamba-shell {0} run: source ./.github/workflows/macos_install.sh $HOME/install - name: Add the bin directory to PATH run: echo "$HOME/install/bin" >> $GITHUB_PATH - name: Check installed version if: ${{ !cancelled() }} - shell: bash -l {0} + shell: micromamba-shell {0} run: source ./.github/workflows/print_versions.sh - name: Run pytest with multiple workers in parallel - shell: bash -el {0} + shell: micromamba-shell {0} run: | export PYTHONPATH=$(grass --config python_path):$PYTHONPATH export LD_LIBRARY_PATH=$(grass --config path)/lib:$LD_LIBRARY_PATH @@ -78,7 +78,7 @@ jobs: -ra . \ -m 'not needs_solo_run' - name: Run pytest with a single worker (for tests marked with needs_solo_run) - shell: bash -el {0} + shell: micromamba-shell {0} run: | export PYTHONPATH=$(grass --config python_path):$PYTHONPATH export LD_LIBRARY_PATH=$(grass --config path)/lib:$LD_LIBRARY_PATH @@ -87,7 +87,7 @@ jobs: -m 'needs_solo_run' - name: Run gunittest tests - shell: bash -el {0} + shell: micromamba-shell {0} run: | grass --tmp-project XY --exec \ g.download.project url=${{ env.SampleData }} path=$HOME From 63a80f5c198a48ecf741a9bb97d6d37106651f12 Mon Sep 17 00:00:00 2001 From: Mohan Yelugoti Date: Mon, 16 Sep 2024 02:22:55 -0400 Subject: [PATCH 224/471] lib/linkm: Change non-returning internal function types to void (#4318) --- lib/linkm/test/try.c | 6 +++--- lib/linkm/test/try2.c | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/linkm/test/try.c b/lib/linkm/test/try.c index 4f2a431e003..3b71b6f684a 100644 --- a/lib/linkm/test/try.c +++ b/lib/linkm/test/try.c @@ -61,7 +61,7 @@ int main(int argc, char *argv[]) exit(0); } -int add_link_rev(struct link *List, struct link *link) +void add_link_rev(struct link *List, struct link *link) { struct link *p; @@ -70,7 +70,7 @@ int add_link_rev(struct link *List, struct link *link) link->next = p; } -int add_link(struct link *List, struct link *link) +void add_link(struct link *List, struct link *link) { struct link *p; @@ -81,7 +81,7 @@ int add_link(struct link *List, struct link *link) link->next = NULL; } -int dumplist(struct link *List) +void dumplist(struct link *List) { struct link *p; diff --git a/lib/linkm/test/try2.c b/lib/linkm/test/try2.c index ad226826795..7eb153f16f3 100644 --- a/lib/linkm/test/try2.c +++ b/lib/linkm/test/try2.c @@ -63,7 +63,7 @@ int main(int argc, char *argv[]) exit(0); } -int add_link_rev(struct link *List, struct link *link) +void add_link_rev(struct link *List, struct link *link) { struct link *p; @@ -72,7 +72,7 @@ int add_link_rev(struct link *List, struct link *link) link->next = p; } -int add_link(struct link *List, struct link *link) +void add_link(struct link *List, struct link *link) { struct link *p; @@ -83,7 +83,7 @@ int add_link(struct link *List, struct link *link) link->next = NULL; } -int dumplist(struct link *List) +void dumplist(struct link *List) { struct link *p; From d0f1634cfe08ed6adda7ce2513d363898bf5b347 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Mon, 16 Sep 2024 03:55:03 -0400 Subject: [PATCH 225/471] CI(OSGeo4W): Add -pipe to CFLAGS and CXXFLAGS to reduce build time (#4326) With the current GitHub Hosted runners, the build step time is reduced by about 2 minutes, down to about 8min30 instead of about 10min30, for the couple builds I observed --- .github/workflows/osgeo4w.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/osgeo4w.yml b/.github/workflows/osgeo4w.yml index 2f17427b05d..2b92c254238 100644 --- a/.github/workflows/osgeo4w.yml +++ b/.github/workflows/osgeo4w.yml @@ -66,7 +66,10 @@ jobs: - name: Compile GRASS GIS shell: msys2 {0} - run: .github/workflows/build_osgeo4w.sh + run: | + export CFLAGS="${CFLAGS} -pipe" + export CXXFLAGS="${CXXFLAGS} -pipe" + .github/workflows/build_osgeo4w.sh - name: Print installed versions if: always() From 3dbe2d7f3ae980c1aefa5c376c11c58ad3724ea5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 11:48:39 +0000 Subject: [PATCH 226/471] CI(deps): Update peter-evans/create-pull-request action to v7.0.3 (#4327) --- .github/workflows/periodic_update.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/periodic_update.yml b/.github/workflows/periodic_update.yml index ce03bf8e281..7b8c08b5b70 100644 --- a/.github/workflows/periodic_update.yml +++ b/.github/workflows/periodic_update.yml @@ -33,7 +33,7 @@ jobs: run: git status --ignored - name: Create Pull Request id: cpr - uses: peter-evans/create-pull-request@d121e62763d8cc35b5fb1710e887d6e69a52d3a4 # v7.0.2 + uses: peter-evans/create-pull-request@6cd32fd93684475c31847837f87bb135d40a2b79 # v7.0.3 with: commit-message: "config.guess + config.sub: updated from http://git.savannah.gnu.org/cgit/config.git/plain/" branch: periodic/update-configure From 560340a9c01414469996b3f94d60406d1297e2c5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 09:33:53 -0400 Subject: [PATCH 227/471] CI(deps): Update ruff to v0.6.5 (#4315) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * CI(deps): Update ruff to v0.6.5 * style: Fix FURB188: Prefer `removeprefix` over conditionally replacing with slice. Ruff rule: https://docs.astral.sh/ruff/rules/slice-to-remove-prefix-or-suffix/ This is a new rule introduced in ruff 0.6.5 --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Edouard Choinière <27212526+echoix@users.noreply.github.com> --- .github/workflows/python-code-quality.yml | 2 +- .pre-commit-config.yaml | 2 +- man/parser_standard_options.py | 3 +-- python/grass/gunittest/reporters.py | 6 ++---- utils/generate_release_notes.py | 4 +--- 5 files changed, 6 insertions(+), 11 deletions(-) diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index f5810fbe7c2..cd2624051a5 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -36,7 +36,7 @@ jobs: # renovate: datasource=pypi depName=bandit BANDIT_VERSION: "1.7.9" # renovate: datasource=pypi depName=ruff - RUFF_VERSION: "0.6.4" + RUFF_VERSION: "0.6.5" runs-on: ${{ matrix.os }} permissions: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a5221f29efe..3fdb0741681 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -37,7 +37,7 @@ repos: ) - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.6.4 + rev: v0.6.5 hooks: # Run the linter. - id: ruff diff --git a/man/parser_standard_options.py b/man/parser_standard_options.py index 15d1e0fc9ae..ad027a1b6b4 100644 --- a/man/parser_standard_options.py +++ b/man/parser_standard_options.py @@ -47,8 +47,7 @@ def split_opt_line(line): index = line.index("=") key = line[:index].strip() default = line[index + 1 :].strip() - if default.startswith("_("): - default = default[2:] + default = default.removeprefix("_(") return key, default def parse_glines(glines): diff --git a/python/grass/gunittest/reporters.py b/python/grass/gunittest/reporters.py index 0911ca0fdd7..ec175613bc3 100644 --- a/python/grass/gunittest/reporters.py +++ b/python/grass/gunittest/reporters.py @@ -172,8 +172,7 @@ def get_svn_revision(): rc = p.poll() if not rc: stdout = stdout.strip() - if stdout.endswith("M"): - stdout = stdout[:-1] + stdout = stdout.removesuffix("M") if ":" in stdout: # the first one is the one of source code stdout = stdout.split(":")[0] @@ -211,8 +210,7 @@ def get_svn_info(): if relurl is not None: relurl = relurl.text # relative path has ^ at the beginning in SVN version 1.8.8 - if relurl.startswith("^"): - relurl = relurl[1:] + relurl = relurl.removeprefix("^") else: # SVN version 1.8.8 supports relative-url but older do not # so, get relative part from absolute URL diff --git a/utils/generate_release_notes.py b/utils/generate_release_notes.py index 3cfffe77bd2..66aa4ced08c 100755 --- a/utils/generate_release_notes.py +++ b/utils/generate_release_notes.py @@ -97,9 +97,7 @@ def print_category(category, changes, file=None): # Relies on author being specified as username. if " " in author: author = author.split(" ", maxsplit=1)[0] - if author.startswith("@"): - # We expect that to be always the case, but we test anyway. - author = author[1:] + author = author.removeprefix("@") if author in known_bot_names or author.endswith("[bot]"): hidden.append(item) elif len(visible) > max_section_length: From d7c126f2d844c014b79cb94bda55e97984e6445f Mon Sep 17 00:00:00 2001 From: Markus Neteler Date: Mon, 16 Sep 2024 16:05:15 +0200 Subject: [PATCH 228/471] docs: add README.md to doc/development/rfc/ (#4316) --- doc/development/rfc/README.md | 26 ++++++++++++++++++++++++ doc/development/rfc/version_numbering.md | 2 +- 2 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 doc/development/rfc/README.md diff --git a/doc/development/rfc/README.md b/doc/development/rfc/README.md new file mode 100644 index 00000000000..590be34e169 --- /dev/null +++ b/doc/development/rfc/README.md @@ -0,0 +1,26 @@ +# Request For Comment (RFC) List + +GRASS Project Steering Committee: + +- [GRASS-PSC](https://grasswiki.osgeo.org/wiki/PSC) + +A list of all GRASS GIS RFC documents, with status: + +- [RFC 1: Project Steering Committee Guidelines](PSC_guidelines.md) (Adopted) +- [RFC 2: Legal aspects of code contributions](legal_aspects_of_code_contributions.md) + (Adopted) +- [RFC 3: PSC Voting Procedures](PSC_voting_procedures.md) (Adopted) +- [RFC 4: Release Procedure](https://github.com/OSGeo/grass/pull/2815) (Draft) +- [RFC 5: GRASS GIS Errata](https://github.com/OSGeo/grass/pull/2813) (Draft) +- [RFC 6: Migration from SVN to GitHub](migration_github.md) (Adopted) +- [RFC 7: Language Standards Support](language_standards_support.md) (Adopted) +- [RFC 8: Python Language Support](python_language_support.md) (Adopted) +- [RFC 9: Version Numbering](version_numbering.md) (Adopted) +- [RFC X: Release Policy](https://github.com/OSGeo/grass/pull/3673) (Draft) + +Status values: + +- Adopted: RFC is approved (and presumably implemented). +- Proposed: RFC is complete, and open for voting. +- Draft: RFC is being written/revised/discussed. +- Withdrawn: RFC is unapproved, and not being pursued further. diff --git a/doc/development/rfc/version_numbering.md b/doc/development/rfc/version_numbering.md index 7829b0eb513..3428a8b92aa 100644 --- a/doc/development/rfc/version_numbering.md +++ b/doc/development/rfc/version_numbering.md @@ -1,4 +1,4 @@ -# RFC: Version Numbering +# RFC 9: Version Numbering Author: Vaclav Petras From 7c2708027889e5c0f7f19c841839b3f9f568f7ea Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Mon, 16 Sep 2024 12:04:33 -0400 Subject: [PATCH 229/471] r.clump: Fix unchecked return value from lseek (#4151) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change addresses an issue identified by Coverity Scan (CID : 1415709) [unchecked return value from library]. The checks are added for lseek usages using an error message already used in the library. --------- Co-authored-by: Edouard Choinière <27212526+echoix@users.noreply.github.com> --- raster/r.clump/clump.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/raster/r.clump/clump.c b/raster/r.clump/clump.c index 052ad6ef034..cf94a5bf142 100644 --- a/raster/r.clump/clump.c +++ b/raster/r.clump/clump.c @@ -25,6 +25,8 @@ #include #include #include "local_proto.h" +#include +#include #define INCR 1024 @@ -92,7 +94,9 @@ static CELL do_renumber(int *in_fd, DCELL *rng, int nin, int diag, int minsize, G_percent(row, nrows, 2); coffset = (off_t)row * csize; - lseek(cfd, coffset, SEEK_SET); + if (lseek(cfd, coffset, SEEK_SET) == -1) { + G_fatal_error(_("Unable to seek: %s"), strerror(errno)); + } if (read(cfd, cur_clump, csize) != csize) G_fatal_error(_("Unable to read from temp file")); @@ -108,7 +112,9 @@ static CELL do_renumber(int *in_fd, DCELL *rng, int nin, int diag, int minsize, temp_clump++; } if (do_write) { - lseek(cfd, coffset, SEEK_SET); + if (lseek(cfd, coffset, SEEK_SET) == -1) { + G_fatal_error(_("Unable to seek: %s"), strerror(errno)); + } if (write(cfd, cur_clump, csize) != csize) G_fatal_error(_("Unable to write to temp file")); } From fdcd8d8223fed5c1e5b05c0579f111a0325bd5ca Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Mon, 16 Sep 2024 13:01:57 -0400 Subject: [PATCH 230/471] v.to.3d: Fix Resource Leak Issue in trans2.c (#4320) * Handling Resource Leak * Resource Leak --- vector/v.to.3d/trans2.c | 1 + 1 file changed, 1 insertion(+) diff --git a/vector/v.to.3d/trans2.c b/vector/v.to.3d/trans2.c index 1680d950117..c4bde9b3888 100644 --- a/vector/v.to.3d/trans2.c +++ b/vector/v.to.3d/trans2.c @@ -73,6 +73,7 @@ void trans2d(struct Map_info *In, struct Map_info *Out, int type, double height, G_debug(3, "%d records selected", cvarr.n_values); db_close_database_shutdown_driver(driver); + Vect_destroy_field_info(Fi); } G_message(_("Transforming features...")); From c840f3c43874fa8ebb52ea45ddb1c1a3fec5bf55 Mon Sep 17 00:00:00 2001 From: Mohan Yelugoti Date: Mon, 16 Sep 2024 13:02:43 -0400 Subject: [PATCH 231/471] r.viewshed: initialize struct member before using struct (#4232) * r.viewshed: initialize struct member before using struct This was reported by cppcheck tool. Technically, we are not using the member in the struct variable in any of the subsequent functions, but it's a good practice to initialize all the struct members whenever possible. Signed-off-by: Mohan Yelugoti * Use right literal while initializing angle Signed-off-by: Mohan Yelugoti --------- Signed-off-by: Mohan Yelugoti --- raster/r.viewshed/viewshed.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/raster/r.viewshed/viewshed.cpp b/raster/r.viewshed/viewshed.cpp index 17ef21b7811..3ce07fa4652 100644 --- a/raster/r.viewshed/viewshed.cpp +++ b/raster/r.viewshed/viewshed.cpp @@ -261,6 +261,7 @@ MemoryVisibilityGrid *viewshed_in_memory(char *inputfname, GridHeader *hd, e.elev[0] = data[0][i]; e.elev[1] = data[1][i]; e.elev[2] = data[2][i]; + e.angle = -1.0; if (!is_nodata(visgrid->grid->hd, data[1][i]) && !is_point_outside_max_dist(*vp, *hd, sn.row, sn.col, @@ -499,6 +500,7 @@ IOVisibilityGrid *viewshed_external(char *inputfname, GridHeader *hd, e.elev[0] = data[0][i]; e.elev[1] = data[1][i]; e.elev[2] = data[2][i]; + e.angle = -1.0; if (!is_nodata(visgrid->hd, data[1][i]) && !is_point_outside_max_dist(*vp, *hd, sn.row, sn.col, viewOptions.maxDist)) { From 95c123d4b8ce740cefa2fcbc23f649ba25454807 Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Mon, 16 Sep 2024 13:04:15 -0400 Subject: [PATCH 232/471] i.rectify: Fix copy into fixed size buffer issue in main.c of i.rectify module (#4299) * Copy into fix size buffer issue * removed variable Len * Update imagery/i.rectify/main.c Co-authored-by: Nicklas Larsson --------- Co-authored-by: Shubham Vasudeo Desai Co-authored-by: Shubham Vasudeo Desai Co-authored-by: Nicklas Larsson --- imagery/i.rectify/main.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/imagery/i.rectify/main.c b/imagery/i.rectify/main.c index 3bbe830e3cd..a740a07a0ae 100644 --- a/imagery/i.rectify/main.c +++ b/imagery/i.rectify/main.c @@ -26,6 +26,8 @@ #include #include "global.h" +#include + int seg_mb_img; func interpolate; @@ -151,8 +153,14 @@ int main(int argc, char *argv[]) interpolate = menu[method].method; G_strip(grp->answer); - strcpy(group.name, grp->answer); - strcpy(extension, ext->answer); + if (G_strlcpy(group.name, grp->answer, sizeof(group.name)) >= + sizeof(group.name)) { + G_fatal_error(_("Group name <%s> is too long"), grp->answer); + } + if (G_strlcpy(extension, ext->answer, sizeof(extension)) >= + sizeof(extension)) { + G_fatal_error(_("Extension <%s> is too long"), ext->answer); + } order = atoi(val->answer); seg_mb = NULL; From 826ab62d781b07ac456cf0056631f39882419859 Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Mon, 16 Sep 2024 20:18:31 -0400 Subject: [PATCH 233/471] i.segment: Move unused variable value into a code comment (#4153) * remove unused variable * Specify old ideas in the comment explicitly --------- Co-authored-by: Vaclav Petras --- imagery/i.segment/mean_shift.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/imagery/i.segment/mean_shift.c b/imagery/i.segment/mean_shift.c index 833ff5ca8a8..1d838335352 100644 --- a/imagery/i.segment/mean_shift.c +++ b/imagery/i.segment/mean_shift.c @@ -179,8 +179,9 @@ int mean_shift(struct globals *globals) hspec = globals->hr; if (hspec < 0 || hspec >= 1) { - hspec = sqrt(avgdiffavg / 10.0); - hspec = avgdiffavg; + // Other ideas how to compute this are: + // sqrt(avgdiffavg / 10.0) + // avgdiffavg (as is) hspec = mindiffzeroavg; if (do_progressive) From a0486dd3a7f3d00abf4541e96f814fda8de66f2d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 17 Sep 2024 07:35:21 -0400 Subject: [PATCH 234/471] CI(deps): Update ubuntu:22.04 Docker digest to 58b8789 (#4331) --- Dockerfile | 2 +- docker/ubuntu/Dockerfile | 2 +- docker/ubuntu_wxgui/Dockerfile | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index aba1df48a1d..c6366eeb873 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,7 +5,7 @@ ARG GUI=without -FROM ubuntu:22.04@sha256:adbb90115a21969d2fe6fa7f9af4253e16d45f8d4c1e930182610c4731962658 as common_start +FROM ubuntu:22.04@sha256:58b87898e82351c6cf9cf5b9f3c20257bb9e2dcf33af051e12ce532d7f94e3fe as common_start LABEL authors="Carmen Tawalika,Markus Neteler,Anika Weinmann,Stefan Blumentrath" LABEL maintainer="tawalika@mundialis.de,neteler@mundialis.de,weinmann@mundialis.de" diff --git a/docker/ubuntu/Dockerfile b/docker/ubuntu/Dockerfile index aba1df48a1d..c6366eeb873 100644 --- a/docker/ubuntu/Dockerfile +++ b/docker/ubuntu/Dockerfile @@ -5,7 +5,7 @@ ARG GUI=without -FROM ubuntu:22.04@sha256:adbb90115a21969d2fe6fa7f9af4253e16d45f8d4c1e930182610c4731962658 as common_start +FROM ubuntu:22.04@sha256:58b87898e82351c6cf9cf5b9f3c20257bb9e2dcf33af051e12ce532d7f94e3fe as common_start LABEL authors="Carmen Tawalika,Markus Neteler,Anika Weinmann,Stefan Blumentrath" LABEL maintainer="tawalika@mundialis.de,neteler@mundialis.de,weinmann@mundialis.de" diff --git a/docker/ubuntu_wxgui/Dockerfile b/docker/ubuntu_wxgui/Dockerfile index 21426eca886..36d33ffeb20 100644 --- a/docker/ubuntu_wxgui/Dockerfile +++ b/docker/ubuntu_wxgui/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:22.04@sha256:adbb90115a21969d2fe6fa7f9af4253e16d45f8d4c1e930182610c4731962658 +FROM ubuntu:22.04@sha256:58b87898e82351c6cf9cf5b9f3c20257bb9e2dcf33af051e12ce532d7f94e3fe LABEL authors="Carmen Tawalika,Markus Neteler,Anika Weinmann,Tomas Zigo" LABEL maintainer="tawalika@mundialis.de,neteler@mundialis.de,weinmann@mundialis.de" From 083fc212767839553dba185cde94c947a5827b9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Tue, 17 Sep 2024 10:22:44 -0400 Subject: [PATCH 235/471] style: Enable checking for SIM115 adding exclusions for existing issues (#4330) --- pyproject.toml | 77 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 72 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index f6d37ac3bea..f3ca9ab8dd6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -255,7 +255,6 @@ ignore = [ "SIM110", # reimplemented-builtin "SIM113", # enumerate-for-loop "SIM114", # if-with-same-arms - "SIM115", # open-file-with-context-handler "SIM116", # if-else-block-instead-of-dict-lookup "SIM118", # in-dict-keys "SIM201", # negate-equal-op @@ -284,41 +283,109 @@ ignore = [ # Ignore `E402` (import violations) in all `__init__.py` files "*/testsuite/**.py" = ["PT009", "PT027"] "__init__.py" = ["E402"] +"display/d.mon/render_cmd.py" = ["SIM115"] "gui/**" = ["PLW0108"] # See https://github.com/OSGeo/grass/issues/4124 +"gui/wxpython/animation/temporal_manager.py" = ["SIM115"] +"gui/wxpython/core/*.py" = ["SIM115"] +"gui/wxpython/dbmgr/base.py" = ["SIM115"] +"gui/wxpython/gcp/manager.py" = ["SIM115"] +"gui/wxpython/gmodeler/*.py" = ["SIM115"] +"gui/wxpython/gui_core/*.py" = ["SIM115"] +"gui/wxpython/iclass/frame*.py" = ["SIM115"] "gui/wxpython/iclass/frame.py" = ["FLY002"] "gui/wxpython/iclass/statistics.py" = ["A005"] +"gui/wxpython/image2target/*.py" = ["SIM115"] "gui/wxpython/iscatt/plots.py" = ["PLW0108"] +"gui/wxpython/lmgr/workspace.py" = ["SIM115"] +"gui/wxpython/location_wizard/wizard.py" = ["SIM115"] +"gui/wxpython/mapdisp/main.py" = ["SIM115"] +"gui/wxpython/modules/colorrules.py" = ["SIM115"] +"gui/wxpython/modules/mcalc_builder.py" = ["SIM115"] +"gui/wxpython/photo2image/*.py" = ["SIM115"] +"gui/wxpython/psmap/*.py" = ["SIM115"] "gui/wxpython/psmap/utils.py" = ["PGH004"] +"gui/wxpython/rdigit/controller.py" = ["SIM115"] +"gui/wxpython/rlisetup/*.py" = ["SIM115"] "gui/wxpython/timeline/frame.py" = ["FLY002"] +"gui/wxpython/tools/update_menudata.py" = ["SIM115"] "gui/wxpython/tplot/frame.py" = ["FLY002"] +"gui/wxpython/vdigit/mapwindow.py" = ["SIM115"] +"gui/wxpython/vnet/*.py" = ["SIM115"] +"gui/wxpython/web_services/dialogs.py" = ["SIM115"] +"gui/wxpython/wxplot/profile*.py" = ["SIM115"] "gui/wxpython/wxplot/profile.py" = ["A005"] +"imagery/i.atcorr/create_iwave.py" = ["SIM115"] +"lib/imagery/testsuite/test_imagery_signature_management.py" = ["SIM115"] "lib/imagery/testsuite/test_imagery_sigsetfile.py" = ["FURB152"] +"lib/init/grass.py" = ["SIM115"] +"locale/grass_po_stats.py" = ["SIM115"] +"man/build_*.py" = ["SIM115"] +"man/parser_standard_options.py" = ["SIM115"] "python/grass/__init__.py" = ["PYI056"] "python/grass/exp*/tests/grass_script_mapset_session_test.py" = ["SIM117"] "python/grass/exp*/tests/grass_script_tmp_mapset_session_test.py" = ["SIM117"] +"python/grass/gunittest/*.py" = ["SIM115"] "python/grass/gunittest/loader.py" = ["PYI024"] "python/grass/gunittest/multireport.py" = ["PYI024"] "python/grass/gunittest/testsu*/d*/s*/s*/subsub*/t*/test_segfaut.py" = ["B018"] "python/grass/gunittest/testsuite/test_assertions_rast3d.py" = ["FLY002"] +"python/grass/imaging/images2*.py" = ["SIM115"] "python/grass/jupyter/tests/reprojection_renderer_test.py" = ["PT013"] "python/grass/jupyter/testsuite/interactivemap_test.py" = ["PGH004"] "python/grass/jupyter/testsuite/map_test.py" = ["PGH004"] "python/grass/pydispatch/signal.py" = ["A005"] +"python/grass/pygrass/modules/grid/grid.py" = ["SIM115"] +"python/grass/pygrass/modules/interface/env.py" = ["SIM115"] +"python/grass/pygrass/raster/segment.py" = ["SIM115"] +"python/grass/pygrass/tests/*.py" = ["SIM115"] "python/grass/pygrass/vector/geometry.py" = ["PYI024"] "python/grass/pygrass/vector/sql.py" = ["FLY002"] "python/grass/pygrass/vector/testsuite/test_table.py" = ["PLW0108"] "python/grass/script/array.py" = ["A005"] +"python/grass/script/core.py" = ["SIM115"] +"python/grass/script/db.py" = ["SIM115"] +"python/grass/script/raster.py" = ["SIM115"] +"python/grass/script/utils.py" = ["SIM115"] +"python/grass/temporal/*.py" = ["SIM115"] +"python/grass/utils/download.py" = ["SIM115"] +"raster/r.*/testsuite/*.py" = ["SIM115"] +"raster/r.topidx/*.py" = ["SIM115"] "raster3d/r3.flow/testsuite/r3flow_test.py" = ["FLY002"] "raster3d/r3.gradient/testsuite/r3gradient_test.py" = ["FLY002"] -"scripts/d.polar/d.polar.py" = ["FURB154"] -"scripts/i.pansharpen/i.pansharpen.py" = ["FLY002"] -"scripts/i.spectral/i.spectral.py" = ["FLY002"] -"scripts/r.in.srtm/r.in.srtm.py" = ["FLY002"] +"scripts/d.correlate/d.correlate.py" = ["SIM115"] +"scripts/d.frame/d.frame.py" = ["SIM115"] +"scripts/d.polar/d.polar.py" = ["FURB154", "SIM115"] +"scripts/db.in.ogr/db.in.ogr.py" = ["SIM115"] +"scripts/db.test/db.test.py" = ["SIM115"] +"scripts/db.univar/db.univar.py" = ["SIM115"] +"scripts/g.extension.all/g.extension.all.py" = ["SIM115"] +"scripts/g.extension/g.extension.py" = ["SIM115"] +"scripts/g.search.modules/g.search.modules.py" = ["SIM115"] +"scripts/i.in.spotvgt/i.in.spotvgt.py" = ["SIM115"] +"scripts/i.oif/i.oif*.py" = ["SIM115"] +"scripts/i.pansharpen/i.pansharpen.py" = ["FLY002", "SIM115"] +"scripts/i.spectral/i.spectral.py" = ["FLY002", "SIM115"] +"scripts/m.proj/m.proj.py" = ["SIM115"] +"scripts/r.fillnulls/r.fillnulls.py" = ["SIM115"] +"scripts/r.in.srtm/r.in.srtm.py" = ["FLY002", "SIM115"] +"scripts/r.in.wms/wms_*.py" = ["SIM115"] +"scripts/r.tileset/r.tileset.py" = ["SIM115"] +"scripts/v.*/v.*.py" = ["SIM115"] +"scripts/wxpyimgview/wxpyimgview_gui.py" = ["SIM115"] +"temporal/t.list/t.list.py" = ["SIM115"] "temporal/t.rast.algebra/testsu*/*_algebra_arithmetic.py" = ["FLY002"] +"temporal/t.rast.colors/t.rast.colors.py" = ["SIM115"] +"temporal/t.rast.series/t.rast.series.py" = ["SIM115"] +"temporal/t.rast.univar/testsuite/test_t_rast_univar.py" = ["SIM115"] +"temporal/t.rast.what/t.rast.what.py" = ["SIM115"] +"temporal/t.rast3d.univar/testsuite/test_t_rast3d_univar.py" = ["SIM115"] "temporal/t.register/testsu*/*_raster_different_local.py" = ["FLY002"] "temporal/t.register/testsu*/*_raster_mapmetadata.py" = ["FLY002"] "temporal/t.register/testsuite/test_t_register_raster.py" = ["FLY002"] "temporal/t.register/testsuite/test_t_register_raster_file.py" = ["FLY002"] +"temporal/t.remove/t.remove.py" = ["SIM115"] +"temporal/t.unregister/t.unregister.py" = ["SIM115"] +"utils/**.py" = ["SIM115"] "utils/generate_release_notes.py" = ["PGH004"] "vector/v.fill.holes/examples.ipynb" = ["PTH201"] From df714ec1f02fef2301a7bea3c7647f39b1530b55 Mon Sep 17 00:00:00 2001 From: Markus Neteler Date: Tue, 17 Sep 2024 17:27:11 +0200 Subject: [PATCH 236/471] docs: update and cleanup of infrastructure.md (#4294) - various minor updates - removal of GRASS Travis CI section --- doc/infrastructure.md | 78 ++++++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 41 deletions(-) diff --git a/doc/infrastructure.md b/doc/infrastructure.md index 4494a6cbd69..e5b59eee73b 100644 --- a/doc/infrastructure.md +++ b/doc/infrastructure.md @@ -1,11 +1,11 @@ # How the GRASS GIS Webserver and related infrastructure works Author: Markus Neteler -Last update: Dec 2023 +Last update: Sep 2024 ## GRASS GIS Source code repository -Maintainer: Markus Neteler, Martin Landa, OSGeo-SAC, +Maintainer: Markus Neteler, Martin Landa, OSGeo-SAC, Important update April 2019: The source code is now managed on GitHub (rather than in SVN). @@ -17,12 +17,12 @@ The GitHub repositories are: - GRASS GIS Add-ons: - GRASS GIS promotional material: - GRASS GIS Website (Hugo site): -- Github mirror at OSGeo: +- Github mirror at OSGeo (updated daily): Git usage: - [CONTRIBUTING.md file](../CONTRIBUTING.md) -- +- [Guide to contributing on GitHub](development/github_guide.md) Issues: @@ -39,8 +39,8 @@ Maintainer: M. Neteler - - - osgeo7-grass: LXD container on osgeo7 () - - OS: Debian Buster + - osgeo8-grass: LXD container on osgeo8 () + - OS: Debian GNU/Linux 11 bullseye - Apache Server with Hugo () - for migration details (7/2020), see - ssh login: via jumphost hop.osgeo8.osgeo.org @@ -48,17 +48,14 @@ Maintainer: M. Neteler - (CMSMS, replaced in 2020 by above Hugo based solution) - - Shared virtual OSGeo machine (osgeo6) hosted at Oregon State University - Open Source Lab server: osgeo6.osgeo.osuosl.org) - - Login: via OSGeo LDAP, there is a "grass" LDAP group - - Software: - - OS: Debian Wheezy - - Apache Server with PHP - - Login: via OSGeo LDAP, there is a "grass" LDAP group + - a static copy of the old site is found at + `neteler@grasslxd:old-grass-website/old.grass.osgeo.org/` (13 GB) + - otherwise: - Backups: - - osgeo7-grass: container on osgeo8 is backup'ed, see + - osgeo8-grass + wiki: container on osgeo8 is backup'ed, + see - Mirrors: @@ -70,18 +67,18 @@ Maintainer: M. Neteler - Weekly software snapshots (generated Saturday morning Portland (OR), US time): - Source code tarball of git (GitHub) - - Linux binary snapshot is compiled on osgeo7-grass + - Linux binary snapshot is compiled on osgeo8-grass - GRASS is compiled with GDAL, PROJ, SQLite, MySQL, PostgreSQL, FFTW, C++ support, see - binary tar.gz and manuals are moved into Web space - GRASS user manual HTML: - - generated during compilation of weekly Linux binary snapshot on osgeo7-grass + - generated during compilation of weekly Linux binary snapshot on osgeo8-grass - GRASS addons manual HTML: - - generated during compilation of weekly Linux binary snapshot on osgeo7-grass + - generated during compilation of weekly Linux binary snapshot on osgeo8-grass - GRASS programmer's manual () @@ -89,13 +86,15 @@ Maintainer: M. Neteler - HTML: cronjob run Saturday morning Portland (OR), US time - disabled: PDF: cronjob run Saturday morning Portland (OR), US time -- Mailman mailing lists + automated greylisting (at lists.osgeo.org since 11/2007) +- Mailman mailing lists + automated greylisting + (at , since 11/2007) - Mailman is doing the job, only registered users can post - messages from unsubscribed people is auto-discarded without notification - the open "weblist" operates instead like this: - - User -> grass-web at lists osgeo.org -> greylisting -> Mailman - - for greylisting, see + - mails goes to grass-web at lists osgeo.org -> greylisting -> Mailman + - for greylisting, see + - Moderation of queue: - Backup of mailing lists (mbox files) @@ -104,7 +103,7 @@ Maintainer: M. Neteler - Web statistics - Matomo: (not publicly accessible; access: Markus Neteler) - - Selected stats: + - Selected stats: and Summary: The system should run almost autonomously. @@ -148,11 +147,9 @@ Maintainer: Martin Landa, Markus Neteler - - Mediawiki software -- requires registration to keep spammers out - -Summary: The system should run almost autonomous. An eye must be be kept -on people trying to spam the site. Several layers of registration protection -are in place due to excessive spam. +- requires registration at + [OSGeo-LDAP](https://www.osgeo.org/community/getting-started-osgeo/osgeo_userid/) + (to keep spammers out) Macros for manual pages (src, cmd, API, ...): @@ -163,9 +160,15 @@ Macros for manual pages (src, cmd, API, ...): Channel: irc://irc.libera.chat/grass Web based client: See +Libera IRC: + +- Founder: jmckenna, markusN + +Former freenode IRC: + - channel owner: Alessandro Frigeri ("geoalf") - quasi guru level: Markus Neteler ("markusN") -- original (freenode) operators: +- original operators: - Jachym ("jachym") - Luca ("doktoreas") - Soeren ("huhabla") @@ -181,16 +184,16 @@ Old bugtrackers: see ## GRASS GIS Addons -Maintainer: Martin Landa and Markus Neteler +Maintainer: Martin Landa, Tomas Zigo, and Markus Neteler Details: -- Windows-addons: grass-addons/utils/addons/README.txt +- Windows-addons: `grass-addons/utils/addons/README.txt` - Addon manual pages cronjob: - Rendered manuals: -The redirect to the latest grassX directory is defined on grass.osgeo.org: -/etc/apache2/includes/grass.osgeo.org.inc +The redirect to the latest `grassX` directory is defined on grass.osgeo.org: +`/etc/apache2/includes/grass.osgeo.org.inc` Procedure building of binaries (Windows): @@ -199,8 +202,8 @@ Procedure building of binaries (Windows): - A new compilation is triggered every time a commit is done in the Addons repo. - Logs: - Linux log files: (compiled on - `grasslxd` on `osgeo7`) - - Windows log files: + `grasslxd` on `osgeo8`) + - Windows log files: Procedure of granting write access to Addons repo: @@ -213,14 +216,7 @@ Procedure of granting write access to Addons repo: XML file for g.extension: -- generated in grass-addons/utils/addons/grass-addons-publish.sh - -## GRASS Travis CI - -Maintainer: Martin Landa - -- -- +- generated in `grass-addons/utils/addons/grass-addons-publish.sh` ## GRASS CI: GitHub Actions From a816dc1548fec62273cb3829d614f2ffb8290d67 Mon Sep 17 00:00:00 2001 From: Nicklas Larsson Date: Tue, 17 Sep 2024 18:50:39 +0200 Subject: [PATCH 237/471] docs: update Programmer's Manual's GIS lib (string) section (#4333) --- lib/gis/gislib.dox | 43 +++++++++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/lib/gis/gislib.dox b/lib/gis/gislib.dox index fbe1df178e6..852159228cb 100644 --- a/lib/gis/gislib.dox +++ b/lib/gis/gislib.dox @@ -1179,6 +1179,14 @@ The next routines replaces character(s) from string. Replace all occurrences of character in string with new. + - G_str_to_sql() + +Make string SQL compliant. + + - G_str_replace() + +Replace all occurrences of old_str in buffer with new_str. + This next routine copies a string to allocated memory. - G_store() @@ -1186,6 +1194,14 @@ This next routine copies a string to allocated memory. This routine allocates enough memory to hold the string, and returns a pointer to the allocated memory. + - G_store_lower() + +Copy string to allocated memory and convert copied string to lower case. + + - G_store_upper() + +Copy string to allocated memory and convert copied string to upper case. + The next 2 routines convert between upper and lower case. - G_tolcase() @@ -1203,12 +1219,6 @@ equivalent. This routine remove trailing zeros from decimal number for example: 23.45000 would come back as 23.45. - - G_index() - - - G_rindex() - -Get position of delimiter. - - G_strcasecmp() - G_strncasecmp() @@ -1220,11 +1230,24 @@ String compare ignoring case (upper or lower). Return a pointer to the first occurrence of subString in mainString, or NULL if no occurrences are found. - - G_strdup() + - G_str_concat() + +Concatenation of a list of strings, separated by a given character. + + - G_tokenize() + - G_tokenize2() + +Tokenize string. Create array of strings from an input string split up with +given delimiter characters. + + - G_strlcat() + +Size-bounded string concatenation (wrapper function to strlcat()). + + - G_strlcpy() + +Size-bounded string copying (wrapper function to strlcpy()). -Returns a pointer to a string that is a duplicate of the string given -to G_strdup. The duplicate is created using malloc. If unable to -allocate the required space, NULL is returned. \section Enhanced_UNIX_Routines Enhanced UNIX Routines From 5d6a2e84e19a37adbecc684ca8c5b2e1d5c154c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Tue, 17 Sep 2024 14:27:20 -0400 Subject: [PATCH 238/471] t.rast.univar: Use pathlib Path.read_text to open test outputs in t.rast.univar and t.rast3d.univar (#4334) --- pyproject.toml | 2 -- temporal/t.rast.univar/testsuite/test_t_rast_univar.py | 7 ++++--- temporal/t.rast3d.univar/testsuite/test_t_rast3d_univar.py | 5 +++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index f3ca9ab8dd6..52c24a386fb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -376,9 +376,7 @@ ignore = [ "temporal/t.rast.algebra/testsu*/*_algebra_arithmetic.py" = ["FLY002"] "temporal/t.rast.colors/t.rast.colors.py" = ["SIM115"] "temporal/t.rast.series/t.rast.series.py" = ["SIM115"] -"temporal/t.rast.univar/testsuite/test_t_rast_univar.py" = ["SIM115"] "temporal/t.rast.what/t.rast.what.py" = ["SIM115"] -"temporal/t.rast3d.univar/testsuite/test_t_rast3d_univar.py" = ["SIM115"] "temporal/t.register/testsu*/*_raster_different_local.py" = ["FLY002"] "temporal/t.register/testsu*/*_raster_mapmetadata.py" = ["FLY002"] "temporal/t.register/testsuite/test_t_register_raster.py" = ["FLY002"] diff --git a/temporal/t.rast.univar/testsuite/test_t_rast_univar.py b/temporal/t.rast.univar/testsuite/test_t_rast_univar.py index cfdfa78ab27..11a91614b6c 100644 --- a/temporal/t.rast.univar/testsuite/test_t_rast_univar.py +++ b/temporal/t.rast.univar/testsuite/test_t_rast_univar.py @@ -8,6 +8,7 @@ @author Soeren Gebbert """ +from pathlib import Path from grass.gunittest.case import TestCase from grass.gunittest.gmodules import SimpleModule @@ -243,7 +244,7 @@ def test_subset_with_output(self): a_3@testing||2001-07-01 00:00:00|2001-10-01 00:00:00|300|300|300|300|0|0|0|2880000|0|9600|9600 a_4@testing||2001-10-01 00:00:00|2002-01-01 00:00:00|400|400|400|400|0|0|0|3840000|0|9600|9600 """ - univar_output = open("univar_output.txt", "r").read() + univar_output = Path("univar_output.txt").read_text() for ref, res in zip(univar_text.split("\n"), univar_output.split("\n")): if ref and res: @@ -269,7 +270,7 @@ def test_subset_with_extended_statistics_and_output(self): a_3@m2||2001-07-01 00:00:00|2001-10-01 00:00:00|300|300|300|300|0|0|0|2880000|0|9600|9600|300|300|300|300|300 a_4@m2||2001-10-01 00:00:00|2002-01-01 00:00:00|400|400|400|400|0|0|0|3840000|0|9600|9600|400|400|400|400|400 """ - univar_output = open("univar_output.txt", "r").read() + univar_output = Path("univar_output.txt").read_text() for ref, res in zip(univar_text.split("\n"), univar_output.split("\n")): if ref and res: @@ -292,7 +293,7 @@ def test_subset_with_extended_statistics_and_output(self): a_3@testing||2001-07-01 00:00:00|2001-10-01 00:00:00|300|300|300|300|0|0|0|2880000|0|9600|9600 a_4@testing||2001-10-01 00:00:00|2002-01-01 00:00:00|400|400|400|400|0|0|0|3840000|0|9600|9600 """ - univar_output = open("univar_output.txt", "r").read() + univar_output = Path("univar_output.txt").read_text() for ref, res in zip(univar_text.split("\n"), univar_output.split("\n")): if ref and res: diff --git a/temporal/t.rast3d.univar/testsuite/test_t_rast3d_univar.py b/temporal/t.rast3d.univar/testsuite/test_t_rast3d_univar.py index d34c1f253aa..7c8ab1e4100 100644 --- a/temporal/t.rast3d.univar/testsuite/test_t_rast3d_univar.py +++ b/temporal/t.rast3d.univar/testsuite/test_t_rast3d_univar.py @@ -8,6 +8,7 @@ @author Soeren Gebbert """ +from pathlib import Path from grass.gunittest.case import TestCase from grass.gunittest.gmodules import SimpleModule @@ -119,7 +120,7 @@ def test_subset_with_output(self): a_3@testing|2001-07-01 00:00:00|2001-10-01 00:00:00|300|300|300|300|0|0|0|144000000|0|480000|480000 a_4@testing|2001-10-01 00:00:00|2002-01-01 00:00:00|400|400|400|400|0|0|0|192000000|0|480000|480000 """ - univar_output = open("univar_output.txt", "r").read() + univar_output = Path("univar_output.txt").read_text() for ref, res in zip(univar_text.split("\n"), univar_output.split("\n")): if ref and res: @@ -142,7 +143,7 @@ def test_subset_with_output_no_header(self): a_3@testing|2001-07-01 00:00:00|2001-10-01 00:00:00|300|300|300|300|0|0|0|144000000|0|480000|480000 a_4@testing|2001-10-01 00:00:00|2002-01-01 00:00:00|400|400|400|400|0|0|0|192000000|0|480000|480000 """ - univar_output = open("univar_output.txt", "r").read() + univar_output = Path("univar_output.txt").read_text() for ref, res in zip(univar_text.split("\n"), univar_output.split("\n")): if ref and res: From 5e47e41efad02e47aec2e017a07e12215ec61197 Mon Sep 17 00:00:00 2001 From: Markus Neteler Date: Wed, 18 Sep 2024 00:50:13 +0200 Subject: [PATCH 239/471] docs: update outdated trac URLs to GitHub (#4317) Several documents and files contain references to documents on https://trac.osgeo.org/grass/wiki/ while newer versions exist in GitHub. This PR updates a series of URLs and drops the outdated files `CHANGES` and `NEWS`. Additionally, some minor markdown fixes. --- .github/labeler.yml | 2 -- CHANGES | 10 ------- INSTALL.md | 50 +++++++++++++++++---------------- Makefile | 2 +- NEWS | 18 ------------ README.md | 10 +++---- SECURITY.md | 2 +- general/g.parser/g.parser.html | 2 +- gui/wxpython/gui_core/pyedit.py | 8 ++++-- rpm/grass.spec | 2 +- 10 files changed, 41 insertions(+), 65 deletions(-) delete mode 100644 CHANGES delete mode 100644 NEWS diff --git a/.github/labeler.yml b/.github/labeler.yml index d6caa447e2e..464ccdce2b8 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -123,9 +123,7 @@ docs: - '**/*.png' - '**.cff' - CITING - - CHANGES - AUTHORS - - NEWS - TODO - all-globs-to-all-files: - '!doc/development/rfc/**' diff --git a/CHANGES b/CHANGES deleted file mode 100644 index d4c8280bc7b..00000000000 --- a/CHANGES +++ /dev/null @@ -1,10 +0,0 @@ -CHANGES in GRASS GIS 8.x - -https://trac.osgeo.org/grass/wiki/Grass8/NewFeatures80 -https://trac.osgeo.org/grass/wiki/Grass8/NewFeatures82 - -List of releases: - -- GitHub list: https://github.com/OSGeo/grass/releases -- Overview list: https://trac.osgeo.org/grass/wiki/Release -- History: https://grass.osgeo.org/home/history/releases/ (starting 1984!) diff --git a/INSTALL.md b/INSTALL.md index e6bd041fcf9..41249a04143 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -29,8 +29,8 @@ directory. Installation order: 1. PROJ -2. GDAL-OGR (compiled without GRASS support) -3. optionally: databases such as PostgreSQL, MySQL, sqlite +2. GDAL/OGR (compiled without GRASS support) +3. optionally: databases such as PostgreSQL, MySQL, SQLite 4. GRASS GIS 5. optionally: GDAL-OGR-GRASS plugin @@ -41,7 +41,7 @@ GRASS source code is currently distributed in 2 forms: ### Officially released source code The full source code version contains all the GRASS source code -required for compilation. It is distributed as one file (*.tar.gz +required for compilation. It is distributed as one file (`*.tar.gz` package) and the version is composed of 3 numbers, e.g. 3.7.0, 3.7.1 etc. See . @@ -52,7 +52,7 @@ This version of the source code can be acquired either from the GitHub repository () or as a auto-generated snapshot (`*.tar.gz` package) of the GitHub repository. The snapshot name contains the date when the snapshot was created (checked out from -the GitHub repository), e.g. grass-3.7.git_src_snapshot_2022_04_27.tar.gz +the GitHub repository), e.g. `grass-3.7.git_src_snapshot_2022_04_27.tar.gz` from ## (B) COMPILATION @@ -72,7 +72,7 @@ Detailed Wiki notes for various operating systems (MS-Windows, GNU/Linux distributions, FreeBSD, AIX, etc) are available at: -First step of the compilation (-g for debugging, or -O2 for optimization): +First step of the compilation (`-g` for debugging, or `-O2` for optimization): ```bash CFLAGS="-g -Wall" ./configure @@ -108,7 +108,7 @@ make Note for Solaris users (see also Wiki page above): To configure GRASS correctly on a system which doesn't have a suitable -install program (AC_PROG_INSTALL ignores versions which are known to +install program (`AC_PROG_INSTALL` ignores versions which are known to have problems), you need to ensure that $srcdir is an absolute path, by using e.g.: @@ -136,7 +136,7 @@ CC=cc CPP=cpp ./configure ... ## (C) COMPILATION NOTES for 64bit platforms To successfully compile GRASS on 64bit platforms, the required -FFTW2 library has to be compiled with -fPIC flag: +FFTW library has to be compiled with `-fPIC` flag: ```bash #this applies to FFTW3, not to GRASS GIS: @@ -151,13 +151,13 @@ make install After compilation, the resulting code is stored in the directory ```bash -./dist.$ARCH +./dist.$ARCH/ ``` -and the scripts (grass, ...) in +and the script (`grass`) in ```bash -./bin.$ARCH +./bin.$ARCH/ ``` To run GRASS, simply start @@ -175,7 +175,7 @@ grass ## (E) INSTALLATION ON MACOSX -See the ReadMe.rtf in the ./macosx/ folder and the Wiki page above. +See the `ReadMe.rtf` in the `./macosx/` folder and the Wiki page above. ## (F) RUNNING GRASS GIS @@ -206,7 +206,7 @@ make make install ``` -For details, see +For details, see [Guide to contributing on GitHub](./doc/development/github_guide.md). ## (H) COMPILING INDIVIDUAL MODULES - OWN MODULES @@ -240,7 +240,7 @@ gmake Note: If you keep your module source code outside the standard GRASS source code directory structure, you will have to change the relative -path(s) in the Makefile to absolute path(s). +path(s) in the `Makefile` to absolute path(s). ## (I) CODE OPTIMIZATION @@ -258,8 +258,8 @@ setenv CFLAGS -O ./configure ``` -whichever works on your shell. Use -O2 instead of -O if your compiler -supports this (note: O is the letter, not zero). Using the "gcc" compiler, +whichever works on your shell. Use `-O2` instead of `-O` if your compiler +supports this (note: `O` is the letter, not zero). Using the "gcc" compiler, you can also specify processor specific flags (examples, please suggest better settings to us): @@ -272,11 +272,11 @@ CFLAGS="-O2 -msse -msse2 -mfpmath=sse \ CFLAGS="-mtune=nocona -m64 -minline-all-stringops" # Intel Pentium 64bit processor ``` -Note: As of version 4.3.0, GCC offers the -march=native switch that +Note: As of version 4.3.0, GCC offers the `-march=native` switch that enables CPU auto-detection and automatically selects optimizations supported -by the local machine at GCC runtime including -mtune. +by the local machine at GCC runtime including `-mtune`. -To find out optional CFLAGS for your platform, enter: +To find out optional `CFLAGS` for your platform, enter: ```bash gcc -dumpspecs @@ -285,7 +285,7 @@ gcc -dumpspecs See also: A real fast GRASS version (and small binaries) will be created with -LDFLAGS set to "stripping" (but this disables debugging): +`LDFLAGS` set to "stripping" (but this disables debugging): ```bash CFLAGS="-O2 -mcpu= -Wall" LDFLAGS="-s" ./configure @@ -296,7 +296,7 @@ CFLAGS="-O2 -mcpu= -Wall" LDFLAGS="-s" ./configure The `LDFLAGS=""` part must be undefined as `-s` will strip the debugging information. -Don't use `-O` for CFLAGS if you want to be able to step through function +Don't use `-O` for `CFLAGS` if you want to be able to step through function bodies. When optimisation is enabled, the compiler will re-order statements and re-arrange expressions, resulting in object code which barely resembles the source code. @@ -307,7 +307,7 @@ The `-g` and `-Wall` compiler flags are often useful for assisting debugging: CFLAGS="-g -Wall" ./configure ``` -See also the file ./doc/debugging.txt and the Wiki page +See also the file `./doc/debugging.txt` and the Wiki page ## (K) SUPPORT @@ -322,13 +322,15 @@ developers mailing list. See ## (L) GRASS PROGRAMMER'S MANUAL The Programmer's manual is generated with doxygen from the source code. -Please see the README file and the files at: +Please see the [README](doc/development/README.md) file and the files at: ## (M) CONTRIBUTING CODE AND PATCHES -Please see ./SUBMITTING in this directory, or better, - +Please see + +- [GRASS Programming Style Guide](./doc/development/style_guide.md) +- [Guide to contributing on GitHub](./doc/development/github_guide.md) ## Authors diff --git a/Makefile b/Makefile index be8834ecf42..8b32a149a5a 100644 --- a/Makefile +++ b/Makefile @@ -51,7 +51,7 @@ DIRS = \ SUBDIRS = $(DIRS) -FILES = AUTHORS CHANGES CITING COPYING GPL.TXT INSTALL.md REQUIREMENTS.md contributors.csv contributors_extra.csv translators.csv +FILES = AUTHORS CITING COPYING GPL.TXT INSTALL.md REQUIREMENTS.md contributors.csv contributors_extra.csv translators.csv FILES_DST = $(patsubst %,$(ARCH_DISTDIR)/%,$(FILES)) default: diff --git a/NEWS b/NEWS deleted file mode 100644 index 32b4cd52110..00000000000 --- a/NEWS +++ /dev/null @@ -1,18 +0,0 @@ -NEWS - -GRASS GIS 8 - o https://trac.osgeo.org/grass/wiki/Grass8/NewFeatures80 - o https://trac.osgeo.org/grass/wiki/Grass8/NewFeatures82 - -GRASS GIS 7 - o https://trac.osgeo.org/grass/wiki/Grass7/NewFeatures78 - o https://trac.osgeo.org/grass/wiki/Grass7/NewFeatures76 - o https://trac.osgeo.org/grass/wiki/Grass7/NewFeatures74 - o https://trac.osgeo.org/grass/wiki/Grass7/NewFeatures72 - o https://trac.osgeo.org/grass/wiki/Grass7/NewFeatures - - List of releases - o https://trac.osgeo.org/grass/wiki/Release - -GRASS GIS 1-6 - o List of older releases, starting in 1984 (!), see https://grass.osgeo.org/about/history/releases/ diff --git a/README.md b/README.md index b10bd51ec03..7dfdb49df82 100644 --- a/README.md +++ b/README.md @@ -31,11 +31,6 @@ In general: you don't really need write access as you can simply open a [pull request](https://github.com/OSGeo/grass/pulls) to contribute to GRASS GIS. See [CONTRIBUTING file](CONTRIBUTING.md) for more details. -How to get write access here - -Want to become a core developer? See -[Procedure for gaining Git write access](https://trac.osgeo.org/grass/wiki/HowToContribute#WriteaccesstotheGRASScorerepository) - ## How to compile GRASS > See the INSTALL.md file. @@ -111,6 +106,11 @@ this issue, clean all the compiled files from the source code: make distclean ``` +## Further documents + +- [Contributor Covenant Code of Conduct](CODE_OF_CONDUCT.md) +- [Roadmap](https://grass.osgeo.org/about/roadmap/) + ## Thanks to all contributors ❤ [![GRASS contributors](https://contrib.rocks/image?repo=OSGeo/grass "GRASS contributors")](https://github.com/OSGeo/grass/graphs/contributors) diff --git a/SECURITY.md b/SECURITY.md index feef9649806..0b566dc899d 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -25,7 +25,7 @@ vulnerability, please follow these steps: ## Supported Versions -Please refer to our [Release Schedule](https://trac.osgeo.org/grass/wiki/Release/Schedule) +Please refer to our [download section](https://grass.osgeo.org/download/) for details on which versions are currently supported. ## Security Measures diff --git a/general/g.parser/g.parser.html b/general/g.parser/g.parser.html index 1c0154cca09..b1de89a3a08 100644 --- a/general/g.parser/g.parser.html +++ b/general/g.parser/g.parser.html @@ -673,7 +673,7 @@

SEE ALSO

Overview table: Parser standard options

-Submitting rules for Python +Style Guide: Developing Python scripts

Related Wiki pages: diff --git a/gui/wxpython/gui_core/pyedit.py b/gui/wxpython/gui_core/pyedit.py index 4974e0ce8f5..2de2d45b066 100644 --- a/gui/wxpython/gui_core/pyedit.py +++ b/gui/wxpython/gui_core/pyedit.py @@ -631,10 +631,14 @@ def OnModulesHelp(self, event): self.giface.Help("full_index") def OnSubmittingHelp(self, event): - open_url("https://trac.osgeo.org/grass/wiki/Submitting/Python") + open_url( + "https://github.com/OSGeo/grass/blob/main/doc/development/style_guide.md#python" # noqa: E501 + ) def OnAddonsHelp(self, event): - open_url("https://grass.osgeo.org/development/code-submission/") + open_url( + "https://github.com/OSGeo/grass/blob/main/doc/development/style_guide.md#developing-grass-addons" # noqa: E501 + ) def OnSupport(self, event): open_url("https://grass.osgeo.org/support/") diff --git a/rpm/grass.spec b/rpm/grass.spec index a68453145bc..90c79bc939f 100644 --- a/rpm/grass.spec +++ b/rpm/grass.spec @@ -320,7 +320,7 @@ fi %{_docdir}/%{name}%{shortver} %files libs -%license AUTHORS COPYING GPL.TXT CHANGES +%license AUTHORS COPYING GPL.TXT %{_sysconfdir}/ld.so.conf.d/%{name}-%{_arch}.conf %{_libdir}/%{name}%{shortver}/lib/*.so %dir %{_libdir}/%{name}%{shortver}/driver From 3bc6e957803ce5cafe25313e0b515bf8c8d1bd95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Tue, 17 Sep 2024 18:52:28 -0400 Subject: [PATCH 240/471] grass.temporal.abstract_space_time_dataset: Use Path.read_text() to load SQL template files (#4335) grass.temporal.abstract_space_time_dataset: Use Path.read_text() to load SQL template files Fixes ResourceWarnings about unclosed files, fixing ruff SIM115 at the same time. These 4 places were opening a file and reading it completely in the same line, but never closed explicitly the file, nor used a context manager that would kick in as much as possible on errors inside the calls. --- pyproject.toml | 6 +++- .../temporal/abstract_space_time_dataset.py | 34 +++++++------------ 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 52c24a386fb..b5c7a478864 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -346,7 +346,11 @@ ignore = [ "python/grass/script/db.py" = ["SIM115"] "python/grass/script/raster.py" = ["SIM115"] "python/grass/script/utils.py" = ["SIM115"] -"python/grass/temporal/*.py" = ["SIM115"] +"python/grass/temporal/aggregation.py" = ["SIM115"] +"python/grass/temporal/register.py" = ["SIM115"] +"python/grass/temporal/stds_export.py" = ["SIM115"] +"python/grass/temporal/stds_import.py" = ["SIM115"] +"python/grass/temporal/univar_statistics.py" = ["SIM115"] "python/grass/utils/download.py" = ["SIM115"] "raster/r.*/testsuite/*.py" = ["SIM115"] "raster/r.topidx/*.py" = ["SIM115"] diff --git a/python/grass/temporal/abstract_space_time_dataset.py b/python/grass/temporal/abstract_space_time_dataset.py index cc68f92af2b..fc154270f59 100644 --- a/python/grass/temporal/abstract_space_time_dataset.py +++ b/python/grass/temporal/abstract_space_time_dataset.py @@ -16,6 +16,7 @@ class that is the base class for all space time datasets. import uuid from abc import ABCMeta, abstractmethod from datetime import datetime +from pathlib import Path from .abstract_dataset import AbstractDataset, AbstractDatasetComparisonKeyStartTime from .core import ( @@ -395,9 +396,7 @@ def insert(self, dbif=None, execute=True): # %s;"%(stds_register_table + "_index", stds_register_table)) # Read the SQL template - sql = open( - os.path.join(sql_path, "stds_map_register_table_template.sql"), "r" - ).read() + sql = Path(sql_path, "stds_map_register_table_template.sql").read_text() # Create a raster, raster3d or vector tables sql = sql.replace("SPACETIME_REGISTER_TABLE", stds_register_table) @@ -2818,13 +2817,10 @@ def update_from_registered_maps(self, dbif=None): ) if old_sqlite_version: template_suffix = "_old" - sql = open( - os.path.join( - sql_path, - f"update_stds_spatial_temporal_extent_template{template_suffix}.sql", - ), - "r", - ).read() + sql = Path( + sql_path, + f"update_stds_spatial_temporal_extent_template{template_suffix}.sql", + ).read_text() sql = sql.replace("GRASS_MAP", self.get_new_map_instance(None).get_type()) sql = sql.replace("SPACETIME_REGISTER_TABLE", stds_register_table) sql = sql.replace("SPACETIME_ID", self.base.get_id()) @@ -2834,13 +2830,10 @@ def update_from_registered_maps(self, dbif=None): sql_script += "\n" # Update type specific metadata - sql = open( - os.path.join( - sql_path, - f"update_{self.get_type()}_metadata_template{template_suffix}.sql", - ), - "r", - ).read() + sql = Path( + sql_path, + f"update_{self.get_type()}_metadata_template{template_suffix}.sql", + ).read_text() # Comment out update of semantic labels for DB version < 3 if get_tgis_db_version_from_metadata() < 3: @@ -2853,10 +2846,9 @@ def update_from_registered_maps(self, dbif=None): "-- count(distinct semantic_label)", ) elif old_sqlite_version and self.get_type() == "strds": - semantic_label_sql = open( - os.path.join(sql_path, "update_strds_metadata_template_v3.sql"), - "r", - ).read() + semantic_label_sql = Path( + sql_path, "update_strds_metadata_template_v3.sql" + ).read_text() sql = sql + "\n" + semantic_label_sql sql = sql.replace("SPACETIME_REGISTER_TABLE", stds_register_table) From da15442d8428bb534272fc8fd65f1f13f8dbf55b Mon Sep 17 00:00:00 2001 From: Nicklas Larsson Date: Wed, 18 Sep 2024 09:42:17 +0200 Subject: [PATCH 241/471] lib/gis: match prototype with declaration for G_strlcat and G_strlcpy (#4332) Make local implementations of strlcpy() and strlcat() optimizable by using restrict type qualifier, while keeping public API for G_strlcat and G_strlcpy available to C++ code and having identical prototype and declaration. --- lib/gis/strlcat.c | 14 +++++++++++--- lib/gis/strlcpy.c | 14 +++++++++++--- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/lib/gis/strlcat.c b/lib/gis/strlcat.c index ed6c3887e72..635ffbdedb8 100644 --- a/lib/gis/strlcat.c +++ b/lib/gis/strlcat.c @@ -28,6 +28,9 @@ #include #include +static size_t G__strlcat(char *restrict dst, const char *restrict src, + size_t dsize); + /** * \brief Size-bounded string concatenation * @@ -51,12 +54,18 @@ * including the terminating NUL character). If the return value * is >= dsize, truncation occurred. */ - -size_t G_strlcat(char *restrict dst, const char *restrict src, size_t dsize) +size_t G_strlcat(char *dst, const char *src, size_t dsize) { #ifdef HAVE_STRLCAT return strlcat(dst, src, dsize); #else + return G__strlcat(dst, src, dsize); +#endif +} + +static size_t G__strlcat(char *restrict dst, const char *restrict src, + size_t dsize) +{ const char *odst = dst; const char *osrc = src; size_t n = dsize; @@ -80,5 +89,4 @@ size_t G_strlcat(char *restrict dst, const char *restrict src, size_t dsize) *dst = '\0'; return (dlen + (src - osrc)); /* count does not include NUL */ -#endif } diff --git a/lib/gis/strlcpy.c b/lib/gis/strlcpy.c index 5c0601391f5..a2d6e8918e7 100644 --- a/lib/gis/strlcpy.c +++ b/lib/gis/strlcpy.c @@ -22,6 +22,9 @@ #include +static size_t G__strlcpy(char *restrict dst, const char *restrict src, + size_t dsize); + /** * \brief Safe string copy function. * @@ -46,12 +49,18 @@ * \warning The src string must be a valid NUL-terminated C string. Passing an * unterminated string may result in buffer overrun. */ - -size_t G_strlcpy(char *restrict dst, const char *restrict src, size_t dsize) +size_t G_strlcpy(char *dst, const char *src, size_t dsize) { #ifdef HAVE_STRLCPY return strlcpy(dst, src, dsize); #else + return G__strlcpy(dst, src, dsize); +#endif +} + +static size_t G__strlcpy(char *restrict dst, const char *restrict src, + size_t dsize) +{ const char *osrc = src; size_t nleft = dsize; @@ -72,5 +81,4 @@ size_t G_strlcpy(char *restrict dst, const char *restrict src, size_t dsize) } return (src - osrc - 1); /* count does not include NUL */ -#endif } From 9420d00ea851c6c089b39d450e31bd4c8cab841e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Wed, 18 Sep 2024 13:02:52 -0400 Subject: [PATCH 242/471] grass.gunittest: Fix parsing exclusion from config file on Windows (#4324) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * grass.gunittest: Ensure file list exists before processing exclusions * grass.gunittest: Use pathlib.Path to handle test files across platforms * grass.gunittest: Use pathlib.PurePath instead of concrete pathlib.Path * grass.gunittest: Also use pathlib.PurePath for patterns * grass.gunittest: Simplify pathlib filtering using a set comprehension * grass.gunittest: Add typing to fnmatch_exclude_with_base signature * style: Sort imports with isort * Add Edouard Choinière (echoix) to the authors * Fix typo in comment * grass.gunittest: Filter file list respecting all three filter arguments if specified * grass.gunittest: Change set comprehension variable name --- python/grass/gunittest/loader.py | 47 ++++++++++++++++---------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/python/grass/gunittest/loader.py b/python/grass/gunittest/loader.py index 33234e2b083..f01467ef1ba 100644 --- a/python/grass/gunittest/loader.py +++ b/python/grass/gunittest/loader.py @@ -6,17 +6,28 @@ License (>=v2). Read the file COPYING that comes with GRASS GIS for details. -:authors: Vaclav Petras +:authors: Vaclav Petras, Edouard Choinière """ -import os -import fnmatch -import unittest +from __future__ import annotations + import collections +import fnmatch +import os import re +import unittest +from pathlib import PurePath +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from collections.abc import Iterable -def fnmatch_exclude_with_base(files, base, exclude): +def fnmatch_exclude_with_base( + files: Iterable[str], + base: str | os.PathLike, + exclude: Iterable[str | os.PathLike | PurePath], +) -> list[str]: """Return list of files not matching any exclusion pattern :param files: list of file names @@ -24,23 +35,13 @@ def fnmatch_exclude_with_base(files, base, exclude): :param exclude: list of fnmatch glob patterns for exclusion """ not_excluded = [] - patterns = [] - # Make all dir separators slashes and drop leading current dir - # for both patterns and (later) for files. - for pattern in exclude: - pattern = pattern.replace(os.sep, "/") - if pattern.startswith("./"): - patterns.append(pattern[2:]) - else: - patterns.append(pattern) + patterns = {str(PurePath(item)) for item in exclude} + base_path = PurePath(base) for filename in files: - full_file_path = os.path.join(base, filename) - test_filename = full_file_path.replace(os.sep, "/") - if full_file_path.startswith("./"): - test_filename = full_file_path[2:] + test_filename: PurePath = base_path / filename matches = False for pattern in patterns: - if fnmatch.fnmatch(test_filename, pattern): + if fnmatch.fnmatch(str(test_filename), pattern): matches = True break if not matches: @@ -116,16 +117,16 @@ def discover_modules( dirs.remove(testsuite_dir) # do not recurse to testsuite full = os.path.join(root, testsuite_dir) - all_files = os.listdir(full) + files = os.listdir(full) if file_pattern: - files = fnmatch.filter(all_files, file_pattern) + files = fnmatch.filter(files, file_pattern) if file_regexp: - files = [f for f in all_files if re.match(file_regexp, f)] + files = [f for f in files if re.match(file_regexp, f)] if exclude: files = fnmatch_exclude_with_base(files, full, exclude) files = sorted(files) # get test/module name without .py - # extpecting all files to end with .py + # expecting all files to end with .py # this will not work for invoking bat files but it works fine # as long as we handle only Python files (and using Python # interpreter for invoking) From 05363519914f963f1a2c2d13df0e59e0ad9593e3 Mon Sep 17 00:00:00 2001 From: Makiko Shukunobe Date: Wed, 18 Sep 2024 15:00:49 -0400 Subject: [PATCH 243/471] checks: Flake8 F841 fixes in the wxpython directory part 1 (#4244) --- .flake8 | 8 ++------ gui/wxpython/animation/mapwindow.py | 2 +- gui/wxpython/animation/provider.py | 2 +- gui/wxpython/core/gthread.py | 2 -- gui/wxpython/core/render.py | 2 -- gui/wxpython/core/utils.py | 16 ++++++++++++---- gui/wxpython/core/ws.py | 2 -- 7 files changed, 16 insertions(+), 18 deletions(-) diff --git a/.flake8 b/.flake8 index 3182812dd1d..2cc14b2c6fd 100644 --- a/.flake8 +++ b/.flake8 @@ -29,13 +29,11 @@ per-file-ignores = locale/grass_po_stats.py: E122, E128, E231, E401, E722 gui/scripts/d.wms.py: E501 gui/wxpython/core/gcmd.py: E402 - gui/wxpython/core/gthread.py: F841 gui/wxpython/core/gconsole.py: E722 gui/wxpython/core/toolboxes.py: E722 - gui/wxpython/core/utils.py: E722, F841 + gui/wxpython/core/utils.py: E722 gui/wxpython/core/workspace.py: E722 - gui/wxpython/core/render.py: E722, F841 - gui/wxpython/core/ws.py: F841 + gui/wxpython/core/render.py: E722 gui/wxpython/core/settings.py: E722 gui/wxpython/core/watchdog.py: E402 gui/wxpython/datacatalog/tree.py: E731, E402 @@ -71,8 +69,6 @@ per-file-ignores = gui/wxpython/vnet/*: F841 gui/wxpython/wxgui.py: F841 gui/wxpython/animation/g.gui.animation.py: E501 - gui/wxpython/animation/mapwindow.py: F841 - gui/wxpython/animation/provider.py: F841 gui/wxpython/tplot/frame.py: F841, E722 gui/wxpython/tplot/g.gui.tplot.py: E501 gui/wxpython/rdigit/g.gui.rdigit.py: F841 diff --git a/gui/wxpython/animation/mapwindow.py b/gui/wxpython/animation/mapwindow.py index 749a2da147d..4a6822e0841 100644 --- a/gui/wxpython/animation/mapwindow.py +++ b/gui/wxpython/animation/mapwindow.py @@ -61,7 +61,7 @@ def Draw(self, dc): def OnPaint(self, event): Debug.msg(5, "BufferedWindow.OnPaint()") # All that is needed here is to draw the buffer to screen - dc = wx.BufferedPaintDC(self, self._Buffer) + wx.BufferedPaintDC(self, self._Buffer) def OnSize(self, event): Debug.msg(5, "BufferedWindow.OnSize()") diff --git a/gui/wxpython/animation/provider.py b/gui/wxpython/animation/provider.py index 46eddea7294..fd966219407 100644 --- a/gui/wxpython/animation/provider.py +++ b/gui/wxpython/animation/provider.py @@ -924,7 +924,7 @@ def test(): prov.mapsLoaded.connect(lambda: sys.stdout.write("Maps loading finished\n")) cmdMatrix = layerListToCmdsMatrix(layerList) prov.SetCmds(cmdMatrix, [layer.opacity for layer in layerList]) - app = wx.App() + wx.App() prov.Load(bgcolor=(13, 156, 230), nprocs=4) diff --git a/gui/wxpython/core/gthread.py b/gui/wxpython/core/gthread.py index 7bd8929c553..44a938f335a 100644 --- a/gui/wxpython/core/gthread.py +++ b/gui/wxpython/core/gthread.py @@ -95,8 +95,6 @@ def run(self): else: vars()[key] = None - requestTime = time.time() - ret = None exception = None time.sleep(0.01) diff --git a/gui/wxpython/core/render.py b/gui/wxpython/core/render.py index 3095cb47fc8..b98e4323578 100644 --- a/gui/wxpython/core/render.py +++ b/gui/wxpython/core/render.py @@ -666,8 +666,6 @@ def OnRenderDone(self, env): Make image composiotion, emits updateMap event. """ - stopTime = time.time() - maps = [] masks = [] opacities = [] diff --git a/gui/wxpython/core/utils.py b/gui/wxpython/core/utils.py index fe4f40c359a..10d6dca2759 100644 --- a/gui/wxpython/core/utils.py +++ b/gui/wxpython/core/utils.py @@ -827,8 +827,12 @@ def StoreEnvVariable(key, value=None, envFile=None): if os.path.exists(envFile): try: fd = open(envFile) - except OSError as e: - sys.stderr.write(_("Unable to open file '%s'\n") % envFile) + except OSError as error: + sys.stderr.write( + _("Unable to open file '{name}': {error}\n").format( + name=envFile, error=error + ) + ) return for line in fd: line = line.rstrip(os.linesep) @@ -857,8 +861,12 @@ def StoreEnvVariable(key, value=None, envFile=None): # write update env file try: fd = open(envFile, "w") - except OSError as e: - sys.stderr.write(_("Unable to create file '%s'\n") % envFile) + except OSError as error: + sys.stderr.write( + _("Unable to create file '{name}': {error}\n").format( + name=envFile, error=error + ) + ) return if windows: expCmd = "set" diff --git a/gui/wxpython/core/ws.py b/gui/wxpython/core/ws.py index 156cd0166f9..010deac824f 100644 --- a/gui/wxpython/core/ws.py +++ b/gui/wxpython/core/ws.py @@ -99,7 +99,6 @@ def Render(self, cmd, env): self.updateMap = True fetchData = True # changed to True when calling Render() - zoomChanged = False if self.renderedRegion is None or cmd != self.fetched_data_cmd: fetchData = True @@ -111,7 +110,6 @@ def Render(self, cmd, env): for c in ["e-w resol", "n-s resol"]: if self.renderedRegion and region[c] != self.renderedRegion[c]: - zoomChanged = True break if fetchData: From 4f8f676b8bd25da1d27ddf8fcb3007d3774ae714 Mon Sep 17 00:00:00 2001 From: Makiko Shukunobe Date: Wed, 18 Sep 2024 16:12:44 -0400 Subject: [PATCH 244/471] checks: Flake8 F841 fixes in the wxpython directory part 3 (#4261) --- .flake8 | 17 +++---- gui/wxpython/gcp/g.gui.gcp.py | 2 +- gui/wxpython/gcp/manager.py | 66 ++++++++++++---------------- gui/wxpython/gcp/mapdisplay.py | 2 - gui/wxpython/gui_core/dialogs.py | 1 - gui/wxpython/gui_core/forms.py | 3 -- gui/wxpython/gui_core/goutput.py | 6 +-- gui/wxpython/gui_core/gselect.py | 6 --- gui/wxpython/gui_core/menu.py | 15 ------- gui/wxpython/gui_core/preferences.py | 2 - gui/wxpython/gui_core/prompt.py | 2 - gui/wxpython/gui_core/pystc.py | 1 - gui/wxpython/gui_core/treeview.py | 16 ++++--- gui/wxpython/gui_core/widgets.py | 2 +- 14 files changed, 48 insertions(+), 93 deletions(-) diff --git a/.flake8 b/.flake8 index 2cc14b2c6fd..e72eaa73333 100644 --- a/.flake8 +++ b/.flake8 @@ -42,17 +42,14 @@ per-file-ignores = gui/wxpython/dbmgr/sqlbuilder.py: E722 gui/wxpython/dbmgr/manager.py: E722 gui/wxpython/docs/wxgui_sphinx/conf.py: E402, W291 - gui/wxpython/gcp/g.gui.gcp.py: F841 - gui/wxpython/gcp/manager.py: F841, E722 - gui/wxpython/gcp/mapdisplay.py: F841 - gui/wxpython/gui_core/*: F841, E266, E722 - gui/wxpython/gui_core/dialogs.py: E722, F841 - gui/wxpython/gui_core/forms.py: E722, F841 + gui/wxpython/gcp/manager.py: E722 + gui/wxpython/gui_core/*: E266, E722 + gui/wxpython/gui_core/dialogs.py: E722 + gui/wxpython/gui_core/forms.py: E722 gui/wxpython/gui_core/ghelp.py: E722 - gui/wxpython/gui_core/gselect.py: F841, E266, E722 - gui/wxpython/gui_core/preferences.py: E266, F841 - gui/wxpython/gui_core/treeview.py: F841 - gui/wxpython/gui_core/widgets.py: F841, E722, E266 + gui/wxpython/gui_core/gselect.py: E266, E722 + gui/wxpython/gui_core/preferences.py: E266 + gui/wxpython/gui_core/widgets.py: E722, E266 gui/wxpython/image2target/*: F841, E722, E265 gui/wxpython/image2target/g.gui.image2target.py: E501, E265, F841 gui/wxpython/iscatt/*: F841, E722, F405, F403 diff --git a/gui/wxpython/gcp/g.gui.gcp.py b/gui/wxpython/gcp/g.gui.gcp.py index 32ef2c4fc0d..12f0a152090 100755 --- a/gui/wxpython/gcp/g.gui.gcp.py +++ b/gui/wxpython/gcp/g.gui.gcp.py @@ -65,7 +65,7 @@ def main(): app = wx.App() - wizard = GCPWizard(parent=None, giface=StandaloneGrassInterface()) + GCPWizard(parent=None, giface=StandaloneGrassInterface()) app.MainLoop() diff --git a/gui/wxpython/gcp/manager.py b/gui/wxpython/gcp/manager.py index 696bc53609f..e5e0dd37471 100644 --- a/gui/wxpython/gcp/manager.py +++ b/gui/wxpython/gcp/manager.py @@ -1825,23 +1825,21 @@ def OnGeorect(self, event): else: flags = "a" - busy = wx.BusyInfo(_("Rectifying images, please wait..."), parent=self) - wx.GetApp().Yield() - - ret, msg = RunCommand( - "i.rectify", - parent=self, - getErrorMsg=True, - quiet=True, - group=self.xygroup, - extension=self.extension, - order=self.gr_order, - method=self.gr_method, - flags=flags, - overwrite=overwrite, - ) + with wx.BusyInfo(_("Rectifying images, please wait..."), parent=self): + wx.GetApp().Yield() - del busy + ret, msg = RunCommand( + "i.rectify", + parent=self, + getErrorMsg=True, + quiet=True, + group=self.xygroup, + extension=self.extension, + order=self.gr_order, + method=self.gr_method, + flags=flags, + overwrite=overwrite, + ) # provide feedback on failure if ret != 0: @@ -1885,24 +1883,22 @@ def OnGeorect(self, event): ) ret = msg = "" - busy = wx.BusyInfo( + with wx.BusyInfo( _("Rectifying vector map <%s>, please wait...") % vect, parent=self - ) - wx.GetApp().Yield() - - ret, msg = RunCommand( - "v.rectify", - parent=self, - getErrorMsg=True, - quiet=True, - input=vect, - output=self.outname, - group=self.xygroup, - order=self.gr_order, - overwrite=overwrite, - ) - - del busy + ): + wx.GetApp().Yield() + + ret, msg = RunCommand( + "v.rectify", + parent=self, + getErrorMsg=True, + quiet=True, + input=vect, + output=self.outname, + group=self.xygroup, + order=self.gr_order, + overwrite=overwrite, + ) # provide feedback on failure if ret != 0: @@ -2027,7 +2023,6 @@ def OnGROrder(self, event): elif self.gr_order == 2: minNumOfItems = 6 - diff = 6 - numOfItems # self.SetStatusText( # _('Insufficient points, 6+ points needed for 2nd order')) @@ -2330,7 +2325,6 @@ def OnZoomToTarget(self, event): def OnZoomMenuGCP(self, event): """Popup Zoom menu""" - point = wx.GetMousePosition() zoommenu = Menu() # Add items to the menu @@ -3432,7 +3426,6 @@ def UpdateSettings(self): srcrenderVector = False tgtrender = False tgtrenderVector = False - reload_target = False if self.new_src_map != src_map: # remove old layer layers = self.parent.grwiz.SrcMap.GetListOfLayers() @@ -3470,7 +3463,6 @@ def UpdateSettings(self): del layers[0] layers = self.parent.grwiz.TgtMap.GetListOfLayers() # self.parent.grwiz.TgtMap.DeleteAllLayers() - reload_target = True tgt_map["raster"] = self.new_tgt_map["raster"] tgt_map["vector"] = self.new_tgt_map["vector"] diff --git a/gui/wxpython/gcp/mapdisplay.py b/gui/wxpython/gcp/mapdisplay.py index 13872b807cf..66daa70d2ca 100644 --- a/gui/wxpython/gcp/mapdisplay.py +++ b/gui/wxpython/gcp/mapdisplay.py @@ -484,7 +484,6 @@ def PrintMenu(self, event): """ Print options and output menu for map display """ - point = wx.GetMousePosition() printmenu = Menu() # Add items to the menu setup = wx.MenuItem(printmenu, wx.ID_ANY, _("Page setup")) @@ -528,7 +527,6 @@ def SaveDisplayRegion(self, event): def OnZoomMenu(self, event): """Popup Zoom menu""" - point = wx.GetMousePosition() zoommenu = Menu() # Add items to the menu diff --git a/gui/wxpython/gui_core/dialogs.py b/gui/wxpython/gui_core/dialogs.py index c31939312a7..f7c7711cadf 100644 --- a/gui/wxpython/gui_core/dialogs.py +++ b/gui/wxpython/gui_core/dialogs.py @@ -180,7 +180,6 @@ def OnLocation(self, event): dbase = grass.gisenv()["GISDBASE"] self.element2.UpdateItems(dbase=dbase, location=location) self.element2.SetSelection(0) - mapset = self.element2.GetStringSelection() def GetValues(self): """Get location, mapset""" diff --git a/gui/wxpython/gui_core/forms.py b/gui/wxpython/gui_core/forms.py index f9829cd9d9b..84fd849908c 100644 --- a/gui/wxpython/gui_core/forms.py +++ b/gui/wxpython/gui_core/forms.py @@ -307,8 +307,6 @@ def run(self): pTable = self.task.get_param( "dbtable", element="element", raiseError=False ) - if pTable: - table = pTable.get("value", "") if name == "LayerSelect": # determine format @@ -1546,7 +1544,6 @@ def __init__(self, parent, giface, task, id=wx.ID_ANY, frame=None, *args, **kwar if value: selection.SetValue(value) - formatSelector = True # A gselect.Select is a combobox with two children: a textctl # and a popupwindow; we target the textctl here textWin = selection.GetTextCtrl() diff --git a/gui/wxpython/gui_core/goutput.py b/gui/wxpython/gui_core/goutput.py index 6aca497f365..3c8927f2233 100644 --- a/gui/wxpython/gui_core/goutput.py +++ b/gui/wxpython/gui_core/goutput.py @@ -619,11 +619,7 @@ def AddStyledMessage(self, message, style=None): if c == "\b": self.linePos -= 1 else: - if c == "\r": - pos = self.GetCurLine()[1] - # self.SetCurrentPos(pos) - else: - self.SetCurrentPos(self.linePos) + self.SetCurrentPos(self.linePos) self.ReplaceSelection(c) self.linePos = self.GetCurrentPos() if c != " ": diff --git a/gui/wxpython/gui_core/gselect.py b/gui/wxpython/gui_core/gselect.py index eae12085f92..cec6963a527 100644 --- a/gui/wxpython/gui_core/gselect.py +++ b/gui/wxpython/gui_core/gselect.py @@ -1373,12 +1373,6 @@ def Insert(self, group): """Insert subgroups for defined group""" if not group: return - gisenv = gs.gisenv() - try: - name, mapset = group.split("@", 1) - except ValueError: - name = group - mapset = gisenv["MAPSET"] mlist = RunCommand("i.group", group=group, read=True, flags="sg").splitlines() try: diff --git a/gui/wxpython/gui_core/menu.py b/gui/wxpython/gui_core/menu.py index 71d6d153f8b..de6b49b7e93 100644 --- a/gui/wxpython/gui_core/menu.py +++ b/gui/wxpython/gui_core/menu.py @@ -68,8 +68,6 @@ def _createMenu(self, node): self._createMenuItem(menu, label=child.label, **data) - self.parent.Bind(wx.EVT_MENU_HIGHLIGHT_ALL, self.OnMenuHighlight) - return menu def _createMenuItem( @@ -136,19 +134,6 @@ def GetCmd(self): """ return self.menucmd - def OnMenuHighlight(self, event): - """ - Default menu help handler - """ - # Show how to get menu item info from this event handler - id = event.GetMenuId() - item = self.FindItemById(id) - if item: - help = item.GetHelp() - - # but in this case just call Skip so the default is done - event.Skip() - class Menu(MenuBase, wx.MenuBar): def __init__(self, parent, model, class_handler=None): diff --git a/gui/wxpython/gui_core/preferences.py b/gui/wxpython/gui_core/preferences.py index 3c3df8e8e42..c2020b2ffba 100644 --- a/gui/wxpython/gui_core/preferences.py +++ b/gui/wxpython/gui_core/preferences.py @@ -2021,8 +2021,6 @@ def OnCheckColorTable(self, event): def OnLoadEpsgCodes(self, event): """Load EPSG codes from the file""" - win = self.FindWindowById(self.winId["projection:statusbar:projFile"]) - path = win.GetValue() epsgCombo = self.FindWindowById(self.winId["projection:statusbar:epsg"]) wx.BeginBusyCursor() try: diff --git a/gui/wxpython/gui_core/prompt.py b/gui/wxpython/gui_core/prompt.py index 6ac9219f827..6734de169a9 100644 --- a/gui/wxpython/gui_core/prompt.py +++ b/gui/wxpython/gui_core/prompt.py @@ -511,7 +511,6 @@ def OnChar(self, event): # complete command after pressing '.' if event.GetKeyCode() == 46: self.autoCompList = [] - entry = self.GetTextLeft() self.InsertText(pos, ".") self.CharRight() self.toComplete = self.EntityToComplete() @@ -538,7 +537,6 @@ def OnChar(self, event): or event.GetKeyCode() == wx.WXK_SUBTRACT ): self.autoCompList = [] - entry = self.GetTextLeft() self.InsertText(pos, "-") self.CharRight() self.toComplete = self.EntityToComplete() diff --git a/gui/wxpython/gui_core/pystc.py b/gui/wxpython/gui_core/pystc.py index 33d5f03b5f9..d545ccb21e4 100644 --- a/gui/wxpython/gui_core/pystc.py +++ b/gui/wxpython/gui_core/pystc.py @@ -98,7 +98,6 @@ def __init__(self, parent, id=wx.ID_ANY, statusbar=None): 9, wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL ) face = font.GetFaceName() - size = font.GetPointSize() # setting the monospace here to not mess with the rest of the code # TODO: review the whole styling diff --git a/gui/wxpython/gui_core/treeview.py b/gui/wxpython/gui_core/treeview.py index 4c20885599c..b5ee50a7822 100644 --- a/gui/wxpython/gui_core/treeview.py +++ b/gui/wxpython/gui_core/treeview.py @@ -303,17 +303,19 @@ def main(): root = tree.root n1 = tree.AppendNode(parent=root, data={"label": "node1"}) n2 = tree.AppendNode(parent=root, data={"label": "node2"}) - n3 = tree.AppendNode(parent=root, data={"label": "node3"}) # pylint: disable=W0612 + n3 = tree.AppendNode( # noqa: F841 # pylint: disable=W0612 + parent=root, data={"label": "node3"} + ) n11 = tree.AppendNode(parent=n1, data={"label": "node11", "xxx": "A"}) - n12 = tree.AppendNode( + n12 = tree.AppendNode( # noqa: F841 # pylint: disable=W0612 parent=n1, data={"label": "node12", "xxx": "B"} - ) # pylint: disable=W0612 - n21 = tree.AppendNode( + ) + n21 = tree.AppendNode( # noqa: F841 # pylint: disable=W0612 parent=n2, data={"label": "node21", "xxx": "A"} - ) # pylint: disable=W0612 - n111 = tree.AppendNode( + ) + n111 = tree.AppendNode( # noqa: F841 # pylint: disable=W0612 parent=n11, data={"label": "node111", "xxx": "A"} - ) # pylint: disable=W0612 + ) app = wx.App() frame = TreeFrame(model=tree) diff --git a/gui/wxpython/gui_core/widgets.py b/gui/wxpython/gui_core/widgets.py index 035c70e6fbd..0ec50553114 100644 --- a/gui/wxpython/gui_core/widgets.py +++ b/gui/wxpython/gui_core/widgets.py @@ -170,7 +170,7 @@ def InsertPage(self, *args, **kwargs): self.classObject.InsertPage(self.widget, *args, **kwargs) except ( TypeError - ) as e: # documentation says 'index', but certain versions of wx require 'n' + ): # documentation says 'index', but certain versions of wx require 'n' kwargs["n"] = kwargs["index"] del kwargs["index"] self.classObject.InsertPage(self.widget, *args, **kwargs) From dc27b28b4142504ac69a4a17cd4a1673ebbdbf4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Wed, 18 Sep 2024 19:06:08 -0400 Subject: [PATCH 245/471] grass.gunittest: Fix SIM115 using context managers to open files (#4338) * grass.gunittest: Use a context manager for opening htmldiff_file * grass.gunittest: Specify encoding for writing htmldiff_file * grass.gunittest: Write all lines of htmldiff_file at once * grass.gunittest: Use Path.write_text() to write htmldiff_file * grass.gunittest: Use a context manager for opening actual and reference files (SIM115) * grass.gunittest: Use a context manager for output StringIO in case.py * grass.gunittest: Use a context manager for opening files (SIM115) In function replace_in_file of reporters.py * grass.gunittest: Use a context manager for opening files (SIM115) In function wrap_stdstream_to_html of reporters.py * grass.gunittest: Use a context manager for opening files (SIM115) In function report_for_dirs of reporters.py. Reordered so string creation ends up together outside the context manger where the file is written. * grass.gunittest: Use a context manager for opening files (SIM115) In function end_file_test of class GrassTestFilesHtmlReporter of reporters.py. Reordered so string creation ends up together outside the context manger where the strings are written to the file. * grass.gunittest: Use a context manager for opening files (SIM115) In function report_for_dir of class TestsuiteDirReporter of reporters.py * grass.gunittest: Ignore remaining two SIM115 in reporters.py * grass.gunittest: Use a context manager for opening files in multireport (SIM115) * style: Enable checking of SIM115 --- pyproject.toml | 1 - python/grass/gunittest/case.py | 28 +-- python/grass/gunittest/multireport.py | 280 +++++++++++++------------- python/grass/gunittest/reporters.py | 277 ++++++++++++------------- 4 files changed, 300 insertions(+), 286 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index b5c7a478864..992e48391ad 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -324,7 +324,6 @@ ignore = [ "python/grass/__init__.py" = ["PYI056"] "python/grass/exp*/tests/grass_script_mapset_session_test.py" = ["SIM117"] "python/grass/exp*/tests/grass_script_tmp_mapset_session_test.py" = ["SIM117"] -"python/grass/gunittest/*.py" = ["SIM115"] "python/grass/gunittest/loader.py" = ["PYI024"] "python/grass/gunittest/multireport.py" = ["PYI024"] "python/grass/gunittest/testsu*/d*/s*/s*/subsub*/t*/test_segfaut.py" = ["B018"] diff --git a/python/grass/gunittest/case.py b/python/grass/gunittest/case.py index 4d1b37bdfed..da2c7486d16 100644 --- a/python/grass/gunittest/case.py +++ b/python/grass/gunittest/case.py @@ -10,6 +10,7 @@ """ import os +from pathlib import Path import shutil import subprocess import hashlib @@ -1227,8 +1228,9 @@ def assertVectorAsciiEqualsVectorAscii( """ import difflib - fromlines = open(actual).readlines() - tolines = open(reference).readlines() + with open(actual) as f1, open(reference) as f2: + fromlines = f1.readlines() + tolines = f2.readlines() context_lines = 3 # number of context lines # TODO: filenames are set to "actual" and "reference", isn't it too general? # it is even more useful if map names or file names are some generated @@ -1252,18 +1254,18 @@ def assertVectorAsciiEqualsVectorAscii( os.remove(reference) stdmsg = "There is a difference between vectors when compared as ASCII files.\n" - output = StringIO() # TODO: there is a diff size constant which we can use # we are setting it unlimited but we can just set it large maxlines = 100 i = 0 - for line in diff: - if i >= maxlines: - break - output.write(line) - i += 1 - stdmsg += output.getvalue() - output.close() + with StringIO() as output: + for line in diff: + if i >= maxlines: + break + output.write(line) + i += 1 + stdmsg += output.getvalue() + # it seems that there is not better way of asking whether there was # a difference (always a iterator object is returned) if i > 0: @@ -1288,11 +1290,9 @@ def assertVectorAsciiEqualsVectorAscii( "actual", context=True, numlines=context_lines, + charset="utf-8", ) - htmldiff_file = open(htmldiff_file_name, "w") - for line in htmldiff: - htmldiff_file.write(line) - htmldiff_file.close() + Path(htmldiff_file_name).write_text(htmldiff, encoding="utf-8") self.fail(self._formatMessage(msg, stdmsg)) diff --git a/python/grass/gunittest/multireport.py b/python/grass/gunittest/multireport.py index edeb98636ee..f183d4e0219 100644 --- a/python/grass/gunittest/multireport.py +++ b/python/grass/gunittest/multireport.py @@ -16,6 +16,7 @@ import datetime import operator from collections import defaultdict, namedtuple +from pathlib import Path from grass.gunittest.checkers import text_to_keyvalue from grass.gunittest.utils import ensure_dir @@ -472,7 +473,7 @@ def main(): # skipping incomplete reports # use only results list for further processing continue - summary = text_to_keyvalue(open(summary_file).read(), sep="=") + summary = text_to_keyvalue(Path(summary_file).read_text(), sep="=") if use_timestamps: test_timestamp = datetime.datetime.fromtimestamp( os.path.getmtime(summary_file) @@ -516,147 +517,156 @@ def main(): except KeyError as e: print("File %s does not have right values (%s)" % (report, e.message)) - locations_main_page = open(os.path.join(output, "index.html"), "w") - locations_main_page.write( - "" - "

Test reports grouped by location type

" - "" - "" - "" - "" - "" - "" - ) + with open(os.path.join(output, "index.html"), "w") as locations_main_page: + locations_main_page.write( + "" + "

Test reports grouped by location type

" + "
LocationSuccessful filesSuccessful tests
" + "" + "" + "" + "" + "" + ) - PlotStyle = namedtuple( - "PlotStyle", - ["linestyle", "linewidth", "success_color", "fail_color", "total_color"], - ) - plot_style = PlotStyle( - linestyle="-", linewidth=4.0, success_color="g", fail_color="r", total_color="b" - ) + PlotStyle = namedtuple( + "PlotStyle", + ["linestyle", "linewidth", "success_color", "fail_color", "total_color"], + ) + plot_style = PlotStyle( + linestyle="-", + linewidth=4.0, + success_color="g", + fail_color="r", + total_color="b", + ) - for location_type, results in results_in_locations.items(): - results = sorted(results, key=operator.attrgetter("timestamp")) - # TODO: document: location type must be a valid dir name - directory = os.path.join(output, location_type) - ensure_dir(directory) + for location_type, results in results_in_locations.items(): + results = sorted(results, key=operator.attrgetter("timestamp")) + # TODO: document: location type must be a valid dir name + directory = os.path.join(output, location_type) + ensure_dir(directory) - if location_type == "unknown": - title = "Test reports" - else: - title = "Test reports for <{type}> location type".format( - type=location_type + if location_type == "unknown": + title = "Test reports" + else: + title = "Test reports for <{type}> location type".format( + type=location_type + ) + + x = [date2num(result.timestamp) for result in results] + # the following would be an alternative but it does not work with + # labels and automatic axis limits even after removing another date fun + # x = [result.svn_revision for result in results] + xlabels = [ + result.timestamp.strftime("%Y-%m-%d") + + " (r" + + result.svn_revision + + ")" + for result in results + ] + step = len(x) / 10 + xticks = x[step::step] + xlabels = xlabels[step::step] + tests_successful_plot( + x=x, + xticks=xticks, + xlabels=xlabels, + results=results, + filename=os.path.join(directory, "tests_successful_plot.png"), + style=plot_style, + ) + files_successful_plot( + x=x, + xticks=xticks, + xlabels=xlabels, + results=results, + filename=os.path.join(directory, "files_successful_plot.png"), + style=plot_style, + ) + tests_plot( + x=x, + xticks=xticks, + xlabels=xlabels, + results=results, + filename=os.path.join(directory, "tests_plot.png"), + style=plot_style, + ) + tests_percent_plot( + x=x, + xticks=xticks, + xlabels=xlabels, + results=results, + filename=os.path.join(directory, "tests_percent_plot.png"), + style=plot_style, + ) + files_plot( + x=x, + xticks=xticks, + xlabels=xlabels, + results=results, + filename=os.path.join(directory, "files_plot.png"), + style=plot_style, + ) + files_percent_plot( + x=x, + xticks=xticks, + xlabels=xlabels, + results=results, + filename=os.path.join(directory, "files_percent_plot.png"), + style=plot_style, + ) + info_plot( + x=x, + xticks=xticks, + xlabels=xlabels, + results=results, + filename=os.path.join(directory, "info_plot.png"), + style=plot_style, ) - x = [date2num(result.timestamp) for result in results] - # the following would be an alternative but it does not work with - # labels and automatic axis limits even after removing another date fun - # x = [result.svn_revision for result in results] - xlabels = [ - result.timestamp.strftime("%Y-%m-%d") + " (r" + result.svn_revision + ")" - for result in results - ] - step = len(x) / 10 - xticks = x[step::step] - xlabels = xlabels[step::step] - tests_successful_plot( - x=x, - xticks=xticks, - xlabels=xlabels, - results=results, - filename=os.path.join(directory, "tests_successful_plot.png"), - style=plot_style, - ) - files_successful_plot( - x=x, - xticks=xticks, - xlabels=xlabels, - results=results, - filename=os.path.join(directory, "files_successful_plot.png"), - style=plot_style, - ) - tests_plot( - x=x, - xticks=xticks, - xlabels=xlabels, - results=results, - filename=os.path.join(directory, "tests_plot.png"), - style=plot_style, - ) - tests_percent_plot( - x=x, - xticks=xticks, - xlabels=xlabels, - results=results, - filename=os.path.join(directory, "tests_percent_plot.png"), - style=plot_style, - ) - files_plot( - x=x, - xticks=xticks, - xlabels=xlabels, - results=results, - filename=os.path.join(directory, "files_plot.png"), - style=plot_style, - ) - files_percent_plot( - x=x, - xticks=xticks, - xlabels=xlabels, - results=results, - filename=os.path.join(directory, "files_percent_plot.png"), - style=plot_style, - ) - info_plot( - x=x, - xticks=xticks, - xlabels=xlabels, - results=results, - filename=os.path.join(directory, "info_plot.png"), - style=plot_style, - ) + main_page( + results=results, + filename="index.html", + images=[ + "tests_successful_plot.png", + "files_successful_plot.png", + "tests_plot.png", + "files_plot.png", + "tests_percent_plot.png", + "files_percent_plot.png", + "info_plot.png", + ], + captions=[ + "Success of individual tests in percents", + "Success of test files in percents", + "Successes, failures and number of individual tests", + "Successes, failures and number of test files", + "Successes and failures of individual tests in percent", + "Successes and failures of test files in percents", + "Additional information", + ], + directory=directory, + title=title, + ) - main_page( - results=results, - filename="index.html", - images=[ - "tests_successful_plot.png", - "files_successful_plot.png", - "tests_plot.png", - "files_plot.png", - "tests_percent_plot.png", - "files_percent_plot.png", - "info_plot.png", - ], - captions=[ - "Success of individual tests in percents", - "Success of test files in percents", - "Successes, failures and number of individual tests", - "Successes, failures and number of test files", - "Successes and failures of individual tests in percent", - "Successes and failures of test files in percents", - "Additional information", - ], - directory=directory, - title=title, - ) + files_successes = sum(result.files_successes for result in results) + files_total = sum(result.files_total for result in results) + successes = sum(result.successes for result in results) + total = sum(result.total for result in results) + per_test = success_to_html_percent(total=total, successes=successes) + per_file = success_to_html_percent( + total=files_total, successes=files_successes + ) + locations_main_page.write( + "" + "" + "" + "".format(location=location_type, pfiles=per_file, ptests=per_test) + ) + locations_main_page.write("
LocationSuccessful filesSuccessful tests
{location}{pfiles}{ptests}
") + locations_main_page.write("") - files_successes = sum(result.files_successes for result in results) - files_total = sum(result.files_total for result in results) - successes = sum(result.successes for result in results) - total = sum(result.total for result in results) - per_test = success_to_html_percent(total=total, successes=successes) - per_file = success_to_html_percent(total=files_total, successes=files_successes) - locations_main_page.write( - "" - "{location}" - "{pfiles}{ptests}" - "".format(location=location_type, pfiles=per_file, ptests=per_test) - ) - locations_main_page.write("") - locations_main_page.write("") - locations_main_page.close() return 0 diff --git a/python/grass/gunittest/reporters.py b/python/grass/gunittest/reporters.py index ec175613bc3..650480a234c 100644 --- a/python/grass/gunittest/reporters.py +++ b/python/grass/gunittest/reporters.py @@ -51,12 +51,9 @@ def replace_in_file(file_path, pattern, repl): """ # using tmp file to store the replaced content tmp_file_path = file_path + ".tmp" - old_file = open(file_path, "r") - new_file = open(tmp_file_path, "w") - for line in old_file: - new_file.write(re.sub(pattern=pattern, string=line, repl=repl)) - new_file.close() - old_file.close() + with open(file_path, "r") as old_file, open(tmp_file_path, "w") as new_file: + for line in old_file: + new_file.write(re.sub(pattern=pattern, string=line, repl=repl)) # remove old file since it must not exist for rename/move os.remove(file_path) # replace old file by new file @@ -455,13 +452,11 @@ def percent_to_html(percent): def wrap_stdstream_to_html(infile, outfile, module, stream): before = "

%s

" % (module.name + " " + stream)
     after = "
" - html = open(outfile, "w") - html.write(before) - with open(infile) as text: + with open(outfile, "w") as html, open(infile) as text: + html.write(before) for line in text: html.write(color_error_line(html_escape(line))) - html.write(after) - html.close() + html.write(after) def html_file_preview(filename): @@ -482,7 +477,7 @@ def html_file_preview(filename): elif size < 10 * max_size: def tail(filename, n): - return collections.deque(open(filename), n) + return collections.deque(open(filename), n) # noqa: SIM115 html.write("... (lines omitted)\n") for line in tail(filename, 50): @@ -564,7 +559,8 @@ def start(self, results_dir): super().start(results_dir) # having all variables public although not really part of API main_page_name = os.path.join(results_dir, self._main_page_name) - self.main_index = open(main_page_name, "w") + # TODO: Ensure file is closed in all situations + self.main_index = open(main_page_name, "w") # noqa: SIM115 # TODO: this can be moved to the counter class self.failures = 0 @@ -733,8 +729,7 @@ def end_file_test( ) file_index_path = os.path.join(cwd, "index.html") - file_index = open(file_index_path, "w") - file_index.write( + header = ( '' "

{m.name}

" "

{m.tested_dir} – {m.name}

" @@ -773,7 +768,6 @@ def end_file_test( dur=self.file_time, ) ) - file_index.write(summary_section) modules = test_summary.get("tested_modules", None) if modules: @@ -782,12 +776,6 @@ def end_file_test( # alternatively a link to module test summary if type(modules) is not list: modules = [modules] - file_index.write( - "Tested modules{0}".format( - ", ".join(sorted(set(modules))) - ) - ) - file_index.write("") # here we would have also links to coverage, profiling, ... # '
  • code coverage
  • ' @@ -797,7 +785,6 @@ def end_file_test( '
  • standard output (stdout)
  • ' '
  • standard error output (stderr)
  • ' ) - file_index.write(files_section) supplementary_files = test_summary.get("supplementary_files", None) if supplementary_files: @@ -809,17 +796,31 @@ def end_file_test( # moreover something can be shared with other explicitly # using constructors as seems advantageous for counting self._file_anonymizer.anonymize(supplementary_files) - for f in supplementary_files: - file_index.write('
  • {f}
  • '.format(f=f)) - file_index.write("") + with open(file_index_path, "w") as file_index: + file_index.write(header) + file_index.write(summary_section) + if modules: + file_index.write( + "Tested modules{0}".format( + ", ".join(sorted(set(modules))) + ) + ) + file_index.write("") + + file_index.write(files_section) - if returncode: - file_index.write("

    Standard error output (stderr)

    ") - file_index.write(html_file_preview(stderr)) + if supplementary_files: + for f in supplementary_files: + file_index.write('
  • {f}
  • '.format(f=f)) + + file_index.write("") - file_index.write("") - file_index.close() + if returncode: + file_index.write("

    Standard error output (stderr)

    ") + file_index.write(html_file_preview(stderr)) + + file_index.write("") if returncode: pass @@ -1093,7 +1094,7 @@ def report_for_dir(self, root, directory, test_files): os.path.join(root, directory) ) == os.path.abspath(root): page_name = os.path.join(root, self.top_level_testsuite_page_name) - page = open(page_name, "w") + # TODO: should we use forward slashes also for the HTML because # it is simpler are more consistent with the rest on MS Windows? head = "

    {name} testsuite results

    ".format(name=directory) @@ -1106,106 +1107,108 @@ def report_for_dir(self, root, directory, test_files): "FailedPercent successful" "" ) - page.write(head) - page.write(tests_table_head) - for test_file_name in test_files: - # TODO: put keyvalue fine name to constant - summary_filename = os.path.join( - root, directory, test_file_name, "test_keyvalue_result.txt" - ) - # if os.path.exists(summary_filename): - summary = text_to_keyvalue(Path(summary_filename).read_text(), sep="=") - # else: - # TODO: write else here - # summary = None - - if "total" not in summary: - bad_ones = successes = UNKNOWN_NUMBER_HTML - total = None - else: - bad_ones = summary["failures"] + summary["errors"] - successes = summary["successes"] - total = summary["total"] - - self.failures += summary["failures"] - self.errors += summary["errors"] - self.skipped += summary["skipped"] - self.successes += summary["successes"] - self.expected_failures += summary["expected_failures"] - self.unexpected_successes += summary["unexpected_successes"] - self.total += summary["total"] - - dir_failures += summary["failures"] - dir_errors += summary["failures"] - dir_skipped += summary["skipped"] - dir_successes += summary["successes"] - dir_expected_failures += summary["expected_failures"] - dir_unexpected_success += summary["unexpected_successes"] - dir_total += summary["total"] - - # TODO: keyvalue method should have types for keys function - # perhaps just the current post processing function is enough - test_file_authors = summary.get("test_file_authors") - if not test_file_authors: - test_file_authors = [] - if type(test_file_authors) is not list: - test_file_authors = [test_file_authors] - test_files_authors.extend(test_file_authors) - - file_total += 1 - # Use non-zero return code in case it is missing. - # (This can happen when the test has timed out.) - return_code = summary.get("returncode", 1) - file_successes += 0 if return_code else 1 + with open(page_name, "w") as page: + page.write(head) + page.write(tests_table_head) + for test_file_name in test_files: + # TODO: put keyvalue fine name to constant + summary_filename = os.path.join( + root, directory, test_file_name, "test_keyvalue_result.txt" + ) + # if os.path.exists(summary_filename): + summary = text_to_keyvalue(Path(summary_filename).read_text(), sep="=") + # else: + # TODO: write else here + # summary = None + + if "total" not in summary: + bad_ones = successes = UNKNOWN_NUMBER_HTML + total = None + else: + bad_ones = summary["failures"] + summary["errors"] + successes = summary["successes"] + total = summary["total"] + + self.failures += summary["failures"] + self.errors += summary["errors"] + self.skipped += summary["skipped"] + self.successes += summary["successes"] + self.expected_failures += summary["expected_failures"] + self.unexpected_successes += summary["unexpected_successes"] + self.total += summary["total"] + + dir_failures += summary["failures"] + dir_errors += summary["failures"] + dir_skipped += summary["skipped"] + dir_successes += summary["successes"] + dir_expected_failures += summary["expected_failures"] + dir_unexpected_success += summary["unexpected_successes"] + dir_total += summary["total"] + + # TODO: keyvalue method should have types for keys function + # perhaps just the current post processing function is enough + test_file_authors = summary.get("test_file_authors") + if not test_file_authors: + test_file_authors = [] + if type(test_file_authors) is not list: + test_file_authors = [test_file_authors] + test_files_authors.extend(test_file_authors) + + file_total += 1 + # Use non-zero return code in case it is missing. + # (This can happen when the test has timed out.) + return_code = summary.get("returncode", 1) + file_successes += 0 if return_code else 1 + + pass_per = success_to_html_percent(total=total, successes=successes) + row = ( + "" + '{f}' + "{status}" + "{ntests}{stests}" + "{ftests}{ptests}" + "".format( + f=test_file_name, + status=returncode_to_html_text(return_code), + stests=successes, + ftests=bad_ones, + ntests=total, + ptests=pass_per, + ) + ) + page.write(row) - pass_per = success_to_html_percent(total=total, successes=successes) - row = ( - "" - '{f}' + self.testsuites += 1 + self.testsuites_successes += 1 if file_successes == file_total else 0 + self.files += file_total + self.files_successes += file_successes + + dir_pass_per = success_to_html_percent( + total=dir_total, successes=dir_successes + ) + file_pass_per = success_to_html_percent( + total=file_total, successes=file_successes + ) + tests_table_foot = ( + "" + "Summary" "{status}" "{ntests}{stests}" "{ftests}{ptests}" - "".format( - f=test_file_name, - status=returncode_to_html_text(return_code), - stests=successes, - ftests=bad_ones, - ntests=total, - ptests=pass_per, + "".format( + status=file_pass_per, + stests=dir_successes, + ftests=dir_failures + dir_errors, + ntests=dir_total, + ptests=dir_pass_per, ) ) - page.write(row) - - self.testsuites += 1 - self.testsuites_successes += 1 if file_successes == file_total else 0 - self.files += file_total - self.files_successes += file_successes - - dir_pass_per = success_to_html_percent(total=dir_total, successes=dir_successes) - file_pass_per = success_to_html_percent( - total=file_total, successes=file_successes - ) - tests_table_foot = ( - "" - "Summary" - "{status}" - "{ntests}{stests}" - "{ftests}{ptests}" - "".format( - status=file_pass_per, - stests=dir_successes, - ftests=dir_failures + dir_errors, - ntests=dir_total, - ptests=dir_pass_per, + page.write(tests_table_foot) + test_authors = get_html_test_authors_table( + directory=directory, tests_authors=test_files_authors ) - ) - page.write(tests_table_foot) - test_authors = get_html_test_authors_table( - directory=directory, tests_authors=test_files_authors - ) - page.write(test_authors) - page.write("") - page.close() + page.write(test_authors) + page.write("") status = success_to_html_text(total=file_total, successes=file_successes) return ( @@ -1233,7 +1236,6 @@ def report_for_dirs(self, root, directories): # absolute/relative paths page_name = os.path.join(root, self.main_page_name) - page = open(page_name, "w") head = "

    Testsuites results

    " tests_table_head = ( "" @@ -1246,14 +1248,6 @@ def report_for_dirs(self, root, directories): "" "" ) - page.write(head) - page.write(tests_table_head) - - for directory, test_files in directories.items(): - row = self.report_for_dir( - root=root, directory=directory, test_files=test_files - ) - page.write(row) pass_per = success_to_html_percent(total=self.total, successes=self.successes) file_pass_per = success_to_html_percent( @@ -1281,5 +1275,16 @@ def report_for_dirs(self, root, directories): ptests=pass_per, ) ) - page.write(tests_table_foot) - page.write("") + + with open(page_name, "w") as page: + page.write(head) + page.write(tests_table_head) + + for directory, test_files in directories.items(): + row = self.report_for_dir( + root=root, directory=directory, test_files=test_files + ) + page.write(row) + + page.write(tests_table_foot) + page.write("") From b69cacf7ab1737a823148e537099a120b0b33b01 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 19 Sep 2024 00:00:14 +0000 Subject: [PATCH 246/471] CI(deps): Update peter-evans/create-pull-request action to v7.0.5 (#4339) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/periodic_update.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/periodic_update.yml b/.github/workflows/periodic_update.yml index 7b8c08b5b70..bea3b53af4a 100644 --- a/.github/workflows/periodic_update.yml +++ b/.github/workflows/periodic_update.yml @@ -33,7 +33,7 @@ jobs: run: git status --ignored - name: Create Pull Request id: cpr - uses: peter-evans/create-pull-request@6cd32fd93684475c31847837f87bb135d40a2b79 # v7.0.3 + uses: peter-evans/create-pull-request@5e914681df9dc83aa4e4905692ca88beb2f9e91f # v7.0.5 with: commit-message: "config.guess + config.sub: updated from http://git.savannah.gnu.org/cgit/config.git/plain/" branch: periodic/update-configure From 05584c1fbe3012debcefa66b6f27b83a2254b2ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Wed, 18 Sep 2024 21:02:01 -0400 Subject: [PATCH 247/471] CI(OSGeo4W): Update min-success to 86% for gunittest on Windows (#4341) --- .github/workflows/test_thorough.bat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test_thorough.bat b/.github/workflows/test_thorough.bat index 963f24b9b43..40df9534a20 100644 --- a/.github/workflows/test_thorough.bat +++ b/.github/workflows/test_thorough.bat @@ -2,4 +2,4 @@ set grass=%1 set python=%2 call %grass% --tmp-project XY --exec g.download.project url=https://grass.osgeo.org/sampledata/north_carolina/nc_spm_full_v2alpha2.tar.gz path=%USERPROFILE% -call %grass% --tmp-project XY --exec %python% -m grass.gunittest.main --grassdata %USERPROFILE% --location nc_spm_full_v2alpha2 --location-type nc --min-success 80 +call %grass% --tmp-project XY --exec %python% -m grass.gunittest.main --grassdata %USERPROFILE% --location nc_spm_full_v2alpha2 --location-type nc --min-success 86 From 7dc46cdadd53399c566cf0a496632c80ea03db81 Mon Sep 17 00:00:00 2001 From: Mohan Yelugoti Date: Wed, 18 Sep 2024 21:06:20 -0400 Subject: [PATCH 248/471] v.in.dwg: Retire v.in.dwg infavor of v.in.redwg (#4329) v.in.dwg is linked to proprietary library 'OpenDWG toolkit'. One needs to become a member of OpenDWG Alliance to get the needed libraries and headers in order to compile this tool. However, OpenDWG webpage went offline around 2011 and we have 'v.in.redwg' as replacement for it (available in grass-addons). Hence retire 'v.in.dwg' tool. Besides the tool, this completely removes OpenDWG from configure.ac and platform. Signed-off-by: Mohan Yelugoti --- configure | 151 ---------- configure.ac | 43 --- include/Make/Platform.make.in | 6 - vector/Makefile | 1 - vector/v.in.dwg/Makefile | 31 -- vector/v.in.dwg/README | 23 -- vector/v.in.dwg/WARNING | 7 - vector/v.in.dwg/entity.c | 540 ---------------------------------- vector/v.in.dwg/global.h | 59 ---- vector/v.in.dwg/main.c | 295 ------------------- vector/v.in.dwg/v.in.dwg.html | 32 -- 11 files changed, 1188 deletions(-) delete mode 100644 vector/v.in.dwg/Makefile delete mode 100644 vector/v.in.dwg/README delete mode 100644 vector/v.in.dwg/WARNING delete mode 100644 vector/v.in.dwg/entity.c delete mode 100644 vector/v.in.dwg/global.h delete mode 100644 vector/v.in.dwg/main.c delete mode 100644 vector/v.in.dwg/v.in.dwg.html diff --git a/configure b/configure index bc60ae7c41c..4b9a63f7947 100755 --- a/configure +++ b/configure @@ -666,10 +666,6 @@ USE_PTHREAD PTHREADLIB PTHREADLIBPATH PTHREADINCPATH -USE_OPENDWG -OPENDWGLIB -OPENDWGLIBPATH -OPENDWGINCPATH HAVE_NLS INTLLIB FTLIB @@ -912,7 +908,6 @@ with_cairo with_freetype with_nls with_readline -with_opendwg with_regex with_pthread with_openmp @@ -964,8 +959,6 @@ with_freetype_libs with_proj_includes with_proj_libs with_proj_share -with_opendwg_includes -with_opendwg_libs with_regex_includes with_regex_libs with_pthread_includes @@ -1647,7 +1640,6 @@ Optional Packages: --with-freetype support FreeType functionality (default: yes) --with-nls support NLS functionality (default: no) --with-readline support Readline functionality (default: no) - --with-opendwg support openDWG functionality (default: no) --with-regex support regex functionality (default: yes) --with-pthread support POSIX threads functionality (default: no) --with-openmp support OpenMP functionality (default: no) @@ -1734,10 +1726,6 @@ Optional Packages: External PROJ.4 include files are in DIRS --with-proj-libs=DIRS External PROJ.4 library files are in DIRS --with-proj-share=DIR External PROJ.4 data files are in DIR - --with-opendwg-includes=DIRS - openDWG include files are in DIRS - --with-opendwg-libs=DIRS - openDWG library files are in DIRS --with-regex-includes=DIRS regex include files are in DIRS --with-regex-libs=DIRS regex library files are in DIRS @@ -5566,17 +5554,6 @@ fi -# Check whether --with-opendwg was given. -if test ${with_opendwg+y} -then : - withval=$with_opendwg; -else $as_nop - with_opendwg=no -fi - - - - # Check whether --with-regex was given. if test ${with_regex+y} then : @@ -6069,25 +6046,6 @@ fi -# Check whether --with-opendwg-includes was given. -if test ${with_opendwg_includes+y} -then : - withval=$with_opendwg_includes; -fi - - - - -# Check whether --with-opendwg-libs was given. -if test ${with_opendwg_libs+y} -then : - withval=$with_opendwg_libs; -fi - - - - - # Check whether --with-regex-includes was given. if test ${with_regex_includes+y} then : @@ -15207,113 +15165,6 @@ fi -# Enable openDWG option - - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether to use openDWG" >&5 -printf %s "checking whether to use openDWG... " >&6; } -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: \"$with_opendwg\"" >&5 -printf "%s\n" "\"$with_opendwg\"" >&6; } -case "$with_opendwg" in - "no") USE_OPENDWG= ;; - "yes") USE_OPENDWG="1" ;; - *) as_fn_error $? "*** You must answer yes or no." "$LINENO" 5 ;; -esac - - - -OPENDWGINCPATH= -OPENDWGLIBPATH= -OPENDWGLIB= - -if test -n "${USE_OPENDWG}"; then - -# With OPENDWG includes directory - - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for location of openDGW includes" >&5 -printf %s "checking for location of openDGW includes... " >&6; } -case "$with_opendwg_includes" in -y | ye | yes | n | no) - as_fn_error $? "*** You must supply a directory to --with-opendwg-includes." "$LINENO" 5 - ;; -esac -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $with_opendwg_includes" >&5 -printf "%s\n" "$with_opendwg_includes" >&6; } - -if test -n "$with_opendwg_includes" ; then - for dir in $with_opendwg_includes; do - if test -d "$dir"; then - OPENDWGINCPATH="$OPENDWGINCPATH -I$dir" - else - as_fn_error $? "*** openDGW includes directory $dir does not exist." "$LINENO" 5 - fi - done -fi - - - -ac_save_cppflags="$CPPFLAGS" -CPPFLAGS="$OPENDWGINCPATH $CPPFLAGS" - for ac_header in ad2.h -do : - ac_fn_c_check_header_compile "$LINENO" "ad2.h" "ac_cv_header_ad2_h" "$ac_includes_default" -if test "x$ac_cv_header_ad2_h" = xyes -then : - printf "%s\n" "#define HAVE_AD2_H 1" >>confdefs.h - -else $as_nop - - as_fn_error $? "*** Unable to locate openDWG includes." "$LINENO" 5 - -fi - -done -CPPFLAGS=$ac_save_cppflags - - -# With OPENDWG library directory - - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for location of openDWG library" >&5 -printf %s "checking for location of openDWG library... " >&6; } -case "$with_opendwg_libs" in -y | ye | yes | n | no) - as_fn_error $? "*** You must supply a directory to --with-opendwg-libs." "$LINENO" 5 - ;; -esac -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $with_opendwg_libs" >&5 -printf "%s\n" "$with_opendwg_libs" >&6; } - -if test -n "$with_opendwg_libs"; then - for dir in $with_opendwg_libs; do - if test -d "$dir"; then - OPENDWGLIBPATH="$OPENDWGLIBPATH -L$dir" - else - as_fn_error $? "*** openDWG library directory $dir does not exist." "$LINENO" 5 - fi - done -fi - - -#search for ad2.a, ad3.a ... in openDWG toolkit directory: -#FIX ME how to program this test?? -#LOC_CHECK_LIBS(ad?.a,adSeekLayer,openDWG,$OPENDWGLIBPATH,OPENDWGLIB,,) -# -#for now hack (but working): -TRUEOPENDWGLIBPATH=`echo "$OPENDWGLIBPATH" | cut -b3-` -adlib=`ls -1 "$TRUEOPENDWGLIBPATH"/ad?.a | tail -1` -OPENDWGLIB="$adlib" - -fi # $USE_OPENDWG - - - - - - -# Done checking OPENDWG - # Enable pthread option @@ -17688,8 +17539,6 @@ echo " C++ support: `if test -n "${USE_CXX}" ; then echo yes ; e echo " Cairo support: `if test -n "${USE_CAIRO}" ; then echo yes ; else echo no ; fi`" -echo " DWG support: `if test -n "${USE_OPENDWG}" ; then echo yes ; else echo no ; fi`" - echo " FFTW support: `if test -n "${USE_FFTW}" ; then echo yes ; else echo no ; fi`" echo " FreeType support: `if test -n "${USE_FREETYPE}" ; then echo yes ; else echo no ; fi`" diff --git a/configure.ac b/configure.ac index b38a2a5936c..00403f1e665 100644 --- a/configure.ac +++ b/configure.ac @@ -312,7 +312,6 @@ LOC_ARG_WITH(cairo, Cairo) LOC_ARG_WITH(freetype, FreeType) LOC_ARG_WITH(nls, NLS, no) LOC_ARG_WITH(readline, Readline, no) -LOC_ARG_WITH(opendwg, openDWG, no) LOC_ARG_WITH(regex, regex) LOC_ARG_WITH(pthread, POSIX threads, no) LOC_ARG_WITH(openmp, OpenMP, no) @@ -411,9 +410,6 @@ LOC_ARG_WITH_INC(proj, External PROJ.4) LOC_ARG_WITH_LIB(proj, External PROJ.4) LOC_ARG_WITH_SHARE(proj, External PROJ.4) -LOC_ARG_WITH_INC(opendwg, openDWG) -LOC_ARG_WITH_LIB(opendwg, openDWG) - LOC_ARG_WITH_INC(regex, regex) LOC_ARG_WITH_LIB(regex, regex) @@ -1860,44 +1856,6 @@ fi AC_SUBST(INTLLIB) AC_SUBST(HAVE_NLS) -# Enable openDWG option - -LOC_CHECK_USE(opendwg,openDWG,USE_OPENDWG) - -OPENDWGINCPATH= -OPENDWGLIBPATH= -OPENDWGLIB= - -if test -n "${USE_OPENDWG}"; then - -# With OPENDWG includes directory - -LOC_CHECK_INC_PATH(opendwg,openDGW,OPENDWGINCPATH) - -LOC_CHECK_INCLUDES(ad2.h,openDWG,$OPENDWGINCPATH) - -# With OPENDWG library directory - -LOC_CHECK_LIB_PATH(opendwg,openDWG,OPENDWGLIBPATH) - -#search for ad2.a, ad3.a ... in openDWG toolkit directory: -#FIX ME how to program this test?? -#LOC_CHECK_LIBS(ad?.a,adSeekLayer,openDWG,$OPENDWGLIBPATH,OPENDWGLIB,,) -# -#for now hack (but working): -TRUEOPENDWGLIBPATH=`echo "$OPENDWGLIBPATH" | cut -b3-` -adlib=`ls -1 "$TRUEOPENDWGLIBPATH"/ad?.a | tail -1` -OPENDWGLIB="$adlib" - -fi # $USE_OPENDWG - -AC_SUBST(OPENDWGINCPATH) -AC_SUBST(OPENDWGLIBPATH) -AC_SUBST(OPENDWGLIB) -AC_SUBST(USE_OPENDWG) - -# Done checking OPENDWG - # Enable pthread option LOC_CHECK_USE(pthread,POSIX threads,USE_PTHREAD) @@ -2072,7 +2030,6 @@ LOC_MSG_USE(BLAS support,USE_BLAS) LOC_MSG_USE(BZIP2 support,USE_BZIP2) LOC_MSG_USE(C++ support,USE_CXX) LOC_MSG_USE(Cairo support,USE_CAIRO) -LOC_MSG_USE(DWG support,USE_OPENDWG) LOC_MSG_USE(FFTW support,USE_FFTW) LOC_MSG_USE(FreeType support,USE_FREETYPE) LOC_MSG_USE(GDAL support,USE_GDAL) diff --git a/include/Make/Platform.make.in b/include/Make/Platform.make.in index c65ed28c686..a66ddf801d3 100644 --- a/include/Make/Platform.make.in +++ b/include/Make/Platform.make.in @@ -203,12 +203,6 @@ PROJINC = @PROJINC@ PROJLIB = @PROJLIB@ PROJSHARE = @PROJSHARE@ -#OPENDWG: -OPENDWGINCPATH = @OPENDWGINCPATH@ -OPENDWGLIBPATH = @OPENDWGLIBPATH@ -OPENDWGLIB = @OPENDWGLIB@ -USE_OPENDWG = @USE_OPENDWG@ - #cairo CAIROINC = @CAIROINC@ CAIROLIB = @CAIROLIB@ diff --git a/vector/Makefile b/vector/Makefile index f195ce3aafb..7244ac59af5 100644 --- a/vector/Makefile +++ b/vector/Makefile @@ -102,7 +102,6 @@ SUBDIRS = \ v.out.ogr \ v.in.ogr \ v.external \ - v.in.dwg \ v.in.lidar \ v.external.out diff --git a/vector/v.in.dwg/Makefile b/vector/v.in.dwg/Makefile deleted file mode 100644 index c5c30c1dde7..00000000000 --- a/vector/v.in.dwg/Makefile +++ /dev/null @@ -1,31 +0,0 @@ -MODULE_TOPDIR = ../.. - -PGM=v.in.dwg -ADINIT = $(ETC)/adinit.dat -ADINITSRC := $(shell echo $(OPENDWGINCPATH) | cut -b3- )/adinit/adinit.dat - -DEPENDENCIES = $(VECTORDEP) $(DBMIDEP) $(GISDEP) -LIBES = $(VECTORLIB) $(DBMILIB) $(GISLIB) $(OPENDWGLIBPATH) $(OPENDWGLIB) -EXTRA_INC = $(VECT_INC) $(OPENDWGINCPATH) -EXTRA_CFLAGS = $(VECT_CFLAGS) - -include $(MODULE_TOPDIR)/include/Make/Module.make - -ifneq ($(USE_OPENDWG),) -default: check - $(MAKE) cmd $(ADINIT) -endif - -check: -ifneq ($(strip $(MINGW)),) - cat WARNING -else - cat WARNING >/dev/tty -endif - @read IN ." diff --git a/vector/v.in.dwg/entity.c b/vector/v.in.dwg/entity.c deleted file mode 100644 index c70bc308fe0..00000000000 --- a/vector/v.in.dwg/entity.c +++ /dev/null @@ -1,540 +0,0 @@ -/* ************************************************************** - * - * MODULE: v.in.dwg - * - * AUTHOR(S): Radim Blazek - * - * PURPOSE: Import of DWG/DXF files - * - * COPYRIGHT: (C) 2001 by the GRASS Development Team - * - * This program is free software under the - * GNU General Public License (>=v2). - * Read the file COPYING that comes with GRASS - * for details. - * - * In addition, as a special exception, Radim Blazek gives permission - * to link the code of this program with the OpenDWG libraries (or with - * modified versions of the OpenDWG libraries that use the same license - * as OpenDWG libraries), and distribute linked combinations including the two. - * You must obey the GNU General Public License in all respects for all - * of the code used other than. If you modify this file, you may extend - * this exception to your version of the file, but you are not obligated - * to do so. If you do not wish to do so, delete this exception statement - * from your version. - * - * **************************************************************/ - -/* Documentation: - * http://www.opendwg.org - * -> OpenDWG Toolkit Reference - * - * Unsupported entities must be added in wrentity() - * - * TODO: 3rd dimension is not functional for CIRCLE and ARC - * -> required updated of transformation in INSERT - * (how to do that??) - */ - -#define AD_PROTOTYPES -#define AD_VM_PC - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "ad2.h" -#include "global.h" - -#define exampleprintf printf -#define LOCPI M_PI - -char buf[1000]; -char buf2[1000]; - -void getEntTypeName(PAD_ENT_HDR adenhd, char *name) -{ - switch (adenhd->enttype) { - case AD_ENT_LINE: - strcpy(name, "LINE"); - break; - case AD_ENT_POINT: - strcpy(name, "POINT"); - break; - case AD_ENT_CIRCLE: - strcpy(name, "CIRCLE"); - break; - case AD_ENT_SHAPE: - strcpy(name, "SHAPE"); - break; - case AD_ENT_ELLIPSE: - strcpy(name, "ELLIPSE"); - break; - case AD_ENT_SPLINE: - strcpy(name, "SPLINE"); - break; - case AD_ENT_TEXT: - strcpy(name, "TEXT"); - break; - case AD_ENT_ARC: - strcpy(name, "ARC"); - break; - case AD_ENT_TRACE: - strcpy(name, "TRACE"); - break; - case AD_ENT_SOLID: - strcpy(name, "SOLID"); - break; - case AD_ENT_BLOCK: - strcpy(name, "BLOCK"); - break; - case AD_ENT_ENDBLK: - strcpy(name, "ENDBLK"); - break; - case AD_ENT_INSERT: - strcpy(name, "INSERT"); - break; - case AD_ENT_ATTDEF: - strcpy(name, "ATTDEF"); - break; - case AD_ENT_ATTRIB: - strcpy(name, "ATTRIB"); - break; - case AD_ENT_SEQEND: - strcpy(name, "SEQEND"); - break; - case AD_ENT_POLYLINE: - strcpy(name, "POLYLINE"); - break; - case AD_ENT_VERTEX: - strcpy(name, "VERTEX"); - break; - case AD_ENT_LINE3D: - strcpy(name, "3DLINE"); - break; - case AD_ENT_FACE3D: - strcpy(name, "3DFACE"); - break; - case AD_ENT_DIMENSION: - strcpy(name, "DIMENSION"); - break; - case AD_ENT_VIEWPORT: - strcpy(name, "VIEWPORT"); - break; - case AD_ENT_SOLID3D: - strcpy(name, "SOLID3D"); - break; - case AD_ENT_RAY: - strcpy(name, "RAY"); - break; - case AD_ENT_XLINE: - strcpy(name, "XLINE"); - break; - case AD_ENT_MTEXT: - strcpy(name, "MTEXT"); - break; - case AD_ENT_LEADER: - strcpy(name, "LEADER"); - break; - case AD_ENT_TOLERANCE: - strcpy(name, "TOLERANCE"); - break; - case AD_ENT_MLINE: - strcpy(name, "MLINE"); - break; - case AD_ENT_BODY: - strcpy(name, "BODY"); - break; - case AD_ENT_REGION: - strcpy(name, "REGION"); - break; - default: - if (adenhd->enttype == adOle2frameEnttype(dwghandle)) - strcpy(name, "OLE2FRAME"); - else if (adenhd->enttype == adLwplineEnttype(dwghandle)) - strcpy(name, "LWPOLYLINE"); - else if (adenhd->enttype == adHatchEnttype(dwghandle)) - strcpy(name, "HATCH"); - else if (adenhd->enttype == adImageEnttype(dwghandle)) - strcpy(name, "IMAGE"); - else if (adenhd->enttype == adArcAlignedTextEnttype(dwghandle)) - strcpy(name, "ArcAlignedText"); - else if (adenhd->enttype == adWipeoutEnttype(dwghandle)) - strcpy(name, "Wipeout"); - else if (adenhd->enttype == adRtextEnttype(dwghandle)) - strcpy(name, "Rtext"); - else { /* regular proxy */ - - G_debug(3, "adenhd->enttype: %d", adenhd->enttype); - strcpy(name, "Proxy"); - } - break; - } -} - -int write_line(PAD_ENT_HDR adenhd, int type, int level) -{ - int i, l; - double x, y, z, r, ang; - - adSeekLayer(dwghandle, adenhd->entlayerobjhandle, Layer); - - /* Transformation, go up through all levels of transformation */ - /* not sure what is the right order of transformation */ - for (l = level; l >= 0; l--) { - for (i = 0; i < Points->n_points; i++) { - /* scale */ - x = Points->x[i] * Trans[l].xscale; - y = Points->y[i] * Trans[l].yscale; - z = Points->z[i] * Trans[l].zscale; - /* rotate */ - r = sqrt(x * x + y * y); - ang = atan2(y, x) + Trans[l].rotang; - x = r * cos(ang); - y = r * sin(ang); - /* move */ - x += Trans[l].dx; - y += Trans[l].dy; - z += Trans[l].dz; - Points->x[i] = x; - Points->y[i] = y; - Points->z[i] = z; - } - } - - Vect_reset_cats(Cats); - Vect_cat_set(Cats, 1, cat); - Vect_write_line(&Map, type, Points, Cats); - - /* Cat */ - sprintf(buf, "insert into %s values ( %d", Fi->table, cat); - db_set_string(&sql, buf); - - /* Entity name */ - getEntTypeName(adenhd, buf2); - sprintf(buf, ", '%s'", buf2); - db_append_string(&sql, buf); - - /* Color */ - sprintf(buf, ", %d", adenhd->entcolor); - db_append_string(&sql, buf); - - /* Weight */ - sprintf(buf, ", %d", adenhd->lineweight); - db_append_string(&sql, buf); - - /* Layer name */ - if (!Layer->purgedflag && Layer->name != NULL) { - db_set_string(&str, Layer->name); - db_double_quote_string(&str); - sprintf(buf, ", '%s'", db_get_string(&str)); - } - else { - sprintf(buf, ", ''"); - } - db_append_string(&sql, buf); - - /* Block name */ - if (Block != NULL) { - db_set_string(&str, Block); - db_double_quote_string(&str); - } - else { - db_set_string(&str, ""); - } - sprintf(buf, ", '%s'", db_get_string(&str)); - db_append_string(&sql, buf); - - /* Text */ - if (Txt != NULL) { - db_set_string(&str, Txt); - db_double_quote_string(&str); - } - else { - db_set_string(&str, ""); - } - sprintf(buf, ", '%s'", db_get_string(&str)); - db_append_string(&sql, buf); - - db_append_string(&sql, ")"); - G_debug(3, db_get_string(&sql)); - - if (db_execute_immediate(driver, &sql) != DB_OK) { - db_close_database(driver); - db_shutdown_driver(driver); - G_fatal_error("Cannot insert new row: %s", db_get_string(&sql)); - } - - cat++; - return 0; -} - -/* Returns 1 if element has geometry and may be written to vector */ -int is_low_level(PAD_ENT_HDR adenhd) -{ - if (adenhd->enttype == AD_ENT_BLOCK || adenhd->enttype == AD_ENT_ENDBLK || - adenhd->enttype == AD_ENT_SEQEND || adenhd->enttype == AD_ENT_INSERT) { - return 0; - } - return 1; -} - -void wrentity(PAD_ENT_HDR adenhd, PAD_ENT aden, int level, AD_VMADDR entlist, - int circle_as_point) -{ - short ret; - PAD_BLOB_CTRL bcptr; - PAD_ENT_HDR adenhd2; - PAD_ENT aden2; - OdaLong il; - double tempdouble[3], tempbulge, tempwidth[3]; - double x, y, z, ang; - PAD_BLKH adblkh; - int layer_found = 1; - - if (is_low_level(adenhd)) - n_elements++; - - /* Check layer name */ - if (layers_opt->answers) { - int i = 0; - - adSeekLayer(dwghandle, adenhd->entlayerobjhandle, Layer); - - layer_found = 0; - if (!Layer->purgedflag) { - while (layers_opt->answers[i]) { - if (strcmp(Layer->name, layers_opt->answers[i]) == 0) { - layer_found = 1; - break; - } - i++; - } - } - - if ((!invert_flag->answer && !layer_found) || - (invert_flag->answer && layer_found)) { - if (is_low_level(adenhd)) - n_skipped++; - if (adenhd->enttype != AD_ENT_INSERT && - adenhd->enttype != AD_ENT_POLYLINE) - return; - } - } - - getEntTypeName(adenhd, buf); - G_debug(1, "Entity: %s", buf); - - Txt = NULL; - adenhd2 = (PAD_ENT_HDR)G_malloc(sizeof(AD_ENT_HDR)); - aden2 = (PAD_ENT)G_malloc(sizeof(AD_ENT)); - adblkh = (PAD_BLKH)G_malloc(sizeof(AD_BLKH)); - Vect_reset_line(Points); - - /* Check space for lower level */ - if (level + 1 == atrans) { - atrans += 10; - Trans = (TRANS *)G_realloc(Trans, atrans * sizeof(TRANS)); - } - - switch (adenhd->enttype) { - case AD_ENT_LINE: - Vect_append_point(Points, aden->line.pt0[0], aden->line.pt0[1], - aden->line.pt0[2]); - Vect_append_point(Points, aden->line.pt1[0], aden->line.pt1[1], - aden->line.pt1[2]); - write_line(adenhd, GV_LINE, level); - break; - - case AD_ENT_FACE3D: - Vect_append_point(Points, aden->face3d.pt0[0], aden->face3d.pt0[1], - aden->face3d.pt0[2]); - Vect_append_point(Points, aden->face3d.pt1[0], aden->face3d.pt1[1], - aden->face3d.pt1[2]); - Vect_append_point(Points, aden->face3d.pt2[0], aden->face3d.pt2[1], - aden->face3d.pt2[2]); - Vect_append_point(Points, aden->face3d.pt3[0], aden->face3d.pt3[1], - aden->face3d.pt3[2]); - write_line(adenhd, GV_FACE, level); - break; - - case AD_ENT_SOLID: - Vect_append_point(Points, aden->solid.pt0[0], aden->solid.pt0[1], - aden->solid.pt0[2]); - Vect_append_point(Points, aden->solid.pt1[0], aden->solid.pt1[1], - aden->solid.pt1[2]); - Vect_append_point(Points, aden->solid.pt2[0], aden->solid.pt2[1], - aden->solid.pt2[2]); - Vect_append_point(Points, aden->solid.pt3[0], aden->solid.pt3[1], - aden->solid.pt3[2]); - write_line(adenhd, GV_FACE, level); - break; - - case AD_ENT_TEXT: - Txt = aden->text.textstr; - Vect_append_point(Points, aden->text.pt0[0], aden->text.pt0[1], - aden->line.pt0[2]); - write_line(adenhd, GV_POINT, level); - break; - - case AD_ENT_POINT: - Vect_append_point(Points, aden->point.pt0[0], aden->point.pt0[1], - aden->line.pt0[2]); - write_line(adenhd, GV_POINT, level); - break; - - case AD_ENT_ARC: - for (ang = aden->arc.stang; ang < aden->arc.endang; - ang += 2 * LOCPI / 360) { - x = aden->arc.pt0[0] + aden->arc.radius * cos(ang); - y = aden->arc.pt0[1] + aden->arc.radius * sin(ang); - z = aden->arc.pt0[2]; - Vect_append_point(Points, x, y, z); - } - x = aden->arc.pt0[0] + aden->arc.radius * cos(aden->arc.endang); - y = aden->arc.pt0[1] + aden->arc.radius * sin(aden->arc.endang); - z = aden->arc.pt0[2]; - Vect_append_point(Points, x, y, z); - write_line(adenhd, GV_LINE, level); - break; - - case AD_ENT_CIRCLE: - if (circle_as_point) { - Vect_append_point(Points, aden->circle.pt0[0], aden->circle.pt0[1], - aden->circle.pt0[3]); - write_line(adenhd, GV_POINT, level); - } - else { - for (ang = 0; ang < 2 * LOCPI; ang += 2 * LOCPI / 360) { - x = aden->circle.pt0[0] + aden->circle.radius * cos(ang); - y = aden->circle.pt0[1] + aden->circle.radius * sin(ang); - z = aden->circle.pt0[3]; - Vect_append_point(Points, x, y, z); - } - Vect_append_point(Points, Points->x[0], Points->y[0], Points->z[0]); - write_line(adenhd, GV_LINE, level); - } - break; - - /* BLOCK starts block of entities but makes no transformation - is it - * right ? - * -> do nothing just warn for xref */ - case AD_ENT_BLOCK: - if (aden->block.xrefpath[0]) { - G_warning("External reference for block not supported.\n xref: %s", - aden->block.xrefpath); - } - Block = G_store(aden->block.name2); - break; - - case AD_ENT_ENDBLK: /* endblk - no data */ - G_free(Block); - Block = NULL; - break; - - case AD_ENT_INSERT: /* insert */ - /* get transformation */ - /* TODO: fix rotation for CIRCLE and ARC */ - G_debug(3, " x,y,z: %f, %f, %f", aden->insert.pt0[0], - aden->insert.pt0[1], aden->insert.pt0[2]); - G_debug(3, " xscale, yscale, zscale: %f, %f, %f", aden->insert.xscale, - aden->insert.yscale, aden->insert.zscale); - G_debug(3, " rotang: %f", aden->insert.rotang); - G_debug(3, " ncols, nrows: %d, %d", aden->insert.numcols, - aden->insert.numrows); - G_debug(3, " coldist, rowdist: %f, %f", aden->insert.coldist, - aden->insert.rowdist); - - /* write block entities */ - adSeekBlockheader(dwghandle, aden->insert.blockheaderobjhandle, adblkh); - if (!adblkh->purgedflag) { - adStartEntityGet(adblkh->entitylist); - while (1) { - ret = adGetEntity(adblkh->entitylist, adenhd2, aden2); - if (adenhd2->enttype == AD_ENT_ENDBLK) - break; - if (ret) { - /* Set transformation for lower level */ - Trans[level + 1].dx = aden->insert.pt0[0]; - Trans[level + 1].dy = aden->insert.pt0[1]; - Trans[level + 1].dz = aden->insert.pt0[2]; - Trans[level + 1].xscale = aden->insert.xscale; - Trans[level + 1].yscale = aden->insert.yscale; - Trans[level + 1].zscale = aden->insert.zscale; - Trans[level + 1].rotang = aden->insert.rotang; - wrentity(adenhd2, aden2, level + 1, adblkh->entitylist, - circle_as_point); - } - } - } - break; - - case AD_ENT_SEQEND: /* seqend */ - break; - - case AD_ENT_POLYLINE: - while (1) { - ret = adGetEntity(entlist, adenhd2, aden2); - if (ret != 1) { - G_warning("Cannot get entity: %d: %s.", adError(), - adErrorStr(adError())); - break; - } - - if (adenhd2->enttype == AD_ENT_SEQEND) - break; - if (adenhd2->enttype != AD_ENT_VERTEX) { - getEntTypeName(adenhd2, buf); - G_warning("Expected VERTEX got %s in POLYLINE -> skip", buf); - } - else { - Vect_append_point(Points, aden2->vertex.pt0[0], - aden2->vertex.pt0[1], aden2->vertex.pt0[2]); - } - }; - if ((!invert_flag->answer && layer_found) || - (invert_flag->answer && !layer_found)) - write_line(adenhd, GV_LINE, level); - break; - - default: - if (adenhd->enttype == adLwplineEnttype(dwghandle)) { - G_debug(3, "Npoints: %ld\n", aden->lwpline.numpoints); - bcptr = adStartBlobRead(aden->lwpline.ldblob); - for (il = 0; il < aden->lwpline.numpoints; il++) { - adReadBlob2Double(bcptr, tempdouble); - Vect_append_point(Points, tempdouble[0], tempdouble[1], - tempdouble[2]); - tempbulge = tempwidth[0] = tempwidth[1] = tempwidth[2] = 0.0; - if (aden->lwpline.flag & AD_LWPLINE_HAS_BULGES) { - adReadBlobDouble(bcptr, &tempbulge); - } - if (aden->lwpline.flag & AD_LWPLINE_HAS_WIDTHS) { - adReadBlob2Double(bcptr, tempwidth); - } - } - G_debug(3, "flag = %d", aden->lwpline.flag); - if (aden->lwpline.flag & AD_LWPLINE_IS_CLOSED) { - G_debug(3, " -> is closed"); - Vect_append_point(Points, Points->x[0], Points->y[0], - Points->z[0]); - } - write_line(adenhd, GV_LINE, level); - adEndBlobRead(bcptr); - } - else { - getEntTypeName(adenhd, buf); - G_warning("%s entity not supported", buf); - } - break; - - } /* end of switch */ - - G_free(aden2); - G_free(adenhd2); -} diff --git a/vector/v.in.dwg/global.h b/vector/v.in.dwg/global.h deleted file mode 100644 index 39e46f17856..00000000000 --- a/vector/v.in.dwg/global.h +++ /dev/null @@ -1,59 +0,0 @@ -/* ************************************************************** - * - * MODULE: v.in.dwg - * - * AUTHOR(S): Radim Blazek - * - * PURPOSE: Import of DWG/DXF files - * - * COPYRIGHT: (C) 2001 by the GRASS Development Team - * - * This program is free software under the - * GNU General Public License (>=v2). - * Read the file COPYING that comes with GRASS - * for details. - * - * In addition, as a special exception, Radim Blazek gives permission - * to link the code of this program with the OpenDWG libraries (or with - * modified versions of the OpenDWG libraries that use the same license - * as OpenDWG libraries), and distribute linked combinations including the two. - * You must obey the GNU General Public License in all respects for all - * of the code used other than. If you modify this file, you may extend - * this exception to your version of the file, but you are not obligated - * to do so. If you do not wish to do so, delete this exception statement - * from your version. - * - * **************************************************************/ - -/* transformation, first level is 0 ( called from main ) and transformation - * for this level is 0,0,0, 1,1,1, 0 so that no transformation is done on first - * level (not effective but better readable?) */ -typedef struct { - double dx, dy, dz; - double xscale, yscale, zscale; - double rotang; -} TRANS; - -extern int cat; -extern int - n_elements; /* number of processed elements (only low level elements) */ -extern int - n_skipped; /* number of skipped low level elements (different layer name) */ -extern struct Map_info Map; -extern dbDriver *driver; -extern dbString sql; -extern dbString str; -extern struct line_pnts *Points; -extern struct line_cats *Cats; -extern PAD_LAY Layer; -extern char *Txt; -extern char *Block; -extern struct field_info *Fi; -extern AD_DB_HANDLE dwghandle; -extern TRANS *Trans; /* transformation */ -extern int atrans; /* number of allocated levels */ -extern struct Option *layers_opt; -extern struct Flag *invert_flag; - -void wrentity(PAD_ENT_HDR adenhd, PAD_ENT aden, int level, AD_VMADDR entlist, - int circle_as_point); diff --git a/vector/v.in.dwg/main.c b/vector/v.in.dwg/main.c deleted file mode 100644 index cc7b9a217b1..00000000000 --- a/vector/v.in.dwg/main.c +++ /dev/null @@ -1,295 +0,0 @@ -/* ************************************************************** - * - * MODULE: v.in.dwg - * - * AUTHOR(S): Radim Blazek - * - * PURPOSE: Import of DWG/DXF files - * - * COPYRIGHT: (C) 2001-2008 by the GRASS Development Team - * - * This program is free software under the - * GNU General Public License (>=v2). - * Read the file COPYING that comes with GRASS - * for details. - * - * In addition, as a special exception, Radim Blazek gives permission - * to link the code of this program with the OpenDWG libraries (or with - * modified versions of the OpenDWG libraries that use the same license - * as OpenDWG libraries), and distribute linked combinations including the two. - * You must obey the GNU General Public License in all respects for all - * of the code used other than. If you modify this file, you may extend - * this exception to your version of the file, but you are not obligated - * to do so. If you do not wish to do so, delete this exception statement - * from your version. - * - * **************************************************************/ -#define AD_PROTOTYPES -#define AD_VM_PC -#define OD_GENERIC_READ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "ad2.h" -#include "io/odio.h" -#include "global.h" - -int cat; -int n_elements; /* number of processed elements (only low level elements) */ -int n_skipped; /* number of skipped low level elements (different layer name) */ -struct Map_info Map; -dbDriver *driver; -dbString sql; -dbString str; -struct line_pnts *Points; -struct line_cats *Cats; -PAD_LAY Layer; -char *Txt; -char *Block; -struct field_info *Fi; -AD_DB_HANDLE dwghandle; -TRANS *Trans; /* transformation */ -int atrans; /* number of allocated levels */ -struct Option *layers_opt; -struct Flag *invert_flag; - -int main(int argc, char *argv[]) -{ - struct GModule *module; - struct Option *out_opt, *in_opt; - struct Flag *z_flag, *circle_flag, *l_flag, *int_flag; - const size_t BUFSIZE = 2000; - char buf[BUFSIZE]; - - /* DWG */ - char path[2000]; - short initerror, entset, retval; - AD_OBJHANDLE pspace, mspace; - PAD_ENT_HDR adenhd; - PAD_ENT aden; - AD_VMADDR entlist; - - G_gisinit(argv[0]); - - module = G_define_module(); - G_add_keyword(_("vector")); - G_add_keyword(_("import")); - module->description = _("Converts DWG/DXF to GRASS vector map"); - - in_opt = G_define_standard_option(G_OPT_F_INPUT); - in_opt->description = _("Name of DWG or DXF file"); - - out_opt = G_define_standard_option(G_OPT_V_OUTPUT); - out_opt->required = YES; - - layers_opt = G_define_option(); - layers_opt->key = "layers"; - layers_opt->type = TYPE_STRING; - layers_opt->required = NO; - layers_opt->multiple = YES; - layers_opt->description = _("List of layers to import"); - - invert_flag = G_define_flag(); - invert_flag->key = 'i'; - invert_flag->description = - _("Invert selection by layers (don't import layers in list)"); - - z_flag = G_define_flag(); - z_flag->key = 'z'; - z_flag->description = _("Create 3D vector map"); - - circle_flag = G_define_flag(); - circle_flag->key = 'c'; - circle_flag->description = _("Write circles as points (centre)"); - - l_flag = G_define_flag(); - l_flag->key = 'l'; - l_flag->description = _("List available layers and exit"); - - int_flag = G_define_flag(); - int_flag->key = 'n'; - int_flag->description = _("Use numeric type for attribute \"layer\""); - - if (G_parser(argc, argv)) - exit(EXIT_FAILURE); - - db_init_string(&sql); - db_init_string(&str); - adenhd = (PAD_ENT_HDR)G_malloc(sizeof(AD_ENT_HDR)); - aden = (PAD_ENT)G_malloc(sizeof(AD_ENT)); - Layer = (PAD_LAY)G_malloc(sizeof(AD_LAY)); - Points = Vect_new_line_struct(); - Cats = Vect_new_cats_struct(); - Block = NULL; - - atrans = 20; /* nested, recursive levels */ - Trans = (TRANS *)G_malloc(atrans * sizeof(TRANS)); - - /* Init OpenDWG */ - sprintf(path, "%s/etc/adinit.dat", G_gisbase()); - if (!adInitAd2(path, &initerror)) { - snprintf(buf, BUFSIZE, - _("Unable to initialize OpenDWG Toolkit, error: %d: %s."), - initerror, adErrorStr(initerror)); - size_t buflen = strlen(buf); - if (initerror == AD_UNABLE_TO_OPEN_INIT_FILE) - snprintf(buf + buflen, BUFSIZE - buflen, _(" Cannot open %s"), - path); - G_fatal_error(buf); - } - adSetupDwgRead(); - adSetupDxfRead(); - - /* Open input file */ - if ((dwghandle = adLoadFile(in_opt->answer, AD_PRELOAD_ALL, 1)) == NULL) { - G_fatal_error(_("Unable to open input file <%s>. Error %d: %s"), - in_opt->answer, adError(), adErrorStr(adError())); - } - - if (l_flag->answer) { /* List layers */ - PAD_TB adtb; - AD_DWGHDR adhd; - int i; - char on, frozen, vpfrozen, locked; - - adtb = (PAD_TB)G_malloc(sizeof(AD_TB)); - - G_debug(2, "%d layers", (int)adNumLayers(dwghandle)); - adReadHeaderBlock(dwghandle, &adhd); - adStartLayerGet(dwghandle); - - fprintf(stdout, "%d layers:\n", (int)adNumLayers(dwghandle)); - for (i = 0; i < (int)adNumLayers(dwghandle); i++) { - adGetLayer(dwghandle, &(adtb->lay)); - if (!adtb->lay.purgedflag) { - fprintf(stdout, "%s COLOR %d, ", adtb->lay.name, - adtb->lay.color); - } - adGetLayerState(dwghandle, adtb->lay.objhandle, &on, &frozen, - &vpfrozen, &locked); - if (on) - fprintf(stdout, "ON, "); - else - fprintf(stdout, "OFF, "); - if (frozen) - fprintf(stdout, "FROZEN, "); - else - fprintf(stdout, "THAWED, "); - if (vpfrozen) - fprintf(stdout, "VPFROZEN, "); - else - fprintf(stdout, "VPTHAWED, "); - if (locked) - fprintf(stdout, "LOCKED\n"); - else - fprintf(stdout, "UNLOCKED\n"); - } - adCloseFile(dwghandle); - adCloseAd2(); - exit(EXIT_SUCCESS); - } - - /* open output vector */ - if (Vect_open_new(&Map, out_opt->answer, z_flag->answer) < 0) - G_fatal_error(_("Unable to create vector map <%s>"), out_opt->answer); - - Vect_hist_command(&Map); - - /* Add DB link */ - Fi = Vect_default_field_info(&Map, 1, NULL, GV_1TABLE); - Vect_map_add_dblink(&Map, 1, NULL, Fi->table, GV_KEY_COLUMN, Fi->database, - Fi->driver); - - driver = db_start_driver_open_database(Fi->driver, - Vect_subst_var(Fi->database, &Map)); - if (driver == NULL) { - G_fatal_error(_("Unable to open database <%s> by driver <%s>"), - Vect_subst_var(Fi->database, &Map), Fi->driver); - } - db_set_error_handler_driver(driver); - - db_begin_transaction(driver); - - /* Create table */ - if (int_flag->answer) { /* List layers */ - sprintf(buf, - "create table %s ( cat integer, entity_name varchar(20), color " - "int, weight int, " - "layer real, block varchar(100), txt varchar(100) )", - Fi->table); - } - else { - sprintf(buf, - "create table %s ( cat integer, entity_name varchar(20), color " - "int, weight int, " - "layer varchar(100), block varchar(100), txt varchar(100) )", - Fi->table); - } - db_set_string(&sql, buf); - G_debug(3, db_get_string(&sql)); - - if (db_execute_immediate(driver, &sql) != DB_OK) { - db_close_database(driver); - db_shutdown_driver(driver); - G_fatal_error(_("Unable to create table: '%s'"), db_get_string(&sql)); - } - - if (db_create_index2(driver, Fi->table, GV_KEY_COLUMN) != DB_OK) - G_warning(_("Unable to create index for table <%s>, key <%s>"), - Fi->table, GV_KEY_COLUMN); - - if (db_grant_on_table(driver, Fi->table, DB_PRIV_SELECT, - DB_GROUP | DB_PUBLIC) != DB_OK) - G_fatal_error(_("Unable to grant privileges on table <%s>"), Fi->table); - - cat = 1; - n_elements = n_skipped = 0; - /* Write each entity. Some entities may be composed by other entities (like - * INSERT or BLOCK) */ - /* Set transformation for first (index 0) level */ - Trans[0].dx = Trans[0].dy = Trans[0].dz = 0; - Trans[0].xscale = Trans[0].yscale = Trans[0].zscale = 1; - Trans[0].rotang = 0; - if (adGetBlockHandle(dwghandle, pspace, AD_PAPERSPACE_HANDLE)) { - entlist = adEntityList(dwghandle, pspace); - adStartEntityGet(entlist); - for (entset = 0; entset < 2; entset++) { - do { - if (!(retval = adGetEntity(entlist, adenhd, aden))) - continue; - wrentity(adenhd, aden, 0, entlist, circle_flag->answer); - } while (retval == 1); - if (entset == 0) { - if (adGetBlockHandle(dwghandle, mspace, AD_MODELSPACE_HANDLE)) { - entlist = adEntityList(dwghandle, mspace); - adStartEntityGet(entlist); - } - } - } - } - - db_commit_transaction(driver); - db_close_database_shutdown_driver(driver); - - adCloseFile(dwghandle); - adCloseAd2(); - - Vect_build(&Map, stderr); - Vect_close(&Map); - - if (n_skipped > 0) - G_message(_("%d elements skipped (layer name was not in list)"), - n_skipped); - - G_done_msg(_("%d elements processed"), n_elements); - - exit(EXIT_SUCCESS); -} diff --git a/vector/v.in.dwg/v.in.dwg.html b/vector/v.in.dwg/v.in.dwg.html deleted file mode 100644 index 60f00966f9d..00000000000 --- a/vector/v.in.dwg/v.in.dwg.html +++ /dev/null @@ -1,32 +0,0 @@ -

    DESCRIPTION

    - -v.in.dwg imports DWG/DXF file into GRASS. - -

    EXAMPLE

    - -
    -v.in.dwg input=map.dwg output=map
    -
    - -

    NOTES

    - -v.in.dwg requires OpenDWG toolkit. To get this toolkit you must become at -least "Associate Member" of OpenDWG Alliance (http://www.opendesign.com/). -

    The toolkit, for example ad27linx.tar, unpack in a directory -(e.g. /home/usr1/opendwg27) and use the related configure options -to tell GRASS about it: - -

    -   ./configure \
    -   ... \
    -   --with-opendwg \
    -   --with-opendwg-includes=/home/usr1/opendwg27 \
    -   --with-opendwg-libs=/home/usr1/opendwg27
    -
    - -Then you can compile this module. -

    Not all entity types are supported (warning printed). - -

    AUTHOR

    - -Radim Blazek, ITC-Irst, Trento, Italy From a536bd49c55cc11d0a765c6e58c1829f2e679379 Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Thu, 19 Sep 2024 02:21:55 -0400 Subject: [PATCH 249/471] d.mon: Fix unbounded source buffer (#4260) --- display/d.mon/list.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/display/d.mon/list.c b/display/d.mon/list.c index ab346302a61..c61c1081991 100644 --- a/display/d.mon/list.c +++ b/display/d.mon/list.c @@ -14,11 +14,13 @@ char *get_path(const char *name, int fpath) char tmpdir[GPATH_MAX]; G_temp_element(tmpdir); - strcat(tmpdir, "/"); - strcat(tmpdir, "MONITORS"); + (void)G_strlcat(tmpdir, "/", sizeof(tmpdir)); + (void)G_strlcat(tmpdir, "MONITORS", sizeof(tmpdir)); if (name) { - strcat(tmpdir, "/"); - strcat(tmpdir, name); + (void)G_strlcat(tmpdir, "/", sizeof(tmpdir)); + if (G_strlcat(tmpdir, name, sizeof(tmpdir)) >= sizeof(tmpdir)) { + G_fatal_error(_("Failed to append <%s> to path"), name); + } } if (fpath) { @@ -132,10 +134,13 @@ void list_files(const char *name, FILE *fd_out) DIR *dirp; G_temp_element(tmpdir); - strcat(tmpdir, "/"); - strcat(tmpdir, "MONITORS"); - strcat(tmpdir, "/"); - strcat(tmpdir, name); + (void)G_strlcat(tmpdir, "/", sizeof(tmpdir)); + (void)G_strlcat(tmpdir, "MONITORS", sizeof(tmpdir)); + (void)G_strlcat(tmpdir, "/", sizeof(tmpdir)); + + if (G_strlcat(tmpdir, name, sizeof(tmpdir)) >= sizeof(tmpdir)) { + G_fatal_error(_("Failed to append <%s> to path"), name); + } G_file_name(mon_path, tmpdir, NULL, G_mapset()); fprintf(fd_out, "path=%s\n", mon_path); From d5bb442d7b121861928a37842e05e5265a980427 Mon Sep 17 00:00:00 2001 From: Nicklas Larsson Date: Thu, 19 Sep 2024 09:03:26 +0200 Subject: [PATCH 250/471] lib/gmath: use C interface to BLAS and LAPACK libraries (#4247) Replace outdated direct use of Fortran libraries via g2c.h, f2c.h or private prototypes, with the C interface API and libraries CBLAS and LAPACKE. This simplifies our own code and makes it easier to maintain and expand. Configure is simplified to use pkg-config to retrieve INC and LIB info. `--with-blas` defaults to `--with-blas=cblas` where "cblas" is the pkg-config package name; `---with-lapack` defaults to `--with-lapack=lapacke`. If the default fails the test a list of package names are tested: - BLAS package names tested: cblas blas-netlib openblas blas-atlas - LAPACKE package name tested: lapacke openblas - CI(Linux): replace libopenblas-dev with liblapacke-dev dependency - CI(macOS): replace blas and lapack with openblas dependency - CI(Windows): replace lapack with openblas dependency --- .github/workflows/apt.txt | 2 +- .github/workflows/macos_dependencies.txt | 3 +- .github/workflows/macos_install.sh | 8 +- .github/workflows/osgeo4w.yml | 2 +- configure | 992 +---- configure.ac | 245 +- include/grass/blas.h | 412 -- include/grass/config.h.in | 16 +- include/grass/la.h | 116 +- include/grass/lapack.h | 4748 ---------------------- lib/gmath/ATLAS_wrapper_blas_level_1.c | 90 +- lib/gmath/Makefile | 2 +- lib/gmath/la.c | 139 +- mswindows/osgeo4w/build_osgeo4w.sh | 2 +- 14 files changed, 416 insertions(+), 6361 deletions(-) delete mode 100644 include/grass/blas.h delete mode 100644 include/grass/lapack.h diff --git a/.github/workflows/apt.txt b/.github/workflows/apt.txt index a5a221c939c..4c14cb7b067 100644 --- a/.github/workflows/apt.txt +++ b/.github/workflows/apt.txt @@ -7,8 +7,8 @@ libfftw3-dev libgdal-dev libgl1-mesa-dev libglu1-mesa-dev +liblapacke-dev libnetcdf-dev -libopenblas-dev libpdal-dev libpng-dev libproj-dev diff --git a/.github/workflows/macos_dependencies.txt b/.github/workflows/macos_dependencies.txt index bc60609f7b5..8ef1d3460f6 100644 --- a/.github/workflows/macos_dependencies.txt +++ b/.github/workflows/macos_dependencies.txt @@ -1,4 +1,3 @@ -blas cairo clangxx_osx-arm64 clang_osx-arm64 @@ -13,7 +12,6 @@ giflib git ipython krb5 -lapack lastools libgdal-arrow-parquet libgdal-core @@ -32,6 +30,7 @@ libtiff llvm-openmp matplotlib numpy<2 +openblas pandoc pdal pillow diff --git a/.github/workflows/macos_install.sh b/.github/workflows/macos_install.sh index 79446f51867..8a94bc15c6a 100755 --- a/.github/workflows/macos_install.sh +++ b/.github/workflows/macos_install.sh @@ -47,12 +47,8 @@ CONFIGURE_FLAGS="\ --with-bzlib-libs=${CONDA_PREFIX}/lib \ --with-bzlib-includes=${CONDA_PREFIX}/include \ --with-netcdf=${CONDA_PREFIX}/bin/nc-config \ - --with-blas \ - --with-blas-libs=${CONDA_PREFIX}/lib \ - --with-blas-includes=${CONDA_PREFIX}/include \ - --with-lapack - --with-lapack-includes=${CONDA_PREFIX}/include \ - --with-lapack-libs=${CONDA_PREFIX}/lib \ + --with-blas=openblas \ + --with-lapack=openblas \ --with-netcdf=${CONDA_PREFIX}/bin/nc-config \ --with-nls \ --with-libs=${CONDA_PREFIX}/lib \ diff --git a/.github/workflows/osgeo4w.yml b/.github/workflows/osgeo4w.yml index 2b92c254238..10acd64dc37 100644 --- a/.github/workflows/osgeo4w.yml +++ b/.github/workflows/osgeo4w.yml @@ -39,7 +39,7 @@ jobs: update: true msystem: MINGW64 install: tar libintl make bison flex diffutils git dos2unix zip mingw-w64-x86_64-toolchain - mingw-w64-x86_64-fftw mingw-w64-x86_64-lapack mingw-w64-x86_64-pkgconf + mingw-w64-x86_64-fftw mingw-w64-x86_64-openblas mingw-w64-x86_64-pkgconf mingw-w64-x86_64-gcc mingw-w64-x86_64-ccache mingw-w64-x86_64-zlib mingw-w64-x86_64-libiconv mingw-w64-x86_64-bzip2 mingw-w64-x86_64-gettext mingw-w64-x86_64-libsystre mingw-w64-x86_64-libtre-git mingw-w64-x86_64-libwinpthread-git mingw-w64-x86_64-libpng diff --git a/configure b/configure index 4b9a63f7947..17182bc4b7a 100755 --- a/configure +++ b/configure @@ -901,8 +901,6 @@ with_sqlite with_opengl with_odbc with_fftw -with_blas -with_lapack with_libsvm with_cairo with_freetype @@ -915,6 +913,8 @@ with_opencl with_bzlib with_zstd with_pdal +with_blas +with_lapack with_libpng with_gdal with_liblas @@ -945,10 +945,6 @@ with_odbc_includes with_odbc_libs with_fftw_includes with_fftw_libs -with_blas_includes -with_blas_libs -with_lapack_includes -with_lapack_libs with_libsvm_includes with_libsvm_libs with_cairo_includes @@ -1633,8 +1629,6 @@ Optional Packages: --with-opengl support OpenGL functionality (default: yes) --with-odbc support ODBC functionality (default: no) --with-fftw support FFTW functionality (default: yes) - --with-blas support BLAS functionality (default: no) - --with-lapack support LAPACK functionality (default: no) --with-libsvm support LIBSVM functionality (default: no) --with-cairo support Cairo functionality (default: yes) --with-freetype support FreeType functionality (default: yes) @@ -1647,6 +1641,14 @@ Optional Packages: --with-bzlib support BZIP2 functionality (default: no) --with-zstd support Zstandard functionality (default: yes) --with-pdal support PDAL functionality (default: yes) + --with-blas=pkg-config-package + enable BLAS support with by adding name of + pkg-config package name for CBLAS library, e.g. + '--with-lapack-package=openblas', default: cblas + --with-lapack=pkg-config-package + enable LAPACK support with by adding name of + pkg-config package name for LAPACKE library, e.g. + '--with-lapack-package=openblas', default: lapacke --with-libpng=path/libpng-config enable PNG support (libpng-config with path, e.g. '--with-libpng=/usr/local/bin/libpng-config') @@ -1704,12 +1706,6 @@ Optional Packages: --with-fftw-includes=DIRS FFTW include files are in DIRS --with-fftw-libs=DIRS FFTW library files are in DIRS - --with-blas-includes=DIRS - BLAS include files are in DIRS - --with-blas-libs=DIRS BLAS library files are in DIRS - --with-lapack-includes=DIRS - LAPACK include files are in DIRS - --with-lapack-libs=DIRS LAPACK library files are in DIRS --with-libsvm-includes=DIRS LIBSVM include files are in DIRS --with-libsvm-libs=DIRS LIBSVM library files are in DIRS @@ -5477,28 +5473,6 @@ fi -# Check whether --with-blas was given. -if test ${with_blas+y} -then : - withval=$with_blas; -else $as_nop - with_blas=no -fi - - - - -# Check whether --with-lapack was given. -if test ${with_lapack+y} -then : - withval=$with_lapack; -else $as_nop - with_lapack=no -fi - - - - # Check whether --with-libsvm was given. if test ${with_libsvm+y} then : @@ -5631,6 +5605,27 @@ fi + +# Check whether --with-blas was given. +if test ${with_blas+y} +then : + withval=$with_blas; +else $as_nop + with-blas="no" +fi + + + +# Check whether --with-lapack was given. +if test ${with_lapack+y} +then : + withval=$with_lapack; +else $as_nop + with-lapack="no" +fi + + + # Check whether --with-libpng was given. if test ${with_libpng+y} then : @@ -5914,44 +5909,6 @@ fi -# Check whether --with-blas-includes was given. -if test ${with_blas_includes+y} -then : - withval=$with_blas_includes; -fi - - - - -# Check whether --with-blas-libs was given. -if test ${with_blas_libs+y} -then : - withval=$with_blas_libs; -fi - - - - - -# Check whether --with-lapack-includes was given. -if test ${with_lapack_includes+y} -then : - withval=$with_lapack_includes; -fi - - - - -# Check whether --with-lapack-libs was given. -if test ${with_lapack_libs+y} -then : - withval=$with_lapack_libs; -fi - - - - - # Check whether --with-libsvm-includes was given. if test ${with_libsvm_includes+y} then : @@ -6899,18 +6856,6 @@ if test "x$ac_cv_header_values_h" = xyes then : printf "%s\n" "#define HAVE_VALUES_H 1" >>confdefs.h -fi -ac_fn_c_check_header_compile "$LINENO" "f2c.h" "ac_cv_header_f2c_h" "$ac_includes_default" -if test "x$ac_cv_header_f2c_h" = xyes -then : - printf "%s\n" "#define HAVE_F2C_H 1" >>confdefs.h - -fi -ac_fn_c_check_header_compile "$LINENO" "g2c.h" "ac_cv_header_g2c_h" "$ac_includes_default" -if test "x$ac_cv_header_g2c_h" = xyes -then : - printf "%s\n" "#define HAVE_G2C_H 1" >>confdefs.h - fi ac_fn_c_check_header_compile "$LINENO" "sys/ioctl.h" "ac_cv_header_sys_ioctl_h" "$ac_includes_default" @@ -13759,827 +13704,214 @@ fi # $USE_FFTW # Enable BLAS option - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether to use BLAS" >&5 -printf %s "checking whether to use BLAS... " >&6; } -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: \"$with_blas\"" >&5 -printf "%s\n" "\"$with_blas\"" >&6; } -case "$with_blas" in - "no") USE_BLAS= ;; - "yes") USE_BLAS="1" ;; - *) as_fn_error $? "*** You must answer yes or no." "$LINENO" 5 ;; -esac - - +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether to use (C)BLAS" >&5 +printf %s "checking whether to use (C)BLAS... " >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $with_blas" >&5 +printf "%s\n" "$with_blas" >&6; } BLASLIB= BLASINC= +USE_BLAS= +BLAS_PKGS="cblas blas-netlib openblas blas-atlas" +BLAS_PKG= -if test -n "$USE_BLAS"; then - -# With BLAS includes directory -# BLAS doesn't have includes (FORTRAN library) -# With BLAS library directory - - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for location of BLAS includes" >&5 -printf %s "checking for location of BLAS includes... " >&6; } -case "$with_blas_includes" in -y | ye | yes | n | no) - as_fn_error $? "*** You must supply a directory to --with-blas-includes." "$LINENO" 5 - ;; +case "$with_blas" in + no) + ;; + yes) + USE_BLAS="$with_blas" + ;; + *) + USE_BLAS=yes + BLAS_PKGS="$with_blas" + ;; esac -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $with_blas_includes" >&5 -printf "%s\n" "$with_blas_includes" >&6; } -if test -n "$with_blas_includes" ; then - for dir in $with_blas_includes; do - if test -d "$dir"; then - BLASINC="$BLASINC -I$dir" - else - as_fn_error $? "*** BLAS includes directory $dir does not exist." "$LINENO" 5 - fi - done -fi +if test -n "$USE_BLAS"; then + blas_ok=no -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for location of BLAS library" >&5 -printf %s "checking for location of BLAS library... " >&6; } -case "$with_blas_libs" in -y | ye | yes | n | no) - as_fn_error $? "*** You must supply a directory to --with-blas-libs." "$LINENO" 5 - ;; -esac -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $with_blas_libs" >&5 -printf "%s\n" "$with_blas_libs" >&6; } + for blas_chk in ${BLAS_PKGS} + do + if ! ${PKG_CONFIG} --exists ${blas_chk} ; then + continue + else + BLAS_PKG=${blas_chk} + break + fi + done -if test -n "$with_blas_libs"; then - for dir in $with_blas_libs; do - if test -d "$dir"; then - BLASLIB="$BLASLIB -L$dir" - else - as_fn_error $? "*** BLAS library directory $dir does not exist." "$LINENO" 5 - fi - done -fi + if test -z "$BLAS_PKG" ; then + as_fn_error $? "*** Unable to locate (C)BLAS pkg-conf package(s) + \"${BLAS_PKGS}\". Perhaps you need to set PKG_CONFIG_PATH." "$LINENO" 5 + fi + BLASLIB=$(${PKG_CONFIG} --libs ${BLAS_PKG}) + BLASINC=$(${PKG_CONFIG} --cflags ${BLAS_PKG}) -# See if cblas.h exists (CBLAS,ATLAS,others) -save_CPPFLAGS="$CPPFLAGS" -CPPFLAGS="$BLASINC $CPPFLAGS" -ac_fn_c_check_header_compile "$LINENO" "cblas.h" "ac_cv_header_cblas_h" "$ac_includes_default" + save_CFLAGS="$CFLAGS"; CFLAGS="$BLASINC $CFLAGS" + save_LIBS="$LIBS"; LIBS="$BLASLIB" + + for ac_header in cblas.h +do : + ac_fn_c_check_header_compile "$LINENO" "cblas.h" "ac_cv_header_cblas_h" "$ac_includes_default" if test "x$ac_cv_header_cblas_h" = xyes then : printf "%s\n" "#define HAVE_CBLAS_H 1" >>confdefs.h -fi - -CPPFLAGS="$save_CPPFLAGS" - - -ac_save_ldflags="$LDFLAGS" -LDFLAGS="$BLASLIB $LDFLAGS" - - - - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for dnrm2_ in -lblas" >&5 -printf %s "checking for dnrm2_ in -lblas... " >&6; } - -ac_check_lib_save_LIBS=$LIBS -LIBS="-lblas $MATHLIB $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char dnrm2_ (); -int -main (void) -{ -return dnrm2_ (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO" -then : - ac_cv_lib_blas_dnrm2_=yes else $as_nop - ac_cv_lib_blas_dnrm2_=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_blas_dnrm2_" >&5 -printf "%s\n" "$ac_cv_lib_blas_dnrm2_" >&6; } -if test "x$ac_cv_lib_blas_dnrm2_" = xyes + for ac_header in cblas-atlas.h +do : + ac_fn_c_check_header_compile "$LINENO" "cblas-atlas.h" "ac_cv_header_cblas_atlas_h" "$ac_includes_default" +if test "x$ac_cv_header_cblas_atlas_h" = xyes then : - BLASLIB="$BLASLIB -lblas " + printf "%s\n" "#define HAVE_CBLAS_ATLAS_H 1" >>confdefs.h + else $as_nop + as_fn_error $? "*** Failed to find either \"cblas.h\" or \"cblas-atlas.h\" with \"$CFLAGS\"" "$LINENO" 5 +fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for dnrm2_ in -lblas" >&5 -printf %s "checking for dnrm2_ in -lblas... " >&6; } +done +fi -ac_check_lib_save_LIBS=$LIBS -LIBS="-lblas $MATHLIB -lg2c $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ +done -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char dnrm2_ (); + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for linking to (C)BLAS" >&5 +printf %s "checking for linking to (C)BLAS... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +char cblas_dgemm(); int main (void) { -return dnrm2_ (); +return cblas_dgemm(); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : - ac_cv_lib_blas_dnrm2_=yes + blas_ok=yes else $as_nop - ac_cv_lib_blas_dnrm2_=no + + as_fn_error $? "*** Unable to link to (C)BLAS library with \"$LIBS\"." "$LINENO" 5 fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_blas_dnrm2_" >&5 -printf "%s\n" "$ac_cv_lib_blas_dnrm2_" >&6; } -if test "x$ac_cv_lib_blas_dnrm2_" = xyes -then : - BLASLIB="$BLASLIB -lblas -lg2c" -else $as_nop - -LDFLAGS=${ac_save_ldflags} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $BLAS_PKG" >&5 +printf "%s\n" "$BLAS_PKG" >&6; } - as_fn_error $? "*** Unable to locate BLAS library." "$LINENO" 5 + CFLAGS="$save_CFLAGS" + LIBS="$save_LIBS" -fi +printf "%s\n" "#define HAVE_LIBBLAS 1" >>confdefs.h +fi # $USE_BLAS -fi +# Done checking BLAS +# Enable LAPACK option +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether to use LAPACK(E)" >&5 +printf %s "checking whether to use LAPACK(E)... " >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $with_lapack" >&5 +printf "%s\n" "$with_lapack" >&6; } +LAPACKLIB= +LAPACKINC= +USE_LAPACK= +LAPACK_PKGS="lapacke openblas" +LAPACK_PKG= -LDFLAGS=${ac_save_ldflags} +case "$with_lapack" in + no) + ;; + yes) + USE_LAPACK="$with_lapack" + ;; + *) + USE_LAPACK=yes + LAPACK_PKG="$with_lapack" + ;; +esac +# LAPACK is useless without BLAS +if test -n "$USE_BLAS" && test -n "$USE_LAPACK"; then -blas_ok=no + lapack_ok=no -# Check for ATLAS -save_LDFLAGS="$LDFLAGS" -LDFLAGS="$BLASLIB $LDFLAGS" -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ATL_xerbla in -latlas" >&5 -printf %s "checking for ATL_xerbla in -latlas... " >&6; } + for lapack_chk in ${LAPACK_PKGS} + do + if ! ${PKG_CONFIG} --exists ${lapack_chk} ; then + continue + else + LAPACK_PKG=${lapack_chk} + break + fi + done -ac_check_lib_save_LIBS=$LIBS -LIBS="-latlas $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ + if test -z "$LAPACK_PKG" ; then + as_fn_error $? "*** Unable to locate LAPACK(E) pkg-conf package(s) + \"$LAPACK_PKGS\". Perhaps you need to set PKG_CONFIG_PATH." "$LINENO" 5 + fi -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char ATL_xerbla (); -int -main (void) -{ -return ATL_xerbla (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO" -then : - ac_cv_lib_atlas_ATL_xerbla=yes -else $as_nop - ac_cv_lib_atlas_ATL_xerbla=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_atlas_ATL_xerbla" >&5 -printf "%s\n" "$ac_cv_lib_atlas_ATL_xerbla" >&6; } -if test "x$ac_cv_lib_atlas_ATL_xerbla" = xyes -then : - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for sgemm_ in -lf77blas" >&5 -printf %s "checking for sgemm_ in -lf77blas... " >&6; } + LAPACKLIB=`${PKG_CONFIG} --libs ${LAPACK_PKG}` + LAPACKINC=`${PKG_CONFIG} --cflags ${LAPACK_PKG}` -ac_check_lib_save_LIBS=$LIBS -LIBS="-lf77blas -latlas $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ + save_CFLAGS="$CFLAGS"; CFLAGS="$LAPACKINC $CFLAGS" + save_LIBS="$LIBS"; LIBS="$LAPACKLIB" -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char sgemm_ (); -int -main (void) -{ -return sgemm_ (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO" + for ac_header in lapacke.h +do : + ac_fn_c_check_header_compile "$LINENO" "lapacke.h" "ac_cv_header_lapacke_h" "$ac_includes_default" +if test "x$ac_cv_header_lapacke_h" = xyes then : - ac_cv_lib_f77blas_sgemm_=yes + printf "%s\n" "#define HAVE_LAPACKE_H 1" >>confdefs.h + else $as_nop - ac_cv_lib_f77blas_sgemm_=no + as_fn_error $? "*** Unable to find \"lapacke.h\" with \"$CFLAGS\"." "$LINENO" 5 fi -rm -f core conftest.err conftest.$ac_objext conftest.beam \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_f77blas_sgemm_" >&5 -printf "%s\n" "$ac_cv_lib_f77blas_sgemm_" >&6; } -if test "x$ac_cv_lib_f77blas_sgemm_" = xyes -then : - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for cblas_dgemm in -lcblas" >&5 -printf %s "checking for cblas_dgemm in -lcblas... " >&6; } -ac_check_lib_save_LIBS=$LIBS -LIBS="-lcblas -lf77blas -latlas $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ +done -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char cblas_dgemm (); + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for linking to LAPACK(E)" >&5 +printf %s "checking for linking to LAPACK(E)... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +char LAPACKE_dgesv(); int main (void) { -return cblas_dgemm (); +return LAPACKE_dgesv(); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : - ac_cv_lib_cblas_cblas_dgemm=yes + lapack_ok=yes else $as_nop - ac_cv_lib_cblas_cblas_dgemm=no + + as_fn_error $? "*** Unable to link to LAPACK(E) library with \"$LIBS\"." "$LINENO" 5 fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_cblas_cblas_dgemm" >&5 -printf "%s\n" "$ac_cv_lib_cblas_cblas_dgemm" >&6; } -if test "x$ac_cv_lib_cblas_cblas_dgemm" = xyes -then : - blas_ok=yes; BLASLIB="-lcblas -lf77blas -latlas" -fi - -fi - + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $LAPACK_PKG" >&5 +printf "%s\n" "$LAPACK_PKG" >&6; } -fi - -LDFLAGS="$save_LDFLAGS" + CFLAGS="$save_CFLAGS" + LIBS="$save_LIBS" -# Do we have ATLAS? -if test $blas_ok = yes; then -printf "%s\n" "#define HAVE_LIBATLAS 1" >>confdefs.h - -fi +printf "%s\n" "#define HAVE_LIBLAPACK 1" >>confdefs.h -# BLAS in Apple vecLib framework? (Mac OS-X) -# not dependent on APP build -#if test -n "$MACOSX_APP"; then -if test $blas_ok = no; then - vlib_flags="-framework vecLib" - save_LIBS="$LIBS" - LIBS="$vlib_flags $LIBS" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for sgemm in $vlib_flags" >&5 -printf %s "checking for sgemm in $vlib_flags... " >&6; } - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -int -main (void) -{ -sgemm - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO" -then : - blas_ok=yes; BLASLIB="$vlib_flags" -else $as_nop - BLASLIB="" -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam \ - conftest$ac_exeext conftest.$ac_ext - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $blas_ok" >&5 -printf "%s\n" "$blas_ok" >&6; } - LIBS="$save_LIBS" -fi -#fi # $MACOSX_APP - -# BLAS in PhiPACK libraries? (requires generic BLAS, too) -if test $blas_ok = no; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for sgemm_ in -lblas" >&5 -printf %s "checking for sgemm_ in -lblas... " >&6; } - -ac_check_lib_save_LIBS=$LIBS -LIBS="-lblas $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char sgemm_ (); -int -main (void) -{ -return sgemm_ (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO" -then : - ac_cv_lib_blas_sgemm_=yes -else $as_nop - ac_cv_lib_blas_sgemm_=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_blas_sgemm_" >&5 -printf "%s\n" "$ac_cv_lib_blas_sgemm_" >&6; } -if test "x$ac_cv_lib_blas_sgemm_" = xyes -then : - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for dgemm_ in -ldgemm" >&5 -printf %s "checking for dgemm_ in -ldgemm... " >&6; } - -ac_check_lib_save_LIBS=$LIBS -LIBS="-ldgemm -lblas $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char dgemm_ (); -int -main (void) -{ -return dgemm_ (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO" -then : - ac_cv_lib_dgemm_dgemm_=yes -else $as_nop - ac_cv_lib_dgemm_dgemm_=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dgemm_dgemm_" >&5 -printf "%s\n" "$ac_cv_lib_dgemm_dgemm_" >&6; } -if test "x$ac_cv_lib_dgemm_dgemm_" = xyes -then : - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for sgemm_ in -lsgemm" >&5 -printf %s "checking for sgemm_ in -lsgemm... " >&6; } - -ac_check_lib_save_LIBS=$LIBS -LIBS="-lsgemm -lblas $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char sgemm_ (); -int -main (void) -{ -return sgemm_ (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO" -then : - ac_cv_lib_sgemm_sgemm_=yes -else $as_nop - ac_cv_lib_sgemm_sgemm_=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_sgemm_sgemm_" >&5 -printf "%s\n" "$ac_cv_lib_sgemm_sgemm_" >&6; } -if test "x$ac_cv_lib_sgemm_sgemm_" = xyes -then : - blas_ok=yes; BLASLIB="-lsgemm -ldgemm -lblas" -fi - -fi - - -fi - -fi - -# BLAS in Sun Performance library? -if test $blas_ok = no; then - if test "x$GCC" != xyes; then # only works with Sun CC - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for acosp in -lsunmath" >&5 -printf %s "checking for acosp in -lsunmath... " >&6; } - -ac_check_lib_save_LIBS=$LIBS -LIBS="-lsunmath $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char acosp (); -int -main (void) -{ -return acosp (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO" -then : - ac_cv_lib_sunmath_acosp=yes -else $as_nop - ac_cv_lib_sunmath_acosp=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_sunmath_acosp" >&5 -printf "%s\n" "$ac_cv_lib_sunmath_acosp" >&6; } -if test "x$ac_cv_lib_sunmath_acosp" = xyes -then : - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for sgemm_ in -lsunperf" >&5 -printf %s "checking for sgemm_ in -lsunperf... " >&6; } - -ac_check_lib_save_LIBS=$LIBS -LIBS="-lsunperf -lsunmath $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char sgemm_ (); -int -main (void) -{ -return sgemm_ (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO" -then : - ac_cv_lib_sunperf_sgemm_=yes -else $as_nop - ac_cv_lib_sunperf_sgemm_=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_sunperf_sgemm_" >&5 -printf "%s\n" "$ac_cv_lib_sunperf_sgemm_" >&6; } -if test "x$ac_cv_lib_sunperf_sgemm_" = xyes -then : - blas_ok=yes; BLASLIB="-xlic_lib=sunperf -lsunmath" -fi - - -fi - - fi -fi - -# Generic BLAS library -if test $blas_ok = no; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for sgemm_ in -lblas" >&5 -printf %s "checking for sgemm_ in -lblas... " >&6; } - -ac_check_lib_save_LIBS=$LIBS -LIBS="-lblas $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char sgemm_ (); -int -main (void) -{ -return sgemm_ (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO" -then : - ac_cv_lib_blas_sgemm_=yes -else $as_nop - ac_cv_lib_blas_sgemm_=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_blas_sgemm_" >&5 -printf "%s\n" "$ac_cv_lib_blas_sgemm_" >&6; } -if test "x$ac_cv_lib_blas_sgemm_" = xyes -then : - blas_ok=yes; BLASLIB="-lblas" -fi - -fi - -if test $blas_ok = yes; then - -printf "%s\n" "#define HAVE_LIBBLAS 1" >>confdefs.h - -fi - -fi # $USE_BLAS - - - - -# Done checking BLAS - -# Enable LAPACK option - - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether to use LAPACK" >&5 -printf %s "checking whether to use LAPACK... " >&6; } -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: \"$with_lapack\"" >&5 -printf "%s\n" "\"$with_lapack\"" >&6; } -case "$with_lapack" in - "no") USE_LAPACK= ;; - "yes") USE_LAPACK="1" ;; - *) as_fn_error $? "*** You must answer yes or no." "$LINENO" 5 ;; -esac - - - -LAPACKLIB= -LAPACKINC= - -# LAPACK is useless without BLAS -if test -n "$USE_BLAS"; then -if test -n "$USE_LAPACK"; then - -# With LAPACK includes directory -# LAPACK doesn't have includes (FORTRAN library) -# With LAPACK library directory - -lapack_ok=no - - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for location of LAPACK includes" >&5 -printf %s "checking for location of LAPACK includes... " >&6; } -case "$with_lapack_includes" in -y | ye | yes | n | no) - as_fn_error $? "*** You must supply a directory to --with-lapack-includes." "$LINENO" 5 - ;; -esac -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $with_lapack_includes" >&5 -printf "%s\n" "$with_lapack_includes" >&6; } - -if test -n "$with_lapack_includes" ; then - for dir in $with_lapack_includes; do - if test -d "$dir"; then - LAPACKINC="$LAPACKINC -I$dir" - else - as_fn_error $? "*** LAPACK includes directory $dir does not exist." "$LINENO" 5 - fi - done -fi - - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for location of LAPACK library" >&5 -printf %s "checking for location of LAPACK library... " >&6; } -case "$with_lapack_libs" in -y | ye | yes | n | no) - as_fn_error $? "*** You must supply a directory to --with-lapack-libs." "$LINENO" 5 - ;; -esac -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $with_lapack_libs" >&5 -printf "%s\n" "$with_lapack_libs" >&6; } - -if test -n "$with_lapack_libs"; then - for dir in $with_lapack_libs; do - if test -d "$dir"; then - LAPACKLIB="$LAPACKLIB -L$dir" - else - as_fn_error $? "*** LAPACK library directory $dir does not exist." "$LINENO" 5 - fi - done -fi - - -# See if clapack.h exists (ATLAS) -save_CPPFLAGS="$CPPFLAGS" -CPPFLAGS="$LAPACKINC $CPPFLAGS" -ac_fn_c_check_header_compile "$LINENO" "clapack.h" "ac_cv_header_clapack_h" "$ac_includes_default" -if test "x$ac_cv_header_clapack_h" = xyes -then : - printf "%s\n" "#define HAVE_CLAPACK_H 1" >>confdefs.h - -fi - -CPPFLAGS="$save_CPPFLAGS" - -# LAPACK linked to by default? -if test lapack_ok=no; then - save_LIBS="$LIBS"; LIBS="$LIBS $BLASLIB $MATHLIB $FLIBS" - save_LDFLAGS="$LDFLAGS"; LDFLAGS="$LAPACKLIB $LDFLAGS" - ac_fn_c_check_func "$LINENO" "dsegv_" "ac_cv_func_dsegv_" -if test "x$ac_cv_func_dsegv_" = xyes -then : - lapack_ok=yes -fi - - LIBS="$save_LIBS" - LDFLAGS="$save_LDFLAGS" -fi - -# Generic LAPACK library? -if test $lapack_ok = no; then - save_libs="$LIBS"; LIBS="$BLASLIB $MATHLIB $LIBS" - save_LDFLAGS="$LDFLAGS"; LDFLAGS="$LAPACKLIB $LDFLAGS" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for desgv_ in -llapack" >&5 -printf %s "checking for desgv_ in -llapack... " >&6; } - -ac_check_lib_save_LIBS=$LIBS -LIBS="-llapack $FLIBS $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char desgv_ (); -int -main (void) -{ -return desgv_ (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO" -then : - ac_cv_lib_lapack_desgv_=yes -else $as_nop - ac_cv_lib_lapack_desgv_=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_lapack_desgv_" >&5 -printf "%s\n" "$ac_cv_lib_lapack_desgv_" >&6; } -if test "x$ac_cv_lib_lapack_desgv_" = xyes -then : - lapack_ok=yes; LAPACKLIB="-llapack" -fi - - LIBS="$save_LIBS" - LDFLAGS="$save_LDFLAGS" -fi - -if test $lapack_ok = no; then - -ac_save_ldflags="$LDFLAGS" -LDFLAGS="$LAPACKLIB $LDFLAGS" - - - - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for dgesv_ in -llapack" >&5 -printf %s "checking for dgesv_ in -llapack... " >&6; } - -ac_check_lib_save_LIBS=$LIBS -LIBS="-llapack $BLASLIB $MATHLIB $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char dgesv_ (); -int -main (void) -{ -return dgesv_ (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO" -then : - ac_cv_lib_lapack_dgesv_=yes -else $as_nop - ac_cv_lib_lapack_dgesv_=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_lapack_dgesv_" >&5 -printf "%s\n" "$ac_cv_lib_lapack_dgesv_" >&6; } -if test "x$ac_cv_lib_lapack_dgesv_" = xyes -then : - LAPACKLIB="$LAPACKLIB -llapack " -else $as_nop - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for dgesv_ in -llapack" >&5 -printf %s "checking for dgesv_ in -llapack... " >&6; } - -ac_check_lib_save_LIBS=$LIBS -LIBS="-llapack $BLASLIB $MATHLIB -lg2c $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char dgesv_ (); -int -main (void) -{ -return dgesv_ (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO" -then : - ac_cv_lib_lapack_dgesv_=yes -else $as_nop - ac_cv_lib_lapack_dgesv_=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_lapack_dgesv_" >&5 -printf "%s\n" "$ac_cv_lib_lapack_dgesv_" >&6; } -if test "x$ac_cv_lib_lapack_dgesv_" = xyes -then : - LAPACKLIB="$LAPACKLIB -llapack -lg2c" -else $as_nop - -LDFLAGS=${ac_save_ldflags} - - as_fn_error $? "*** Unable to locate LAPACK library." "$LINENO" 5 - - -fi - - - - -fi - - - - - - -LDFLAGS=${ac_save_ldflags} - -fi - - -printf "%s\n" "#define HAVE_LIBLAPACK 1" >>confdefs.h - - -fi # $USE_LAPACK -fi # $USE_BLAS +fi # $USE_BLAS $USE_LAPACK diff --git a/configure.ac b/configure.ac index 00403f1e665..8b7413c7cea 100644 --- a/configure.ac +++ b/configure.ac @@ -305,8 +305,6 @@ LOC_ARG_WITH(sqlite, SQLite) LOC_ARG_WITH(opengl, OpenGL) LOC_ARG_WITH(odbc, ODBC, no) LOC_ARG_WITH(fftw, FFTW) -LOC_ARG_WITH(blas, BLAS, no) -LOC_ARG_WITH(lapack, LAPACK, no) LOC_ARG_WITH(libsvm, LIBSVM, no) LOC_ARG_WITH(cairo, Cairo) LOC_ARG_WITH(freetype, FreeType) @@ -320,6 +318,19 @@ LOC_ARG_WITH(bzlib, BZIP2, no) LOC_ARG_WITH(zstd, Zstandard) LOC_ARG_WITH(pdal, PDAL) + +AC_ARG_WITH([blas], + [AS_HELP_STRING([--with-blas=pkg-config-package], + [enable BLAS support with by adding name of pkg-config package name + for CBLAS library, e.g. '--with-lapack-package=openblas', + default: cblas])],, [with-blas="no"]) + +AC_ARG_WITH([lapack], + [AS_HELP_STRING([--with-lapack=pkg-config-package], + [enable LAPACK support with by adding name of pkg-config package name + for LAPACKE library, e.g. '--with-lapack-package=openblas', + default: lapacke])],, [with-lapack="no"]) + AC_ARG_WITH(libpng, [ --with-libpng[=path/libpng-config] enable PNG support (libpng-config with path, @@ -390,12 +401,6 @@ LOC_ARG_WITH_LIB(odbc, ODBC) LOC_ARG_WITH_INC(fftw, FFTW) LOC_ARG_WITH_LIB(fftw, FFTW) -LOC_ARG_WITH_INC(blas, BLAS) -LOC_ARG_WITH_LIB(blas, BLAS) - -LOC_ARG_WITH_INC(lapack, LAPACK) -LOC_ARG_WITH_LIB(lapack, LAPACK) - LOC_ARG_WITH_INC(libsvm, LIBSVM) LOC_ARG_WITH_LIB(libsvm, LIBSVM) @@ -501,7 +506,7 @@ AC_CHECK_PROGS(ENV, env) AC_PATH_PROG(PERL, perl, no) #AC_CHECK_HEADERS(curses.h limits.h termio.h termios.h unistd.h values.h) -AC_CHECK_HEADERS(limits.h termio.h termios.h unistd.h values.h f2c.h g2c.h) +AC_CHECK_HEADERS(limits.h termio.h termios.h unistd.h values.h) AC_CHECK_HEADERS(sys/ioctl.h sys/mtio.h sys/resource.h sys/time.h) AC_CHECK_HEADERS(sys/timeb.h sys/types.h sys/utsname.h) AC_CHECK_HEADERS(libintl.h iconv.h) @@ -1576,91 +1581,67 @@ AC_SUBST(FFTWLIB) # Enable BLAS option -LOC_CHECK_USE(blas,BLAS,USE_BLAS) +AC_MSG_CHECKING([whether to use (C)BLAS]) +AC_MSG_RESULT([$with_blas]) BLASLIB= BLASINC= +USE_BLAS= +BLAS_PKGS="cblas blas-netlib openblas blas-atlas" +BLAS_PKG= + +case "$with_blas" in + no) + ;; + yes) + USE_BLAS="$with_blas" + ;; + *) + USE_BLAS=yes + BLAS_PKGS="$with_blas" + ;; +esac if test -n "$USE_BLAS"; then -# With BLAS includes directory -# BLAS doesn't have includes (FORTRAN library) -# With BLAS library directory - -LOC_CHECK_INC_PATH(blas, BLAS, BLASINC) -LOC_CHECK_LIB_PATH(blas, BLAS, BLASLIB) - -# See if cblas.h exists (CBLAS,ATLAS,others) -save_CPPFLAGS="$CPPFLAGS" -CPPFLAGS="$BLASINC $CPPFLAGS" -AC_CHECK_HEADERS(cblas.h) -CPPFLAGS="$save_CPPFLAGS" - -LOC_CHECK_LIBS(blas,dnrm2_,BLAS,$BLASLIB,BLASLIB,$MATHLIB,,,-lg2c) - -blas_ok=no - -# Check for ATLAS -save_LDFLAGS="$LDFLAGS" -LDFLAGS="$BLASLIB $LDFLAGS" -AC_CHECK_LIB(atlas, ATL_xerbla, - [AC_CHECK_LIB(f77blas, sgemm_, - [AC_CHECK_LIB(cblas, cblas_dgemm, - [blas_ok=yes; BLASLIB="-lcblas -lf77blas -latlas"], - [],[-lf77blas -latlas])], - [],[-latlas])] -) -LDFLAGS="$save_LDFLAGS" - -# Do we have ATLAS? -if test $blas_ok = yes; then - AC_DEFINE(HAVE_LIBATLAS, 1, [Define to 1 if ATLAS exists.]) -fi + blas_ok=no -# BLAS in Apple vecLib framework? (Mac OS-X) -# not dependent on APP build -#if test -n "$MACOSX_APP"; then -if test $blas_ok = no; then - vlib_flags="-framework vecLib" - save_LIBS="$LIBS" - LIBS="$vlib_flags $LIBS" - AC_MSG_CHECKING([for sgemm in $vlib_flags]) - AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[sgemm]])],[blas_ok=yes; BLASLIB="$vlib_flags"],[BLASLIB=""]) - AC_MSG_RESULT($blas_ok) - LIBS="$save_LIBS" -fi -#fi # $MACOSX_APP - -# BLAS in PhiPACK libraries? (requires generic BLAS, too) -if test $blas_ok = no; then - AC_CHECK_LIB(blas, sgemm_, - [AC_CHECK_LIB(dgemm, dgemm_, - [AC_CHECK_LIB(sgemm, sgemm_, - [blas_ok=yes; BLASLIB="-lsgemm -ldgemm -lblas"], - [], [-lblas])], - [], [-lblas])] - ) -fi + for blas_chk in ${BLAS_PKGS} + do + if ! ${PKG_CONFIG} --exists ${blas_chk} ; then + continue + else + BLAS_PKG=${blas_chk} + break + fi + done -# BLAS in Sun Performance library? -if test $blas_ok = no; then - if test "x$GCC" != xyes; then # only works with Sun CC - AC_CHECK_LIB(sunmath, acosp, - [AC_CHECK_LIB(sunperf, sgemm_, - [blas_ok=yes; BLASLIB="-xlic_lib=sunperf -lsunmath"], - [], [-lsunmath])] - ) - fi -fi + if test -z "$BLAS_PKG" ; then + AC_MSG_ERROR([*** Unable to locate (C)BLAS pkg-conf package(s) + "${BLAS_PKGS}". Perhaps you need to set PKG_CONFIG_PATH.]) + fi -# Generic BLAS library -if test $blas_ok = no; then - AC_CHECK_LIB(blas, sgemm_, [blas_ok=yes; BLASLIB="-lblas"]) -fi + BLASLIB=$(${PKG_CONFIG} --libs ${BLAS_PKG}) + BLASINC=$(${PKG_CONFIG} --cflags ${BLAS_PKG}) -if test $blas_ok = yes; then -AC_DEFINE(HAVE_LIBBLAS, 1, [Define to 1 if BLAS exists.]) -fi + save_CFLAGS="$CFLAGS"; CFLAGS="$BLASINC $CFLAGS" + save_LIBS="$LIBS"; LIBS="$BLASLIB" + + AC_CHECK_HEADERS([cblas.h],, + AC_CHECK_HEADERS([cblas-atlas.h],, + AC_MSG_ERROR([*** Failed to find either "cblas.h" or "cblas-atlas.h" with "$CFLAGS"]))) + + AC_MSG_CHECKING([for linking to (C)BLAS]) + AC_LINK_IFELSE([AC_LANG_PROGRAM( + [[char cblas_dgemm();]], [[return cblas_dgemm();]])], + [blas_ok=yes] , [ + AC_MSG_ERROR([*** Unable to link to (C)BLAS library with "$LIBS".])]) + AC_MSG_RESULT([$BLAS_PKG]) + + CFLAGS="$save_CFLAGS" + LIBS="$save_LIBS" + + AC_DEFINE(HAVE_LIBBLAS, 1, [Define to 1 if BLAS exists.]) fi # $USE_BLAS @@ -1671,57 +1652,69 @@ AC_SUBST(BLASINC) # Enable LAPACK option -LOC_CHECK_USE(lapack,LAPACK,USE_LAPACK) +AC_MSG_CHECKING([whether to use LAPACK(E)]) +AC_MSG_RESULT([$with_lapack]) LAPACKLIB= LAPACKINC= +USE_LAPACK= +LAPACK_PKGS="lapacke openblas" +LAPACK_PKG= + +case "$with_lapack" in + no) + ;; + yes) + USE_LAPACK="$with_lapack" + ;; + *) + USE_LAPACK=yes + LAPACK_PKG="$with_lapack" + ;; +esac # LAPACK is useless without BLAS -if test -n "$USE_BLAS"; then -if test -n "$USE_LAPACK"; then - -# With LAPACK includes directory -# LAPACK doesn't have includes (FORTRAN library) -# With LAPACK library directory - -lapack_ok=no - -LOC_CHECK_INC_PATH(lapack, LAPACK, LAPACKINC) -LOC_CHECK_LIB_PATH(lapack, LAPACK, LAPACKLIB) - -# See if clapack.h exists (ATLAS) -save_CPPFLAGS="$CPPFLAGS" -CPPFLAGS="$LAPACKINC $CPPFLAGS" -AC_CHECK_HEADERS(clapack.h) -CPPFLAGS="$save_CPPFLAGS" - -# LAPACK linked to by default? -if test lapack_ok=no; then - save_LIBS="$LIBS"; LIBS="$LIBS $BLASLIB $MATHLIB $FLIBS" - save_LDFLAGS="$LDFLAGS"; LDFLAGS="$LAPACKLIB $LDFLAGS" - AC_CHECK_FUNC(dsegv_, [lapack_ok=yes]) - LIBS="$save_LIBS" - LDFLAGS="$save_LDFLAGS" -fi +if test -n "$USE_BLAS" && test -n "$USE_LAPACK"; then -# Generic LAPACK library? -if test $lapack_ok = no; then - save_libs="$LIBS"; LIBS="$BLASLIB $MATHLIB $LIBS" - save_LDFLAGS="$LDFLAGS"; LDFLAGS="$LAPACKLIB $LDFLAGS" - AC_CHECK_LIB(lapack, desgv_, - [lapack_ok=yes; LAPACKLIB="-llapack"], [], [$FLIBS]) - LIBS="$save_LIBS" - LDFLAGS="$save_LDFLAGS" -fi + lapack_ok=no -if test $lapack_ok = no; then - LOC_CHECK_LIBS(lapack,dgesv_,LAPACK,$LAPACKLIB,LAPACKLIB,$BLASLIB $MATHLIB,,,-lg2c) -fi + for lapack_chk in ${LAPACK_PKGS} + do + if ! ${PKG_CONFIG} --exists ${lapack_chk} ; then + continue + else + LAPACK_PKG=${lapack_chk} + break + fi + done -AC_DEFINE(HAVE_LIBLAPACK, 1, [Define to 1 if LAPACK exists.]) + if test -z "$LAPACK_PKG" ; then + AC_MSG_ERROR([*** Unable to locate LAPACK(E) pkg-conf package(s) + "$LAPACK_PKGS". Perhaps you need to set PKG_CONFIG_PATH.]) + fi -fi # $USE_LAPACK -fi # $USE_BLAS + LAPACKLIB=`${PKG_CONFIG} --libs ${LAPACK_PKG}` + LAPACKINC=`${PKG_CONFIG} --cflags ${LAPACK_PKG}` + + save_CFLAGS="$CFLAGS"; CFLAGS="$LAPACKINC $CFLAGS" + save_LIBS="$LIBS"; LIBS="$LAPACKLIB" + + AC_CHECK_HEADERS([lapacke.h], [], + AC_MSG_ERROR([*** Unable to find "lapacke.h" with "$CFLAGS".])) + + AC_MSG_CHECKING([for linking to LAPACK(E)]) + AC_LINK_IFELSE([AC_LANG_PROGRAM( + [[char LAPACKE_dgesv();]], [[return LAPACKE_dgesv(); ]])], + [lapack_ok=yes] , [ + AC_MSG_ERROR([*** Unable to link to LAPACK(E) library with "$LIBS".])]) + AC_MSG_RESULT([$LAPACK_PKG]) + + CFLAGS="$save_CFLAGS" + LIBS="$save_LIBS" + + AC_DEFINE(HAVE_LIBLAPACK, 1, [Define to 1 if LAPACK exists.]) + +fi # $USE_BLAS $USE_LAPACK AC_SUBST(LAPACKLIB) AC_SUBST(LAPACKINC) diff --git a/include/grass/blas.h b/include/grass/blas.h deleted file mode 100644 index 2e7fe0a31f2..00000000000 --- a/include/grass/blas.h +++ /dev/null @@ -1,412 +0,0 @@ -#ifndef BLAS_WRAP_H -#define BLAS_WRAP_H - -extern int caxpy_(integer *n, complex *ca, complex *cx, integer *incx, - complex *cy, integer *incy); -extern int ccopy_(integer *n, complex *cx, integer *incx, complex *cy, - integer *incy); -extern C_f cdotc_(complex *ret_val, integer *n, complex *cx, integer *incx, - complex *cy, integer *incy); -extern C_f cdotu_(complex *ret_val, integer *n, complex *cx, integer *incx, - complex *cy, integer *incy); -extern int cgbmv_(char *trans, integer *m, integer *n, integer *kl, integer *ku, - complex *alpha, complex *a, integer *lda, complex *x, - integer *incx, complex *beta, complex *y, integer *incy, - ftnlen trans_len); -extern int cgemm_(char *transa, char *transb, integer *m, integer *n, - integer *k, complex *alpha, complex *a, integer *lda, - complex *b, integer *ldb, complex *beta, complex *c__, - integer *ldc, ftnlen transa_len, ftnlen transb_len); -extern int cgemv_(char *trans, integer *m, integer *n, complex *alpha, - complex *a, integer *lda, complex *x, integer *incx, - complex *beta, complex *y, integer *incy, ftnlen trans_len); -extern int cgerc_(integer *m, integer *n, complex *alpha, complex *x, - integer *incx, complex *y, integer *incy, complex *a, - integer *lda); -extern int cgeru_(integer *m, integer *n, complex *alpha, complex *x, - integer *incx, complex *y, integer *incy, complex *a, - integer *lda); -extern int chbmv_(char *uplo, integer *n, integer *k, complex *alpha, - complex *a, integer *lda, complex *x, integer *incx, - complex *beta, complex *y, integer *incy, ftnlen uplo_len); -extern int chemm_(char *side, char *uplo, integer *m, integer *n, - complex *alpha, complex *a, integer *lda, complex *b, - integer *ldb, complex *beta, complex *c__, integer *ldc, - ftnlen side_len, ftnlen uplo_len); -extern int chemv_(char *uplo, integer *n, complex *alpha, complex *a, - integer *lda, complex *x, integer *incx, complex *beta, - complex *y, integer *incy, ftnlen uplo_len); -extern int cher_(char *uplo, integer *n, real *alpha, complex *x, integer *incx, - complex *a, integer *lda, ftnlen uplo_len); -extern int cher2_(char *uplo, integer *n, complex *alpha, complex *x, - integer *incx, complex *y, integer *incy, complex *a, - integer *lda, ftnlen uplo_len); -extern int cher2k_(char *uplo, char *trans, integer *n, integer *k, - complex *alpha, complex *a, integer *lda, complex *b, - integer *ldb, real *beta, complex *c__, integer *ldc, - ftnlen uplo_len, ftnlen trans_len); -extern int cherk_(char *uplo, char *trans, integer *n, integer *k, real *alpha, - complex *a, integer *lda, real *beta, complex *c__, - integer *ldc, ftnlen uplo_len, ftnlen trans_len); -extern int chpmv_(char *uplo, integer *n, complex *alpha, complex *ap, - complex *x, integer *incx, complex *beta, complex *y, - integer *incy, ftnlen uplo_len); -extern int chpr_(char *uplo, integer *n, real *alpha, complex *x, integer *incx, - complex *ap, ftnlen uplo_len); -extern int chpr2_(char *uplo, integer *n, complex *alpha, complex *x, - integer *incx, complex *y, integer *incy, complex *ap, - ftnlen uplo_len); -extern int crotg_(complex *ca, complex *cb, real *c__, complex *s); -extern int cscal_(integer *n, complex *ca, complex *cx, integer *incx); -extern int csrot_(integer *n, complex *cx, integer *incx, complex *cy, - integer *incy, real *c__, real *s); -extern int csscal_(integer *n, real *sa, complex *cx, integer *incx); -extern int cswap_(integer *n, complex *cx, integer *incx, complex *cy, - integer *incy); -extern int csymm_(char *side, char *uplo, integer *m, integer *n, - complex *alpha, complex *a, integer *lda, complex *b, - integer *ldb, complex *beta, complex *c__, integer *ldc, - ftnlen side_len, ftnlen uplo_len); -extern int csyr2k_(char *uplo, char *trans, integer *n, integer *k, - complex *alpha, complex *a, integer *lda, complex *b, - integer *ldb, complex *beta, complex *c__, integer *ldc, - ftnlen uplo_len, ftnlen trans_len); -extern int csyrk_(char *uplo, char *trans, integer *n, integer *k, - complex *alpha, complex *a, integer *lda, complex *beta, - complex *c__, integer *ldc, ftnlen uplo_len, - ftnlen trans_len); -extern int ctbmv_(char *uplo, char *trans, char *diag, integer *n, integer *k, - complex *a, integer *lda, complex *x, integer *incx, - ftnlen uplo_len, ftnlen trans_len, ftnlen diag_len); -extern int ctbsv_(char *uplo, char *trans, char *diag, integer *n, integer *k, - complex *a, integer *lda, complex *x, integer *incx, - ftnlen uplo_len, ftnlen trans_len, ftnlen diag_len); -extern int ctpmv_(char *uplo, char *trans, char *diag, integer *n, complex *ap, - complex *x, integer *incx, ftnlen uplo_len, ftnlen trans_len, - ftnlen diag_len); -extern int ctpsv_(char *uplo, char *trans, char *diag, integer *n, complex *ap, - complex *x, integer *incx, ftnlen uplo_len, ftnlen trans_len, - ftnlen diag_len); -extern int ctrmm_(char *side, char *uplo, char *transa, char *diag, integer *m, - integer *n, complex *alpha, complex *a, integer *lda, - complex *b, integer *ldb, ftnlen side_len, ftnlen uplo_len, - ftnlen transa_len, ftnlen diag_len); -extern int ctrmv_(char *uplo, char *trans, char *diag, integer *n, complex *a, - integer *lda, complex *x, integer *incx, ftnlen uplo_len, - ftnlen trans_len, ftnlen diag_len); -extern int ctrsm_(char *side, char *uplo, char *transa, char *diag, integer *m, - integer *n, complex *alpha, complex *a, integer *lda, - complex *b, integer *ldb, ftnlen side_len, ftnlen uplo_len, - ftnlen transa_len, ftnlen diag_len); -extern int ctrsv_(char *uplo, char *trans, char *diag, integer *n, complex *a, - integer *lda, complex *x, integer *incx, ftnlen uplo_len, - ftnlen trans_len, ftnlen diag_len); -extern doublereal dasum_(integer *n, doublereal *dx, integer *incx); -extern int daxpy_(integer *n, doublereal *da, doublereal *dx, integer *incx, - doublereal *dy, integer *incy); -extern doublereal dcabs1_(doublecomplex *z__); -extern int dcopy_(integer *n, doublereal *dx, integer *incx, doublereal *dy, - integer *incy); -extern doublereal ddot_(integer *n, doublereal *dx, integer *incx, - doublereal *dy, integer *incy); -extern int dgbmv_(char *trans, integer *m, integer *n, integer *kl, integer *ku, - doublereal *alpha, doublereal *a, integer *lda, doublereal *x, - integer *incx, doublereal *beta, doublereal *y, integer *incy, - ftnlen trans_len); -extern int dgemm_(char *transa, char *transb, integer *m, integer *n, - integer *k, doublereal *alpha, doublereal *a, integer *lda, - doublereal *b, integer *ldb, doublereal *beta, - doublereal *c__, integer *ldc); -extern int dgemv_(char *trans, integer *m, integer *n, doublereal *alpha, - doublereal *a, integer *lda, doublereal *x, integer *incx, - doublereal *beta, doublereal *y, integer *incy, - ftnlen trans_len); -extern int dger_(integer *m, integer *n, doublereal *alpha, doublereal *x, - integer *incx, doublereal *y, integer *incy, doublereal *a, - integer *lda); -extern doublereal dnrm2_(integer *n, doublereal *x, integer *incx); -extern int drot_(integer *n, doublereal *dx, integer *incx, doublereal *dy, - integer *incy, doublereal *c__, doublereal *s); -extern int drotg_(doublereal *da, doublereal *db, doublereal *c__, - doublereal *s); -extern int drotm_(integer *n, doublereal *dx, integer *incx, doublereal *dy, - integer *incy, doublereal *dparam); -extern int drotmg_(doublereal *dd1, doublereal *dd2, doublereal *dx1, - doublereal *dy1, doublereal *dparam); -extern int dsbmv_(char *uplo, integer *n, integer *k, doublereal *alpha, - doublereal *a, integer *lda, doublereal *x, integer *incx, - doublereal *beta, doublereal *y, integer *incy, - ftnlen uplo_len); -extern int dscal_(integer *n, doublereal *da, doublereal *dx, integer *incx); -extern doublereal dsdot_(integer *n, real *sx, integer *incx, real *sy, - integer *incy); -extern int dspmv_(char *uplo, integer *n, doublereal *alpha, doublereal *ap, - doublereal *x, integer *incx, doublereal *beta, doublereal *y, - integer *incy, ftnlen uplo_len); -extern int dspr_(char *uplo, integer *n, doublereal *alpha, doublereal *x, - integer *incx, doublereal *ap, ftnlen uplo_len); -extern int dspr2_(char *uplo, integer *n, doublereal *alpha, doublereal *x, - integer *incx, doublereal *y, integer *incy, doublereal *ap, - ftnlen uplo_len); -extern int dswap_(integer *n, doublereal *dx, integer *incx, doublereal *dy, - integer *incy); -extern int dsymm_(char *side, char *uplo, integer *m, integer *n, - doublereal *alpha, doublereal *a, integer *lda, doublereal *b, - integer *ldb, doublereal *beta, doublereal *c__, integer *ldc, - ftnlen side_len, ftnlen uplo_len); -extern int dsymv_(char *uplo, integer *n, doublereal *alpha, doublereal *a, - integer *lda, doublereal *x, integer *incx, doublereal *beta, - doublereal *y, integer *incy, ftnlen uplo_len); -extern int dsyr_(char *uplo, integer *n, doublereal *alpha, doublereal *x, - integer *incx, doublereal *a, integer *lda, ftnlen uplo_len); -extern int dsyr2_(char *uplo, integer *n, doublereal *alpha, doublereal *x, - integer *incx, doublereal *y, integer *incy, doublereal *a, - integer *lda, ftnlen uplo_len); -extern int dsyr2k_(char *uplo, char *trans, integer *n, integer *k, - doublereal *alpha, doublereal *a, integer *lda, - doublereal *b, integer *ldb, doublereal *beta, - doublereal *c__, integer *ldc, ftnlen uplo_len, - ftnlen trans_len); -extern int dsyrk_(char *uplo, char *trans, integer *n, integer *k, - doublereal *alpha, doublereal *a, integer *lda, - doublereal *beta, doublereal *c__, integer *ldc, - ftnlen uplo_len, ftnlen trans_len); -extern int dtbmv_(char *uplo, char *trans, char *diag, integer *n, integer *k, - doublereal *a, integer *lda, doublereal *x, integer *incx, - ftnlen uplo_len, ftnlen trans_len, ftnlen diag_len); -extern int dtbsv_(char *uplo, char *trans, char *diag, integer *n, integer *k, - doublereal *a, integer *lda, doublereal *x, integer *incx, - ftnlen uplo_len, ftnlen trans_len, ftnlen diag_len); -extern int dtpmv_(char *uplo, char *trans, char *diag, integer *n, - doublereal *ap, doublereal *x, integer *incx, ftnlen uplo_len, - ftnlen trans_len, ftnlen diag_len); -extern int dtpsv_(char *uplo, char *trans, char *diag, integer *n, - doublereal *ap, doublereal *x, integer *incx, ftnlen uplo_len, - ftnlen trans_len, ftnlen diag_len); -extern int dtrmm_(char *side, char *uplo, char *transa, char *diag, integer *m, - integer *n, doublereal *alpha, doublereal *a, integer *lda, - doublereal *b, integer *ldb, ftnlen side_len, ftnlen uplo_len, - ftnlen transa_len, ftnlen diag_len); -extern int dtrmv_(char *uplo, char *trans, char *diag, integer *n, - doublereal *a, integer *lda, doublereal *x, integer *incx, - ftnlen uplo_len, ftnlen trans_len, ftnlen diag_len); -extern int dtrsm_(char *side, char *uplo, char *transa, char *diag, integer *m, - integer *n, doublereal *alpha, doublereal *a, integer *lda, - doublereal *b, integer *ldb, ftnlen side_len, ftnlen uplo_len, - ftnlen transa_len, ftnlen diag_len); -extern int dtrsv_(char *uplo, char *trans, char *diag, integer *n, - doublereal *a, integer *lda, doublereal *x, integer *incx, - ftnlen uplo_len, ftnlen trans_len, ftnlen diag_len); -extern doublereal dzasum_(integer *n, doublecomplex *zx, integer *incx); -extern doublereal dznrm2_(integer *n, doublecomplex *x, integer *incx); -extern integer icamax_(integer *n, complex *cx, integer *incx); -extern integer idamax_(integer *n, doublereal *dx, integer *incx); -extern integer isamax_(integer *n, real *sx, integer *incx); -extern integer izamax_(integer *n, doublecomplex *zx, integer *incx); -extern logical lsame_(char *ca, char *cb, ftnlen ca_len, ftnlen cb_len); -extern E_f sasum_(integer *n, real *sx, integer *incx); -extern int saxpy_(integer *n, real *sa, real *sx, integer *incx, real *sy, - integer *incy); -extern E_f scasum_(integer *n, complex *cx, integer *incx); -extern E_f scnrm2_(integer *n, complex *x, integer *incx); -extern int scopy_(integer *n, real *sx, integer *incx, real *sy, integer *incy); -extern E_f sdot_(integer *n, real *sx, integer *incx, real *sy, integer *incy); -extern E_f sdsdot_(integer *n, real *sb, real *sx, integer *incx, real *sy, - integer *incy); -extern int sgbmv_(char *trans, integer *m, integer *n, integer *kl, integer *ku, - real *alpha, real *a, integer *lda, real *x, integer *incx, - real *beta, real *y, integer *incy, ftnlen trans_len); -extern int sgemm_(char *transa, char *transb, integer *m, integer *n, - integer *k, real *alpha, real *a, integer *lda, real *b, - integer *ldb, real *beta, real *c__, integer *ldc, - ftnlen transa_len, ftnlen transb_len); -extern int sgemv_(char *trans, integer *m, integer *n, real *alpha, real *a, - integer *lda, real *x, integer *incx, real *beta, real *y, - integer *incy, ftnlen trans_len); -extern int sger_(integer *m, integer *n, real *alpha, real *x, integer *incx, - real *y, integer *incy, real *a, integer *lda); -extern E_f snrm2_(integer *n, real *x, integer *incx); -extern int srot_(integer *n, real *sx, integer *incx, real *sy, integer *incy, - real *c__, real *s); -extern int srotg_(real *sa, real *sb, real *c__, real *s); -extern int srotm_(integer *n, real *sx, integer *incx, real *sy, integer *incy, - real *sparam); -extern int srotmg_(real *sd1, real *sd2, real *sx1, real *sy1, real *sparam); -extern int ssbmv_(char *uplo, integer *n, integer *k, real *alpha, real *a, - integer *lda, real *x, integer *incx, real *beta, real *y, - integer *incy, ftnlen uplo_len); -extern int sscal_(integer *n, real *sa, real *sx, integer *incx); -extern int sspmv_(char *uplo, integer *n, real *alpha, real *ap, real *x, - integer *incx, real *beta, real *y, integer *incy, - ftnlen uplo_len); -extern int sspr_(char *uplo, integer *n, real *alpha, real *x, integer *incx, - real *ap, ftnlen uplo_len); -extern int sspr2_(char *uplo, integer *n, real *alpha, real *x, integer *incx, - real *y, integer *incy, real *ap, ftnlen uplo_len); -extern int sswap_(integer *n, real *sx, integer *incx, real *sy, integer *incy); -extern int ssymm_(char *side, char *uplo, integer *m, integer *n, real *alpha, - real *a, integer *lda, real *b, integer *ldb, real *beta, - real *c__, integer *ldc, ftnlen side_len, ftnlen uplo_len); -extern int ssymv_(char *uplo, integer *n, real *alpha, real *a, integer *lda, - real *x, integer *incx, real *beta, real *y, integer *incy, - ftnlen uplo_len); -extern int ssyr_(char *uplo, integer *n, real *alpha, real *x, integer *incx, - real *a, integer *lda, ftnlen uplo_len); -extern int ssyr2_(char *uplo, integer *n, real *alpha, real *x, integer *incx, - real *y, integer *incy, real *a, integer *lda, - ftnlen uplo_len); -extern int ssyr2k_(char *uplo, char *trans, integer *n, integer *k, real *alpha, - real *a, integer *lda, real *b, integer *ldb, real *beta, - real *c__, integer *ldc, ftnlen uplo_len, ftnlen trans_len); -extern int ssyrk_(char *uplo, char *trans, integer *n, integer *k, real *alpha, - real *a, integer *lda, real *beta, real *c__, integer *ldc, - ftnlen uplo_len, ftnlen trans_len); -extern int stbmv_(char *uplo, char *trans, char *diag, integer *n, integer *k, - real *a, integer *lda, real *x, integer *incx, - ftnlen uplo_len, ftnlen trans_len, ftnlen diag_len); -extern int stbsv_(char *uplo, char *trans, char *diag, integer *n, integer *k, - real *a, integer *lda, real *x, integer *incx, - ftnlen uplo_len, ftnlen trans_len, ftnlen diag_len); -extern int stpmv_(char *uplo, char *trans, char *diag, integer *n, real *ap, - real *x, integer *incx, ftnlen uplo_len, ftnlen trans_len, - ftnlen diag_len); -extern int stpsv_(char *uplo, char *trans, char *diag, integer *n, real *ap, - real *x, integer *incx, ftnlen uplo_len, ftnlen trans_len, - ftnlen diag_len); -extern int strmm_(char *side, char *uplo, char *transa, char *diag, integer *m, - integer *n, real *alpha, real *a, integer *lda, real *b, - integer *ldb, ftnlen side_len, ftnlen uplo_len, - ftnlen transa_len, ftnlen diag_len); -extern int strmv_(char *uplo, char *trans, char *diag, integer *n, real *a, - integer *lda, real *x, integer *incx, ftnlen uplo_len, - ftnlen trans_len, ftnlen diag_len); -extern int strsm_(char *side, char *uplo, char *transa, char *diag, integer *m, - integer *n, real *alpha, real *a, integer *lda, real *b, - integer *ldb, ftnlen side_len, ftnlen uplo_len, - ftnlen transa_len, ftnlen diag_len); -extern int strsv_(char *uplo, char *trans, char *diag, integer *n, real *a, - integer *lda, real *x, integer *incx, ftnlen uplo_len, - ftnlen trans_len, ftnlen diag_len); -extern int xerbla_(char *srname, integer *info, ftnlen srname_len); -extern int zaxpy_(integer *n, doublecomplex *za, doublecomplex *zx, - integer *incx, doublecomplex *zy, integer *incy); -extern int zcopy_(integer *n, doublecomplex *zx, integer *incx, - doublecomplex *zy, integer *incy); -extern Z_f zdotc_(doublecomplex *ret_val, integer *n, doublecomplex *zx, - integer *incx, doublecomplex *zy, integer *incy); -extern Z_f zdotu_(doublecomplex *ret_val, integer *n, doublecomplex *zx, - integer *incx, doublecomplex *zy, integer *incy); -extern int zdrot_(integer *n, doublecomplex *zx, integer *incx, - doublecomplex *zy, integer *incy, doublereal *c__, - doublereal *s); -extern int zdscal_(integer *n, doublereal *da, doublecomplex *zx, - integer *incx); -extern int zgbmv_(char *trans, integer *m, integer *n, integer *kl, integer *ku, - doublecomplex *alpha, doublecomplex *a, integer *lda, - doublecomplex *x, integer *incx, doublecomplex *beta, - doublecomplex *y, integer *incy, ftnlen trans_len); -extern int zgemm_(char *transa, char *transb, integer *m, integer *n, - integer *k, doublecomplex *alpha, doublecomplex *a, - integer *lda, doublecomplex *b, integer *ldb, - doublecomplex *beta, doublecomplex *c__, integer *ldc, - ftnlen transa_len, ftnlen transb_len); -extern int zgemv_(char *trans, integer *m, integer *n, doublecomplex *alpha, - doublecomplex *a, integer *lda, doublecomplex *x, - integer *incx, doublecomplex *beta, doublecomplex *y, - integer *incy, ftnlen trans_len); -extern int zgerc_(integer *m, integer *n, doublecomplex *alpha, - doublecomplex *x, integer *incx, doublecomplex *y, - integer *incy, doublecomplex *a, integer *lda); -extern int zgeru_(integer *m, integer *n, doublecomplex *alpha, - doublecomplex *x, integer *incx, doublecomplex *y, - integer *incy, doublecomplex *a, integer *lda); -extern int zhbmv_(char *uplo, integer *n, integer *k, doublecomplex *alpha, - doublecomplex *a, integer *lda, doublecomplex *x, - integer *incx, doublecomplex *beta, doublecomplex *y, - integer *incy, ftnlen uplo_len); -extern int zhemm_(char *side, char *uplo, integer *m, integer *n, - doublecomplex *alpha, doublecomplex *a, integer *lda, - doublecomplex *b, integer *ldb, doublecomplex *beta, - doublecomplex *c__, integer *ldc, ftnlen side_len, - ftnlen uplo_len); -extern int zhemv_(char *uplo, integer *n, doublecomplex *alpha, - doublecomplex *a, integer *lda, doublecomplex *x, - integer *incx, doublecomplex *beta, doublecomplex *y, - integer *incy, ftnlen uplo_len); -extern int zher_(char *uplo, integer *n, doublereal *alpha, doublecomplex *x, - integer *incx, doublecomplex *a, integer *lda, - ftnlen uplo_len); -extern int zher2_(char *uplo, integer *n, doublecomplex *alpha, - doublecomplex *x, integer *incx, doublecomplex *y, - integer *incy, doublecomplex *a, integer *lda, - ftnlen uplo_len); -extern int zher2k_(char *uplo, char *trans, integer *n, integer *k, - doublecomplex *alpha, doublecomplex *a, integer *lda, - doublecomplex *b, integer *ldb, doublereal *beta, - doublecomplex *c__, integer *ldc, ftnlen uplo_len, - ftnlen trans_len); -extern int zherk_(char *uplo, char *trans, integer *n, integer *k, - doublereal *alpha, doublecomplex *a, integer *lda, - doublereal *beta, doublecomplex *c__, integer *ldc, - ftnlen uplo_len, ftnlen trans_len); -extern int zhpmv_(char *uplo, integer *n, doublecomplex *alpha, - doublecomplex *ap, doublecomplex *x, integer *incx, - doublecomplex *beta, doublecomplex *y, integer *incy, - ftnlen uplo_len); -extern int zhpr_(char *uplo, integer *n, doublereal *alpha, doublecomplex *x, - integer *incx, doublecomplex *ap, ftnlen uplo_len); -extern int zhpr2_(char *uplo, integer *n, doublecomplex *alpha, - doublecomplex *x, integer *incx, doublecomplex *y, - integer *incy, doublecomplex *ap, ftnlen uplo_len); -extern int zrotg_(doublecomplex *ca, doublecomplex *cb, doublereal *c__, - doublecomplex *s); -extern int zscal_(integer *n, doublecomplex *za, doublecomplex *zx, - integer *incx); -extern int zswap_(integer *n, doublecomplex *zx, integer *incx, - doublecomplex *zy, integer *incy); -extern int zsymm_(char *side, char *uplo, integer *m, integer *n, - doublecomplex *alpha, doublecomplex *a, integer *lda, - doublecomplex *b, integer *ldb, doublecomplex *beta, - doublecomplex *c__, integer *ldc, ftnlen side_len, - ftnlen uplo_len); -extern int zsyr2k_(char *uplo, char *trans, integer *n, integer *k, - doublecomplex *alpha, doublecomplex *a, integer *lda, - doublecomplex *b, integer *ldb, doublecomplex *beta, - doublecomplex *c__, integer *ldc, ftnlen uplo_len, - ftnlen trans_len); -extern int zsyrk_(char *uplo, char *trans, integer *n, integer *k, - doublecomplex *alpha, doublecomplex *a, integer *lda, - doublecomplex *beta, doublecomplex *c__, integer *ldc, - ftnlen uplo_len, ftnlen trans_len); -extern int ztbmv_(char *uplo, char *trans, char *diag, integer *n, integer *k, - doublecomplex *a, integer *lda, doublecomplex *x, - integer *incx, ftnlen uplo_len, ftnlen trans_len, - ftnlen diag_len); -extern int ztbsv_(char *uplo, char *trans, char *diag, integer *n, integer *k, - doublecomplex *a, integer *lda, doublecomplex *x, - integer *incx, ftnlen uplo_len, ftnlen trans_len, - ftnlen diag_len); -extern int ztpmv_(char *uplo, char *trans, char *diag, integer *n, - doublecomplex *ap, doublecomplex *x, integer *incx, - ftnlen uplo_len, ftnlen trans_len, ftnlen diag_len); -extern int ztpsv_(char *uplo, char *trans, char *diag, integer *n, - doublecomplex *ap, doublecomplex *x, integer *incx, - ftnlen uplo_len, ftnlen trans_len, ftnlen diag_len); -extern int ztrmm_(char *side, char *uplo, char *transa, char *diag, integer *m, - integer *n, doublecomplex *alpha, doublecomplex *a, - integer *lda, doublecomplex *b, integer *ldb, ftnlen side_len, - ftnlen uplo_len, ftnlen transa_len, ftnlen diag_len); -extern int ztrmv_(char *uplo, char *trans, char *diag, integer *n, - doublecomplex *a, integer *lda, doublecomplex *x, - integer *incx, ftnlen uplo_len, ftnlen trans_len, - ftnlen diag_len); -extern int ztrsm_(char *side, char *uplo, char *transa, char *diag, integer *m, - integer *n, doublecomplex *alpha, doublecomplex *a, - integer *lda, doublecomplex *b, integer *ldb, ftnlen side_len, - ftnlen uplo_len, ftnlen transa_len, ftnlen diag_len); -extern int ztrsv_(char *uplo, char *trans, char *diag, integer *n, - doublecomplex *a, integer *lda, doublecomplex *x, - integer *incx, ftnlen uplo_len, ftnlen trans_len, - ftnlen diag_len); - -#endif diff --git a/include/grass/config.h.in b/include/grass/config.h.in index 95dc2bc034b..2451d8cc2b8 100644 --- a/include/grass/config.h.in +++ b/include/grass/config.h.in @@ -20,12 +20,12 @@ /* Define to 1 if you have the header file. */ #undef HAVE_CAIRO_H +/* Define to 1 if you have the header file. */ +#undef HAVE_CBLAS_ATLAS_H + /* Define to 1 if you have the header file. */ #undef HAVE_CBLAS_H -/* Define to 1 if you have the header file. */ -#undef HAVE_CLAPACK_H - /* Define to 1 if you have the header file. */ #undef HAVE_CL_CL_H @@ -35,9 +35,6 @@ /* Define to 1 if you have the `drand48' function. */ #undef HAVE_DRAND48 -/* Define to 1 if you have the header file. */ -#undef HAVE_F2C_H - /* Define to 1 if you have the header file. */ #undef HAVE_FFTW3_H @@ -53,9 +50,6 @@ /* Define to 1 if you have the `ftime' function. */ #undef HAVE_FTIME -/* Define to 1 if you have the header file. */ -#undef HAVE_G2C_H - /* Define to 1 if GDAL is to be used. */ #undef HAVE_GDAL @@ -89,8 +83,8 @@ /* Define to 1 if you have the header file. */ #undef HAVE_LANGINFO_H -/* Define to 1 if ATLAS exists. */ -#undef HAVE_LIBATLAS +/* Define to 1 if you have the header file. */ +#undef HAVE_LAPACKE_H /* Define to 1 if BLAS exists. */ #undef HAVE_LIBBLAS diff --git a/include/grass/la.h b/include/grass/la.h index 2981dc3401e..edec847c8e1 100644 --- a/include/grass/la.h +++ b/include/grass/la.h @@ -23,93 +23,9 @@ #ifndef GRASS_LA_H #define GRASS_LA_H -/* QUESTION: On some systems there appears to be no default link - to this. Do we create a symlink to - /usr/lib/gcc-lib///include/g2c.h - - or link to any old f2c.h that happens to hanging around? - - A job for autoconf - - [Also a consideration for -lg2c] - */ - #include #include -#ifdef HAVE_G2C_H -typedef int __g77_integer; -typedef unsigned int __g77_uinteger; -typedef long int __g77_longint; -typedef unsigned long int __g77_ulongint; - -#include -#else /* for gcc4+ */ -typedef int integer; -typedef unsigned int uinteger; -typedef char *address; -typedef short shortint; -typedef float real; -typedef double doublereal; -typedef struct { - real r, i; -} complex; -typedef struct { - doublereal r, i; -} doublecomplex; -typedef int logical; -typedef short shortlogical; -typedef char logical1; -typedef char integer1; -typedef long longint; -typedef unsigned long ulongint; - -/* IO stuff */ -typedef int ftnlen; - -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wstrict-prototypes" -#elif defined(__GNUC__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wstrict-prototypes" -#endif - -/* procedure parameter types for -A */ -typedef int (*U_fp)(); -typedef shortint (*J_fp)(); -typedef integer (*I_fp)(); -typedef real (*R_fp)(); -typedef doublereal (*D_fp)(), (*E_fp)(); -typedef void (*C_fp)(); -typedef void (*Z_fp)(); -typedef logical (*L_fp)(); -typedef shortlogical (*K_fp)(); -typedef void (*H_fp)(); -typedef int (*S_fp)(); - -#if defined(__clang__) -#pragma clang diagnostic pop -#elif defined(__GNUC__) -#pragma GCC diagnostic pop -#endif - -/* E_fp is for real functions when -R is not specified */ -typedef void C_f; /* complex function */ -typedef void H_f; /* character function */ -typedef void Z_f; /* double complex function */ -typedef doublereal E_f; /* real function with -R not specified */ -#endif /* HAVE_G2C_H */ - -/* The following may have to be selectively installed according - to platform, at least partly - */ - -#if defined(HAVE_LIBBLAS) && defined(HAVE_LIBLAPACK) -#include -#include -#endif - /* Useful defines */ #define MAX_POS 1 /* Indicates maximum value */ @@ -117,17 +33,7 @@ typedef doublereal E_f; /* real function with -R not specified */ #define MAX_ABS 0 /* Indicates absolute value */ #define DO_COMPACT 0 /* Eliminate unnecessary rows (cols) in matrix */ -#define NO_COMPACT 1 /* ... or not */ - -/* define macros for fortran symbols (called directly). Needed because - of platform invariance on fortran->C symbol translations - */ - -#if defined(HAVE_LIBBLAS) && defined(HAVE_LIBLAPACK) -#define f77_dgesv dgesv_ -#define f77_dgemm dgemm_ -#define f77_dnrm2 dnrm2_ -#endif +#define NO_COMPACT 1 /* ... or not */ /* Operations should know type of coefficient matrix, so that they can call the right driver @@ -146,16 +52,16 @@ typedef enum { RVEC, CVEC } vtype; typedef struct matrix_ { - mat_spec type; /* matrix, row vector or column vector? */ - int v_indx; /* If a vector, which row(column) is active? - * If a matrix this is ignored. If value is < 0, - * the first row(column) is assumed, ie. index 0. */ - int rows, cols; /* Rows and columns of matrix */ - int ldim; /* Lead dimension of matrix. How many `rows' are - * alloc'ed? May exceed real number of rows `rows' */ - doublereal *vals; /* The values (should be dimensioned to lda * cols */ - int is_init; /* Is matrix initialised: values array - * is allocated and parameters set ? */ + mat_spec type; /* matrix, row vector or column vector? */ + int v_indx; /* If a vector, which row(column) is active? + * If a matrix this is ignored. If value is < 0, + * the first row(column) is assumed, ie. index 0. */ + int rows, cols; /* Rows and columns of matrix */ + int ldim; /* Lead dimension of matrix. How many `rows' are + * alloc'ed? May exceed real number of rows `rows' */ + double *vals; /* The values (should be dimensioned to lda * cols */ + int is_init; /* Is matrix initialised: values array + * is allocated and parameters set ? */ } mat_struct; typedef mat_struct vec_struct; diff --git a/include/grass/lapack.h b/include/grass/lapack.h deleted file mode 100644 index 2763404ae8c..00000000000 --- a/include/grass/lapack.h +++ /dev/null @@ -1,4748 +0,0 @@ -#ifndef LAPACK_WRAP_ -#define LAPACK_WRAP_ - -extern int cbdsqr_(char *uplo, integer *n, integer *ncvt, integer *nru, - integer *ncc, real *d__, real *e, complex *vt, integer *ldvt, - complex *u, integer *ldu, complex *c__, integer *ldc, - real *rwork, integer *info, ftnlen uplo_len); -extern int cgbbrd_(char *vect, integer *m, integer *n, integer *ncc, - integer *kl, integer *ku, complex *ab, integer *ldab, - real *d__, real *e, complex *q, integer *ldq, complex *pt, - integer *ldpt, complex *c__, integer *ldc, complex *work, - real *rwork, integer *info, ftnlen vect_len); -extern int cgbcon_(char *norm, integer *n, integer *kl, integer *ku, - complex *ab, integer *ldab, integer *ipiv, real *anorm, - real *rcond, complex *work, real *rwork, integer *info, - ftnlen norm_len); -extern int cgbequ_(integer *m, integer *n, integer *kl, integer *ku, - complex *ab, integer *ldab, real *r__, real *c__, - real *rowcnd, real *colcnd, real *amax, integer *info); -extern int cgbrfs_(char *trans, integer *n, integer *kl, integer *ku, - integer *nrhs, complex *ab, integer *ldab, complex *afb, - integer *ldafb, integer *ipiv, complex *b, integer *ldb, - complex *x, integer *ldx, real *ferr, real *berr, - complex *work, real *rwork, integer *info, ftnlen trans_len); -extern int cgbsv_(integer *n, integer *kl, integer *ku, integer *nrhs, - complex *ab, integer *ldab, integer *ipiv, complex *b, - integer *ldb, integer *info); -extern int cgbsvx_(char *fact, char *trans, integer *n, integer *kl, - integer *ku, integer *nrhs, complex *ab, integer *ldab, - complex *afb, integer *ldafb, integer *ipiv, char *equed, - real *r__, real *c__, complex *b, integer *ldb, complex *x, - integer *ldx, real *rcond, real *ferr, real *berr, - complex *work, real *rwork, integer *info, ftnlen fact_len, - ftnlen trans_len, ftnlen equed_len); -extern int cgbtf2_(integer *m, integer *n, integer *kl, integer *ku, - complex *ab, integer *ldab, integer *ipiv, integer *info); -extern int cgbtrf_(integer *m, integer *n, integer *kl, integer *ku, - complex *ab, integer *ldab, integer *ipiv, integer *info); -extern int cgbtrs_(char *trans, integer *n, integer *kl, integer *ku, - integer *nrhs, complex *ab, integer *ldab, integer *ipiv, - complex *b, integer *ldb, integer *info, ftnlen trans_len); -extern int cgebak_(char *job, char *side, integer *n, integer *ilo, - integer *ihi, real *scale, integer *m, complex *v, - integer *ldv, integer *info, ftnlen job_len, - ftnlen side_len); -extern int cgebal_(char *job, integer *n, complex *a, integer *lda, - integer *ilo, integer *ihi, real *scale, integer *info, - ftnlen job_len); -extern int cgebd2_(integer *m, integer *n, complex *a, integer *lda, real *d__, - real *e, complex *tauq, complex *taup, complex *work, - integer *info); -extern int cgebrd_(integer *m, integer *n, complex *a, integer *lda, real *d__, - real *e, complex *tauq, complex *taup, complex *work, - integer *lwork, integer *info); -extern int cgecon_(char *norm, integer *n, complex *a, integer *lda, - real *anorm, real *rcond, complex *work, real *rwork, - integer *info, ftnlen norm_len); -extern int cgeequ_(integer *m, integer *n, complex *a, integer *lda, real *r__, - real *c__, real *rowcnd, real *colcnd, real *amax, - integer *info); -extern int cgees_(char *jobvs, char *sort, L_fp select, integer *n, complex *a, - integer *lda, integer *sdim, complex *w, complex *vs, - integer *ldvs, complex *work, integer *lwork, real *rwork, - logical *bwork, integer *info, ftnlen jobvs_len, - ftnlen sort_len); -extern int cgeesx_(char *jobvs, char *sort, L_fp select, char *sense, - integer *n, complex *a, integer *lda, integer *sdim, - complex *w, complex *vs, integer *ldvs, real *rconde, - real *rcondv, complex *work, integer *lwork, real *rwork, - logical *bwork, integer *info, ftnlen jobvs_len, - ftnlen sort_len, ftnlen sense_len); -extern int cgeev_(char *jobvl, char *jobvr, integer *n, complex *a, - integer *lda, complex *w, complex *vl, integer *ldvl, - complex *vr, integer *ldvr, complex *work, integer *lwork, - real *rwork, integer *info, ftnlen jobvl_len, - ftnlen jobvr_len); -extern int cgeevx_(char *balanc, char *jobvl, char *jobvr, char *sense, - integer *n, complex *a, integer *lda, complex *w, - complex *vl, integer *ldvl, complex *vr, integer *ldvr, - integer *ilo, integer *ihi, real *scale, real *abnrm, - real *rconde, real *rcondv, complex *work, integer *lwork, - real *rwork, integer *info, ftnlen balanc_len, - ftnlen jobvl_len, ftnlen jobvr_len, ftnlen sense_len); -extern int cgegs_(char *jobvsl, char *jobvsr, integer *n, complex *a, - integer *lda, complex *b, integer *ldb, complex *alpha, - complex *beta, complex *vsl, integer *ldvsl, complex *vsr, - integer *ldvsr, complex *work, integer *lwork, real *rwork, - integer *info, ftnlen jobvsl_len, ftnlen jobvsr_len); -extern int cgegv_(char *jobvl, char *jobvr, integer *n, complex *a, - integer *lda, complex *b, integer *ldb, complex *alpha, - complex *beta, complex *vl, integer *ldvl, complex *vr, - integer *ldvr, complex *work, integer *lwork, real *rwork, - integer *info, ftnlen jobvl_len, ftnlen jobvr_len); -extern int cgehd2_(integer *n, integer *ilo, integer *ihi, complex *a, - integer *lda, complex *tau, complex *work, integer *info); -extern int cgehrd_(integer *n, integer *ilo, integer *ihi, complex *a, - integer *lda, complex *tau, complex *work, integer *lwork, - integer *info); -extern int cgelq2_(integer *m, integer *n, complex *a, integer *lda, - complex *tau, complex *work, integer *info); -extern int cgelqf_(integer *m, integer *n, complex *a, integer *lda, - complex *tau, complex *work, integer *lwork, integer *info); -extern int cgels_(char *trans, integer *m, integer *n, integer *nrhs, - complex *a, integer *lda, complex *b, integer *ldb, - complex *work, integer *lwork, integer *info, - ftnlen trans_len); -extern int cgelsd_(integer *m, integer *n, integer *nrhs, complex *a, - integer *lda, complex *b, integer *ldb, real *s, real *rcond, - integer *rank, complex *work, integer *lwork, real *rwork, - integer *iwork, integer *info); -extern int cgelss_(integer *m, integer *n, integer *nrhs, complex *a, - integer *lda, complex *b, integer *ldb, real *s, real *rcond, - integer *rank, complex *work, integer *lwork, real *rwork, - integer *info); -extern int cgelsx_(integer *m, integer *n, integer *nrhs, complex *a, - integer *lda, complex *b, integer *ldb, integer *jpvt, - real *rcond, integer *rank, complex *work, real *rwork, - integer *info); -extern int cgelsy_(integer *m, integer *n, integer *nrhs, complex *a, - integer *lda, complex *b, integer *ldb, integer *jpvt, - real *rcond, integer *rank, complex *work, integer *lwork, - real *rwork, integer *info); -extern int cgeql2_(integer *m, integer *n, complex *a, integer *lda, - complex *tau, complex *work, integer *info); -extern int cgeqlf_(integer *m, integer *n, complex *a, integer *lda, - complex *tau, complex *work, integer *lwork, integer *info); -extern int cgeqp3_(integer *m, integer *n, complex *a, integer *lda, - integer *jpvt, complex *tau, complex *work, integer *lwork, - real *rwork, integer *info); -extern int cgeqpf_(integer *m, integer *n, complex *a, integer *lda, - integer *jpvt, complex *tau, complex *work, real *rwork, - integer *info); -extern int cgeqr2_(integer *m, integer *n, complex *a, integer *lda, - complex *tau, complex *work, integer *info); -extern int cgeqrf_(integer *m, integer *n, complex *a, integer *lda, - complex *tau, complex *work, integer *lwork, integer *info); -extern int cgerfs_(char *trans, integer *n, integer *nrhs, complex *a, - integer *lda, complex *af, integer *ldaf, integer *ipiv, - complex *b, integer *ldb, complex *x, integer *ldx, - real *ferr, real *berr, complex *work, real *rwork, - integer *info, ftnlen trans_len); -extern int cgerq2_(integer *m, integer *n, complex *a, integer *lda, - complex *tau, complex *work, integer *info); -extern int cgerqf_(integer *m, integer *n, complex *a, integer *lda, - complex *tau, complex *work, integer *lwork, integer *info); -extern int cgesc2_(integer *n, complex *a, integer *lda, complex *rhs, - integer *ipiv, integer *jpiv, real *scale); -extern int cgesdd_(char *jobz, integer *m, integer *n, complex *a, integer *lda, - real *s, complex *u, integer *ldu, complex *vt, - integer *ldvt, complex *work, integer *lwork, real *rwork, - integer *iwork, integer *info, ftnlen jobz_len); -extern int cgesv_(integer *n, integer *nrhs, complex *a, integer *lda, - integer *ipiv, complex *b, integer *ldb, integer *info); -extern int cgesvd_(char *jobu, char *jobvt, integer *m, integer *n, complex *a, - integer *lda, real *s, complex *u, integer *ldu, complex *vt, - integer *ldvt, complex *work, integer *lwork, real *rwork, - integer *info, ftnlen jobu_len, ftnlen jobvt_len); -extern int cgesvx_(char *fact, char *trans, integer *n, integer *nrhs, - complex *a, integer *lda, complex *af, integer *ldaf, - integer *ipiv, char *equed, real *r__, real *c__, complex *b, - integer *ldb, complex *x, integer *ldx, real *rcond, - real *ferr, real *berr, complex *work, real *rwork, - integer *info, ftnlen fact_len, ftnlen trans_len, - ftnlen equed_len); -extern int cgetc2_(integer *n, complex *a, integer *lda, integer *ipiv, - integer *jpiv, integer *info); -extern int cgetf2_(integer *m, integer *n, complex *a, integer *lda, - integer *ipiv, integer *info); -extern int cgetrf_(integer *m, integer *n, complex *a, integer *lda, - integer *ipiv, integer *info); -extern int cgetri_(integer *n, complex *a, integer *lda, integer *ipiv, - complex *work, integer *lwork, integer *info); -extern int cgetrs_(char *trans, integer *n, integer *nrhs, complex *a, - integer *lda, integer *ipiv, complex *b, integer *ldb, - integer *info, ftnlen trans_len); -extern int cggbak_(char *job, char *side, integer *n, integer *ilo, - integer *ihi, real *lscale, real *rscale, integer *m, - complex *v, integer *ldv, integer *info, ftnlen job_len, - ftnlen side_len); -extern int cggbal_(char *job, integer *n, complex *a, integer *lda, complex *b, - integer *ldb, integer *ilo, integer *ihi, real *lscale, - real *rscale, real *work, integer *info, ftnlen job_len); -extern int cgges_(char *jobvsl, char *jobvsr, char *sort, L_fp selctg, - integer *n, complex *a, integer *lda, complex *b, - integer *ldb, integer *sdim, complex *alpha, complex *beta, - complex *vsl, integer *ldvsl, complex *vsr, integer *ldvsr, - complex *work, integer *lwork, real *rwork, logical *bwork, - integer *info, ftnlen jobvsl_len, ftnlen jobvsr_len, - ftnlen sort_len); -extern int cggesx_(char *jobvsl, char *jobvsr, char *sort, L_fp selctg, - char *sense, integer *n, complex *a, integer *lda, - complex *b, integer *ldb, integer *sdim, complex *alpha, - complex *beta, complex *vsl, integer *ldvsl, complex *vsr, - integer *ldvsr, real *rconde, real *rcondv, complex *work, - integer *lwork, real *rwork, integer *iwork, integer *liwork, - logical *bwork, integer *info, ftnlen jobvsl_len, - ftnlen jobvsr_len, ftnlen sort_len, ftnlen sense_len); -extern int cggev_(char *jobvl, char *jobvr, integer *n, complex *a, - integer *lda, complex *b, integer *ldb, complex *alpha, - complex *beta, complex *vl, integer *ldvl, complex *vr, - integer *ldvr, complex *work, integer *lwork, real *rwork, - integer *info, ftnlen jobvl_len, ftnlen jobvr_len); -extern int cggevx_(char *balanc, char *jobvl, char *jobvr, char *sense, - integer *n, complex *a, integer *lda, complex *b, - integer *ldb, complex *alpha, complex *beta, complex *vl, - integer *ldvl, complex *vr, integer *ldvr, integer *ilo, - integer *ihi, real *lscale, real *rscale, real *abnrm, - real *bbnrm, real *rconde, real *rcondv, complex *work, - integer *lwork, real *rwork, integer *iwork, logical *bwork, - integer *info, ftnlen balanc_len, ftnlen jobvl_len, - ftnlen jobvr_len, ftnlen sense_len); -extern int cggglm_(integer *n, integer *m, integer *p, complex *a, integer *lda, - complex *b, integer *ldb, complex *d__, complex *x, - complex *y, complex *work, integer *lwork, integer *info); -extern int cgghrd_(char *compq, char *compz, integer *n, integer *ilo, - integer *ihi, complex *a, integer *lda, complex *b, - integer *ldb, complex *q, integer *ldq, complex *z__, - integer *ldz, integer *info, ftnlen compq_len, - ftnlen compz_len); -extern int cgglse_(integer *m, integer *n, integer *p, complex *a, integer *lda, - complex *b, integer *ldb, complex *c__, complex *d__, - complex *x, complex *work, integer *lwork, integer *info); -extern int cggqrf_(integer *n, integer *m, integer *p, complex *a, integer *lda, - complex *taua, complex *b, integer *ldb, complex *taub, - complex *work, integer *lwork, integer *info); -extern int cggrqf_(integer *m, integer *p, integer *n, complex *a, integer *lda, - complex *taua, complex *b, integer *ldb, complex *taub, - complex *work, integer *lwork, integer *info); -extern int cggsvd_(char *jobu, char *jobv, char *jobq, integer *m, integer *n, - integer *p, integer *k, integer *l, complex *a, integer *lda, - complex *b, integer *ldb, real *alpha, real *beta, - complex *u, integer *ldu, complex *v, integer *ldv, - complex *q, integer *ldq, complex *work, real *rwork, - integer *iwork, integer *info, ftnlen jobu_len, - ftnlen jobv_len, ftnlen jobq_len); -extern int cggsvp_(char *jobu, char *jobv, char *jobq, integer *m, integer *p, - integer *n, complex *a, integer *lda, complex *b, - integer *ldb, real *tola, real *tolb, integer *k, integer *l, - complex *u, integer *ldu, complex *v, integer *ldv, - complex *q, integer *ldq, integer *iwork, real *rwork, - complex *tau, complex *work, integer *info, ftnlen jobu_len, - ftnlen jobv_len, ftnlen jobq_len); -extern int cgtcon_(char *norm, integer *n, complex *dl, complex *d__, - complex *du, complex *du2, integer *ipiv, real *anorm, - real *rcond, complex *work, integer *info, ftnlen norm_len); -extern int cgtrfs_(char *trans, integer *n, integer *nrhs, complex *dl, - complex *d__, complex *du, complex *dlf, complex *df, - complex *duf, complex *du2, integer *ipiv, complex *b, - integer *ldb, complex *x, integer *ldx, real *ferr, - real *berr, complex *work, real *rwork, integer *info, - ftnlen trans_len); -extern int cgtsv_(integer *n, integer *nrhs, complex *dl, complex *d__, - complex *du, complex *b, integer *ldb, integer *info); -extern int cgtsvx_(char *fact, char *trans, integer *n, integer *nrhs, - complex *dl, complex *d__, complex *du, complex *dlf, - complex *df, complex *duf, complex *du2, integer *ipiv, - complex *b, integer *ldb, complex *x, integer *ldx, - real *rcond, real *ferr, real *berr, complex *work, - real *rwork, integer *info, ftnlen fact_len, - ftnlen trans_len); -extern int cgttrf_(integer *n, complex *dl, complex *d__, complex *du, - complex *du2, integer *ipiv, integer *info); -extern int cgttrs_(char *trans, integer *n, integer *nrhs, complex *dl, - complex *d__, complex *du, complex *du2, integer *ipiv, - complex *b, integer *ldb, integer *info, ftnlen trans_len); -extern int cgtts2_(integer *itrans, integer *n, integer *nrhs, complex *dl, - complex *d__, complex *du, complex *du2, integer *ipiv, - complex *b, integer *ldb); -extern int chbev_(char *jobz, char *uplo, integer *n, integer *kd, complex *ab, - integer *ldab, real *w, complex *z__, integer *ldz, - complex *work, real *rwork, integer *info, ftnlen jobz_len, - ftnlen uplo_len); -extern int chbevd_(char *jobz, char *uplo, integer *n, integer *kd, complex *ab, - integer *ldab, real *w, complex *z__, integer *ldz, - complex *work, integer *lwork, real *rwork, integer *lrwork, - integer *iwork, integer *liwork, integer *info, - ftnlen jobz_len, ftnlen uplo_len); -extern int chbevx_(char *jobz, char *range, char *uplo, integer *n, integer *kd, - complex *ab, integer *ldab, complex *q, integer *ldq, - real *vl, real *vu, integer *il, integer *iu, real *abstol, - integer *m, real *w, complex *z__, integer *ldz, - complex *work, real *rwork, integer *iwork, integer *ifail, - integer *info, ftnlen jobz_len, ftnlen range_len, - ftnlen uplo_len); -extern int chbgst_(char *vect, char *uplo, integer *n, integer *ka, integer *kb, - complex *ab, integer *ldab, complex *bb, integer *ldbb, - complex *x, integer *ldx, complex *work, real *rwork, - integer *info, ftnlen vect_len, ftnlen uplo_len); -extern int chbgv_(char *jobz, char *uplo, integer *n, integer *ka, integer *kb, - complex *ab, integer *ldab, complex *bb, integer *ldbb, - real *w, complex *z__, integer *ldz, complex *work, - real *rwork, integer *info, ftnlen jobz_len, ftnlen uplo_len); -extern int chbgvd_(char *jobz, char *uplo, integer *n, integer *ka, integer *kb, - complex *ab, integer *ldab, complex *bb, integer *ldbb, - real *w, complex *z__, integer *ldz, complex *work, - integer *lwork, real *rwork, integer *lrwork, integer *iwork, - integer *liwork, integer *info, ftnlen jobz_len, - ftnlen uplo_len); -extern int chbgvx_(char *jobz, char *range, char *uplo, integer *n, integer *ka, - integer *kb, complex *ab, integer *ldab, complex *bb, - integer *ldbb, complex *q, integer *ldq, real *vl, real *vu, - integer *il, integer *iu, real *abstol, integer *m, real *w, - complex *z__, integer *ldz, complex *work, real *rwork, - integer *iwork, integer *ifail, integer *info, - ftnlen jobz_len, ftnlen range_len, ftnlen uplo_len); -extern int chbtrd_(char *vect, char *uplo, integer *n, integer *kd, complex *ab, - integer *ldab, real *d__, real *e, complex *q, integer *ldq, - complex *work, integer *info, ftnlen vect_len, - ftnlen uplo_len); -extern int checon_(char *uplo, integer *n, complex *a, integer *lda, - integer *ipiv, real *anorm, real *rcond, complex *work, - integer *info, ftnlen uplo_len); -extern int cheev_(char *jobz, char *uplo, integer *n, complex *a, integer *lda, - real *w, complex *work, integer *lwork, real *rwork, - integer *info, ftnlen jobz_len, ftnlen uplo_len); -extern int cheevd_(char *jobz, char *uplo, integer *n, complex *a, integer *lda, - real *w, complex *work, integer *lwork, real *rwork, - integer *lrwork, integer *iwork, integer *liwork, - integer *info, ftnlen jobz_len, ftnlen uplo_len); -extern int cheevr_(char *jobz, char *range, char *uplo, integer *n, complex *a, - integer *lda, real *vl, real *vu, integer *il, integer *iu, - real *abstol, integer *m, real *w, complex *z__, - integer *ldz, integer *isuppz, complex *work, integer *lwork, - real *rwork, integer *lrwork, integer *iwork, - integer *liwork, integer *info, ftnlen jobz_len, - ftnlen range_len, ftnlen uplo_len); -extern int cheevx_(char *jobz, char *range, char *uplo, integer *n, complex *a, - integer *lda, real *vl, real *vu, integer *il, integer *iu, - real *abstol, integer *m, real *w, complex *z__, - integer *ldz, complex *work, integer *lwork, real *rwork, - integer *iwork, integer *ifail, integer *info, - ftnlen jobz_len, ftnlen range_len, ftnlen uplo_len); -extern int chegs2_(integer *itype, char *uplo, integer *n, complex *a, - integer *lda, complex *b, integer *ldb, integer *info, - ftnlen uplo_len); -extern int chegst_(integer *itype, char *uplo, integer *n, complex *a, - integer *lda, complex *b, integer *ldb, integer *info, - ftnlen uplo_len); -extern int chegv_(integer *itype, char *jobz, char *uplo, integer *n, - complex *a, integer *lda, complex *b, integer *ldb, real *w, - complex *work, integer *lwork, real *rwork, integer *info, - ftnlen jobz_len, ftnlen uplo_len); -extern int chegvd_(integer *itype, char *jobz, char *uplo, integer *n, - complex *a, integer *lda, complex *b, integer *ldb, real *w, - complex *work, integer *lwork, real *rwork, integer *lrwork, - integer *iwork, integer *liwork, integer *info, - ftnlen jobz_len, ftnlen uplo_len); -extern int chegvx_(integer *itype, char *jobz, char *range, char *uplo, - integer *n, complex *a, integer *lda, complex *b, - integer *ldb, real *vl, real *vu, integer *il, integer *iu, - real *abstol, integer *m, real *w, complex *z__, - integer *ldz, complex *work, integer *lwork, real *rwork, - integer *iwork, integer *ifail, integer *info, - ftnlen jobz_len, ftnlen range_len, ftnlen uplo_len); -extern int cherfs_(char *uplo, integer *n, integer *nrhs, complex *a, - integer *lda, complex *af, integer *ldaf, integer *ipiv, - complex *b, integer *ldb, complex *x, integer *ldx, - real *ferr, real *berr, complex *work, real *rwork, - integer *info, ftnlen uplo_len); -extern int chesv_(char *uplo, integer *n, integer *nrhs, complex *a, - integer *lda, integer *ipiv, complex *b, integer *ldb, - complex *work, integer *lwork, integer *info, - ftnlen uplo_len); -extern int chesvx_(char *fact, char *uplo, integer *n, integer *nrhs, - complex *a, integer *lda, complex *af, integer *ldaf, - integer *ipiv, complex *b, integer *ldb, complex *x, - integer *ldx, real *rcond, real *ferr, real *berr, - complex *work, integer *lwork, real *rwork, integer *info, - ftnlen fact_len, ftnlen uplo_len); -extern int chetd2_(char *uplo, integer *n, complex *a, integer *lda, real *d__, - real *e, complex *tau, integer *info, ftnlen uplo_len); -extern int chetf2_(char *uplo, integer *n, complex *a, integer *lda, - integer *ipiv, integer *info, ftnlen uplo_len); -extern int chetrd_(char *uplo, integer *n, complex *a, integer *lda, real *d__, - real *e, complex *tau, complex *work, integer *lwork, - integer *info, ftnlen uplo_len); -extern int chetrf_(char *uplo, integer *n, complex *a, integer *lda, - integer *ipiv, complex *work, integer *lwork, integer *info, - ftnlen uplo_len); -extern int chetri_(char *uplo, integer *n, complex *a, integer *lda, - integer *ipiv, complex *work, integer *info, - ftnlen uplo_len); -extern int chetrs_(char *uplo, integer *n, integer *nrhs, complex *a, - integer *lda, integer *ipiv, complex *b, integer *ldb, - integer *info, ftnlen uplo_len); -extern int chgeqz_(char *job, char *compq, char *compz, integer *n, - integer *ilo, integer *ihi, complex *a, integer *lda, - complex *b, integer *ldb, complex *alpha, complex *beta, - complex *q, integer *ldq, complex *z__, integer *ldz, - complex *work, integer *lwork, real *rwork, integer *info, - ftnlen job_len, ftnlen compq_len, ftnlen compz_len); -extern int chpcon_(char *uplo, integer *n, complex *ap, integer *ipiv, - real *anorm, real *rcond, complex *work, integer *info, - ftnlen uplo_len); -extern int chpev_(char *jobz, char *uplo, integer *n, complex *ap, real *w, - complex *z__, integer *ldz, complex *work, real *rwork, - integer *info, ftnlen jobz_len, ftnlen uplo_len); -extern int chpevd_(char *jobz, char *uplo, integer *n, complex *ap, real *w, - complex *z__, integer *ldz, complex *work, integer *lwork, - real *rwork, integer *lrwork, integer *iwork, - integer *liwork, integer *info, ftnlen jobz_len, - ftnlen uplo_len); -extern int chpevx_(char *jobz, char *range, char *uplo, integer *n, complex *ap, - real *vl, real *vu, integer *il, integer *iu, real *abstol, - integer *m, real *w, complex *z__, integer *ldz, - complex *work, real *rwork, integer *iwork, integer *ifail, - integer *info, ftnlen jobz_len, ftnlen range_len, - ftnlen uplo_len); -extern int chpgst_(integer *itype, char *uplo, integer *n, complex *ap, - complex *bp, integer *info, ftnlen uplo_len); -extern int chpgv_(integer *itype, char *jobz, char *uplo, integer *n, - complex *ap, complex *bp, real *w, complex *z__, integer *ldz, - complex *work, real *rwork, integer *info, ftnlen jobz_len, - ftnlen uplo_len); -extern int chpgvd_(integer *itype, char *jobz, char *uplo, integer *n, - complex *ap, complex *bp, real *w, complex *z__, - integer *ldz, complex *work, integer *lwork, real *rwork, - integer *lrwork, integer *iwork, integer *liwork, - integer *info, ftnlen jobz_len, ftnlen uplo_len); -extern int chpgvx_(integer *itype, char *jobz, char *range, char *uplo, - integer *n, complex *ap, complex *bp, real *vl, real *vu, - integer *il, integer *iu, real *abstol, integer *m, real *w, - complex *z__, integer *ldz, complex *work, real *rwork, - integer *iwork, integer *ifail, integer *info, - ftnlen jobz_len, ftnlen range_len, ftnlen uplo_len); -extern int chprfs_(char *uplo, integer *n, integer *nrhs, complex *ap, - complex *afp, integer *ipiv, complex *b, integer *ldb, - complex *x, integer *ldx, real *ferr, real *berr, - complex *work, real *rwork, integer *info, ftnlen uplo_len); -extern int chpsv_(char *uplo, integer *n, integer *nrhs, complex *ap, - integer *ipiv, complex *b, integer *ldb, integer *info, - ftnlen uplo_len); -extern int chpsvx_(char *fact, char *uplo, integer *n, integer *nrhs, - complex *ap, complex *afp, integer *ipiv, complex *b, - integer *ldb, complex *x, integer *ldx, real *rcond, - real *ferr, real *berr, complex *work, real *rwork, - integer *info, ftnlen fact_len, ftnlen uplo_len); -extern int chptrd_(char *uplo, integer *n, complex *ap, real *d__, real *e, - complex *tau, integer *info, ftnlen uplo_len); -extern int chptrf_(char *uplo, integer *n, complex *ap, integer *ipiv, - integer *info, ftnlen uplo_len); -extern int chptri_(char *uplo, integer *n, complex *ap, integer *ipiv, - complex *work, integer *info, ftnlen uplo_len); -extern int chptrs_(char *uplo, integer *n, integer *nrhs, complex *ap, - integer *ipiv, complex *b, integer *ldb, integer *info, - ftnlen uplo_len); -extern int chsein_(char *side, char *eigsrc, char *initv, logical *select, - integer *n, complex *h__, integer *ldh, complex *w, - complex *vl, integer *ldvl, complex *vr, integer *ldvr, - integer *mm, integer *m, complex *work, real *rwork, - integer *ifaill, integer *ifailr, integer *info, - ftnlen side_len, ftnlen eigsrc_len, ftnlen initv_len); -extern int chseqr_(char *job, char *compz, integer *n, integer *ilo, - integer *ihi, complex *h__, integer *ldh, complex *w, - complex *z__, integer *ldz, complex *work, integer *lwork, - integer *info, ftnlen job_len, ftnlen compz_len); -extern int clabrd_(integer *m, integer *n, integer *nb, complex *a, - integer *lda, real *d__, real *e, complex *tauq, - complex *taup, complex *x, integer *ldx, complex *y, - integer *ldy); -extern int clacgv_(integer *n, complex *x, integer *incx); -extern int clacon_(integer *n, complex *v, complex *x, real *est, - integer *kase); -extern int clacp2_(char *uplo, integer *m, integer *n, real *a, integer *lda, - complex *b, integer *ldb, ftnlen uplo_len); -extern int clacpy_(char *uplo, integer *m, integer *n, complex *a, integer *lda, - complex *b, integer *ldb, ftnlen uplo_len); -extern int clacrm_(integer *m, integer *n, complex *a, integer *lda, real *b, - integer *ldb, complex *c__, integer *ldc, real *rwork); -extern int clacrt_(integer *n, complex *cx, integer *incx, complex *cy, - integer *incy, complex *c__, complex *s); -extern C_f cladiv_(complex *ret_val, complex *x, complex *y); -extern int claed0_(integer *qsiz, integer *n, real *d__, real *e, complex *q, - integer *ldq, complex *qstore, integer *ldqs, real *rwork, - integer *iwork, integer *info); -extern int claed7_(integer *n, integer *cutpnt, integer *qsiz, integer *tlvls, - integer *curlvl, integer *curpbm, real *d__, complex *q, - integer *ldq, real *rho, integer *indxq, real *qstore, - integer *qptr, integer *prmptr, integer *perm, - integer *givptr, integer *givcol, real *givnum, - complex *work, real *rwork, integer *iwork, integer *info); -extern int claed8_(integer *k, integer *n, integer *qsiz, complex *q, - integer *ldq, real *d__, real *rho, integer *cutpnt, - real *z__, real *dlamda, complex *q2, integer *ldq2, real *w, - integer *indxp, integer *indx, integer *indxq, integer *perm, - integer *givptr, integer *givcol, real *givnum, - integer *info); -extern int claein_(logical *rightv, logical *noinit, integer *n, complex *h__, - integer *ldh, complex *w, complex *v, complex *b, - integer *ldb, real *rwork, real *eps3, real *smlnum, - integer *info); -extern int claesy_(complex *a, complex *b, complex *c__, complex *rt1, - complex *rt2, complex *evscal, complex *cs1, complex *sn1); -extern int claev2_(complex *a, complex *b, complex *c__, real *rt1, real *rt2, - real *cs1, complex *sn1); -extern int clags2_(logical *upper, real *a1, complex *a2, real *a3, real *b1, - complex *b2, real *b3, real *csu, complex *snu, real *csv, - complex *snv, real *csq, complex *snq); -extern int clagtm_(char *trans, integer *n, integer *nrhs, real *alpha, - complex *dl, complex *d__, complex *du, complex *x, - integer *ldx, real *beta, complex *b, integer *ldb, - ftnlen trans_len); -extern int clahef_(char *uplo, integer *n, integer *nb, integer *kb, complex *a, - integer *lda, integer *ipiv, complex *w, integer *ldw, - integer *info, ftnlen uplo_len); -extern int clahqr_(logical *wantt, logical *wantz, integer *n, integer *ilo, - integer *ihi, complex *h__, integer *ldh, complex *w, - integer *iloz, integer *ihiz, complex *z__, integer *ldz, - integer *info); -extern int clahrd_(integer *n, integer *k, integer *nb, complex *a, - integer *lda, complex *tau, complex *t, integer *ldt, - complex *y, integer *ldy); -extern int claic1_(integer *job, integer *j, complex *x, real *sest, complex *w, - complex *gamma, real *sestpr, complex *s, complex *c__); -extern int clals0_(integer *icompq, integer *nl, integer *nr, integer *sqre, - integer *nrhs, complex *b, integer *ldb, complex *bx, - integer *ldbx, integer *perm, integer *givptr, - integer *givcol, integer *ldgcol, real *givnum, - integer *ldgnum, real *poles, real *difl, real *difr, - real *z__, integer *k, real *c__, real *s, real *rwork, - integer *info); -extern int clalsa_(integer *icompq, integer *smlsiz, integer *n, integer *nrhs, - complex *b, integer *ldb, complex *bx, integer *ldbx, - real *u, integer *ldu, real *vt, integer *k, real *difl, - real *difr, real *z__, real *poles, integer *givptr, - integer *givcol, integer *ldgcol, integer *perm, - real *givnum, real *c__, real *s, real *rwork, - integer *iwork, integer *info); -extern int clalsd_(char *uplo, integer *smlsiz, integer *n, integer *nrhs, - real *d__, real *e, complex *b, integer *ldb, real *rcond, - integer *rank, complex *work, real *rwork, integer *iwork, - integer *info, ftnlen uplo_len); -extern E_f clangb_(char *norm, integer *n, integer *kl, integer *ku, - complex *ab, integer *ldab, real *work, ftnlen norm_len); -extern E_f clange_(char *norm, integer *m, integer *n, complex *a, integer *lda, - real *work, ftnlen norm_len); -extern E_f clangt_(char *norm, integer *n, complex *dl, complex *d__, - complex *du, ftnlen norm_len); -extern E_f clanhb_(char *norm, char *uplo, integer *n, integer *k, complex *ab, - integer *ldab, real *work, ftnlen norm_len, ftnlen uplo_len); -extern E_f clanhe_(char *norm, char *uplo, integer *n, complex *a, integer *lda, - real *work, ftnlen norm_len, ftnlen uplo_len); -extern E_f clanhp_(char *norm, char *uplo, integer *n, complex *ap, real *work, - ftnlen norm_len, ftnlen uplo_len); -extern E_f clanhs_(char *norm, integer *n, complex *a, integer *lda, real *work, - ftnlen norm_len); -extern E_f clanht_(char *norm, integer *n, real *d__, complex *e, - ftnlen norm_len); -extern E_f clansb_(char *norm, char *uplo, integer *n, integer *k, complex *ab, - integer *ldab, real *work, ftnlen norm_len, ftnlen uplo_len); -extern E_f clansp_(char *norm, char *uplo, integer *n, complex *ap, real *work, - ftnlen norm_len, ftnlen uplo_len); -extern E_f clansy_(char *norm, char *uplo, integer *n, complex *a, integer *lda, - real *work, ftnlen norm_len, ftnlen uplo_len); -extern E_f clantb_(char *norm, char *uplo, char *diag, integer *n, integer *k, - complex *ab, integer *ldab, real *work, ftnlen norm_len, - ftnlen uplo_len, ftnlen diag_len); -extern E_f clantp_(char *norm, char *uplo, char *diag, integer *n, complex *ap, - real *work, ftnlen norm_len, ftnlen uplo_len, - ftnlen diag_len); -extern E_f clantr_(char *norm, char *uplo, char *diag, integer *m, integer *n, - complex *a, integer *lda, real *work, ftnlen norm_len, - ftnlen uplo_len, ftnlen diag_len); -extern int clapll_(integer *n, complex *x, integer *incx, complex *y, - integer *incy, real *ssmin); -extern int clapmt_(logical *forwrd, integer *m, integer *n, complex *x, - integer *ldx, integer *k); -extern int claqgb_(integer *m, integer *n, integer *kl, integer *ku, - complex *ab, integer *ldab, real *r__, real *c__, - real *rowcnd, real *colcnd, real *amax, char *equed, - ftnlen equed_len); -extern int claqge_(integer *m, integer *n, complex *a, integer *lda, real *r__, - real *c__, real *rowcnd, real *colcnd, real *amax, - char *equed, ftnlen equed_len); -extern int claqhb_(char *uplo, integer *n, integer *kd, complex *ab, - integer *ldab, real *s, real *scond, real *amax, char *equed, - ftnlen uplo_len, ftnlen equed_len); -extern int claqhe_(char *uplo, integer *n, complex *a, integer *lda, real *s, - real *scond, real *amax, char *equed, ftnlen uplo_len, - ftnlen equed_len); -extern int claqhp_(char *uplo, integer *n, complex *ap, real *s, real *scond, - real *amax, char *equed, ftnlen uplo_len, ftnlen equed_len); -extern int claqp2_(integer *m, integer *n, integer *offset, complex *a, - integer *lda, integer *jpvt, complex *tau, real *vn1, - real *vn2, complex *work); -extern int claqps_(integer *m, integer *n, integer *offset, integer *nb, - integer *kb, complex *a, integer *lda, integer *jpvt, - complex *tau, real *vn1, real *vn2, complex *auxv, - complex *f, integer *ldf); -extern int claqsb_(char *uplo, integer *n, integer *kd, complex *ab, - integer *ldab, real *s, real *scond, real *amax, char *equed, - ftnlen uplo_len, ftnlen equed_len); -extern int claqsp_(char *uplo, integer *n, complex *ap, real *s, real *scond, - real *amax, char *equed, ftnlen uplo_len, ftnlen equed_len); -extern int claqsy_(char *uplo, integer *n, complex *a, integer *lda, real *s, - real *scond, real *amax, char *equed, ftnlen uplo_len, - ftnlen equed_len); -extern int clar1v_(integer *n, integer *b1, integer *bn, real *sigma, real *d__, - real *l, real *ld, real *lld, real *gersch, complex *z__, - real *ztz, real *mingma, integer *r__, integer *isuppz, - real *work); -extern int clar2v_(integer *n, complex *x, complex *y, complex *z__, - integer *incx, real *c__, complex *s, integer *incc); -extern int clarcm_(integer *m, integer *n, real *a, integer *lda, complex *b, - integer *ldb, complex *c__, integer *ldc, real *rwork); -extern int clarf_(char *side, integer *m, integer *n, complex *v, integer *incv, - complex *tau, complex *c__, integer *ldc, complex *work, - ftnlen side_len); -extern int clarfb_(char *side, char *trans, char *direct, char *storev, - integer *m, integer *n, integer *k, complex *v, integer *ldv, - complex *t, integer *ldt, complex *c__, integer *ldc, - complex *work, integer *ldwork, ftnlen side_len, - ftnlen trans_len, ftnlen direct_len, ftnlen storev_len); -extern int clarfg_(integer *n, complex *alpha, complex *x, integer *incx, - complex *tau); -extern int clarft_(char *direct, char *storev, integer *n, integer *k, - complex *v, integer *ldv, complex *tau, complex *t, - integer *ldt, ftnlen direct_len, ftnlen storev_len); -extern int clarfx_(char *side, integer *m, integer *n, complex *v, complex *tau, - complex *c__, integer *ldc, complex *work, ftnlen side_len); -extern int clargv_(integer *n, complex *x, integer *incx, complex *y, - integer *incy, real *c__, integer *incc); -extern int clarnv_(integer *idist, integer *iseed, integer *n, complex *x); -extern int clarrv_(integer *n, real *d__, real *l, integer *isplit, integer *m, - real *w, integer *iblock, real *gersch, real *tol, - complex *z__, integer *ldz, integer *isuppz, real *work, - integer *iwork, integer *info); -extern int clartg_(complex *f, complex *g, real *cs, complex *sn, complex *r__); -extern int clartv_(integer *n, complex *x, integer *incx, complex *y, - integer *incy, real *c__, complex *s, integer *incc); -extern int clarz_(char *side, integer *m, integer *n, integer *l, complex *v, - integer *incv, complex *tau, complex *c__, integer *ldc, - complex *work, ftnlen side_len); -extern int clarzb_(char *side, char *trans, char *direct, char *storev, - integer *m, integer *n, integer *k, integer *l, complex *v, - integer *ldv, complex *t, integer *ldt, complex *c__, - integer *ldc, complex *work, integer *ldwork, - ftnlen side_len, ftnlen trans_len, ftnlen direct_len, - ftnlen storev_len); -extern int clarzt_(char *direct, char *storev, integer *n, integer *k, - complex *v, integer *ldv, complex *tau, complex *t, - integer *ldt, ftnlen direct_len, ftnlen storev_len); -extern int clascl_(char *type__, integer *kl, integer *ku, real *cfrom, - real *cto, integer *m, integer *n, complex *a, integer *lda, - integer *info, ftnlen type_len); -extern int claset_(char *uplo, integer *m, integer *n, complex *alpha, - complex *beta, complex *a, integer *lda, ftnlen uplo_len); -extern int clasr_(char *side, char *pivot, char *direct, integer *m, integer *n, - real *c__, real *s, complex *a, integer *lda, ftnlen side_len, - ftnlen pivot_len, ftnlen direct_len); -extern int classq_(integer *n, complex *x, integer *incx, real *scale, - real *sumsq); -extern int claswp_(integer *n, complex *a, integer *lda, integer *k1, - integer *k2, integer *ipiv, integer *incx); -extern int clasyf_(char *uplo, integer *n, integer *nb, integer *kb, complex *a, - integer *lda, integer *ipiv, complex *w, integer *ldw, - integer *info, ftnlen uplo_len); -extern int clatbs_(char *uplo, char *trans, char *diag, char *normin, - integer *n, integer *kd, complex *ab, integer *ldab, - complex *x, real *scale, real *cnorm, integer *info, - ftnlen uplo_len, ftnlen trans_len, ftnlen diag_len, - ftnlen normin_len); -extern int clatdf_(integer *ijob, integer *n, complex *z__, integer *ldz, - complex *rhs, real *rdsum, real *rdscal, integer *ipiv, - integer *jpiv); -extern int clatps_(char *uplo, char *trans, char *diag, char *normin, - integer *n, complex *ap, complex *x, real *scale, - real *cnorm, integer *info, ftnlen uplo_len, - ftnlen trans_len, ftnlen diag_len, ftnlen normin_len); -extern int clatrd_(char *uplo, integer *n, integer *nb, complex *a, - integer *lda, real *e, complex *tau, complex *w, - integer *ldw, ftnlen uplo_len); -extern int clatrs_(char *uplo, char *trans, char *diag, char *normin, - integer *n, complex *a, integer *lda, complex *x, - real *scale, real *cnorm, integer *info, ftnlen uplo_len, - ftnlen trans_len, ftnlen diag_len, ftnlen normin_len); -extern int clatrz_(integer *m, integer *n, integer *l, complex *a, integer *lda, - complex *tau, complex *work); -extern int clatzm_(char *side, integer *m, integer *n, complex *v, - integer *incv, complex *tau, complex *c1, complex *c2, - integer *ldc, complex *work, ftnlen side_len); -extern int clauu2_(char *uplo, integer *n, complex *a, integer *lda, - integer *info, ftnlen uplo_len); -extern int clauum_(char *uplo, integer *n, complex *a, integer *lda, - integer *info, ftnlen uplo_len); -extern int cpbcon_(char *uplo, integer *n, integer *kd, complex *ab, - integer *ldab, real *anorm, real *rcond, complex *work, - real *rwork, integer *info, ftnlen uplo_len); -extern int cpbequ_(char *uplo, integer *n, integer *kd, complex *ab, - integer *ldab, real *s, real *scond, real *amax, - integer *info, ftnlen uplo_len); -extern int cpbrfs_(char *uplo, integer *n, integer *kd, integer *nrhs, - complex *ab, integer *ldab, complex *afb, integer *ldafb, - complex *b, integer *ldb, complex *x, integer *ldx, - real *ferr, real *berr, complex *work, real *rwork, - integer *info, ftnlen uplo_len); -extern int cpbstf_(char *uplo, integer *n, integer *kd, complex *ab, - integer *ldab, integer *info, ftnlen uplo_len); -extern int cpbsv_(char *uplo, integer *n, integer *kd, integer *nrhs, - complex *ab, integer *ldab, complex *b, integer *ldb, - integer *info, ftnlen uplo_len); -extern int cpbsvx_(char *fact, char *uplo, integer *n, integer *kd, - integer *nrhs, complex *ab, integer *ldab, complex *afb, - integer *ldafb, char *equed, real *s, complex *b, - integer *ldb, complex *x, integer *ldx, real *rcond, - real *ferr, real *berr, complex *work, real *rwork, - integer *info, ftnlen fact_len, ftnlen uplo_len, - ftnlen equed_len); -extern int cpbtf2_(char *uplo, integer *n, integer *kd, complex *ab, - integer *ldab, integer *info, ftnlen uplo_len); -extern int cpbtrf_(char *uplo, integer *n, integer *kd, complex *ab, - integer *ldab, integer *info, ftnlen uplo_len); -extern int cpbtrs_(char *uplo, integer *n, integer *kd, integer *nrhs, - complex *ab, integer *ldab, complex *b, integer *ldb, - integer *info, ftnlen uplo_len); -extern int cpocon_(char *uplo, integer *n, complex *a, integer *lda, - real *anorm, real *rcond, complex *work, real *rwork, - integer *info, ftnlen uplo_len); -extern int cpoequ_(integer *n, complex *a, integer *lda, real *s, real *scond, - real *amax, integer *info); -extern int cporfs_(char *uplo, integer *n, integer *nrhs, complex *a, - integer *lda, complex *af, integer *ldaf, complex *b, - integer *ldb, complex *x, integer *ldx, real *ferr, - real *berr, complex *work, real *rwork, integer *info, - ftnlen uplo_len); -extern int cposv_(char *uplo, integer *n, integer *nrhs, complex *a, - integer *lda, complex *b, integer *ldb, integer *info, - ftnlen uplo_len); -extern int cposvx_(char *fact, char *uplo, integer *n, integer *nrhs, - complex *a, integer *lda, complex *af, integer *ldaf, - char *equed, real *s, complex *b, integer *ldb, complex *x, - integer *ldx, real *rcond, real *ferr, real *berr, - complex *work, real *rwork, integer *info, ftnlen fact_len, - ftnlen uplo_len, ftnlen equed_len); -extern int cpotf2_(char *uplo, integer *n, complex *a, integer *lda, - integer *info, ftnlen uplo_len); -extern int cpotrf_(char *uplo, integer *n, complex *a, integer *lda, - integer *info, ftnlen uplo_len); -extern int cpotri_(char *uplo, integer *n, complex *a, integer *lda, - integer *info, ftnlen uplo_len); -extern int cpotrs_(char *uplo, integer *n, integer *nrhs, complex *a, - integer *lda, complex *b, integer *ldb, integer *info, - ftnlen uplo_len); -extern int cppcon_(char *uplo, integer *n, complex *ap, real *anorm, - real *rcond, complex *work, real *rwork, integer *info, - ftnlen uplo_len); -extern int cppequ_(char *uplo, integer *n, complex *ap, real *s, real *scond, - real *amax, integer *info, ftnlen uplo_len); -extern int cpprfs_(char *uplo, integer *n, integer *nrhs, complex *ap, - complex *afp, complex *b, integer *ldb, complex *x, - integer *ldx, real *ferr, real *berr, complex *work, - real *rwork, integer *info, ftnlen uplo_len); -extern int cppsv_(char *uplo, integer *n, integer *nrhs, complex *ap, - complex *b, integer *ldb, integer *info, ftnlen uplo_len); -extern int cppsvx_(char *fact, char *uplo, integer *n, integer *nrhs, - complex *ap, complex *afp, char *equed, real *s, complex *b, - integer *ldb, complex *x, integer *ldx, real *rcond, - real *ferr, real *berr, complex *work, real *rwork, - integer *info, ftnlen fact_len, ftnlen uplo_len, - ftnlen equed_len); -extern int cpptrf_(char *uplo, integer *n, complex *ap, integer *info, - ftnlen uplo_len); -extern int cpptri_(char *uplo, integer *n, complex *ap, integer *info, - ftnlen uplo_len); -extern int cpptrs_(char *uplo, integer *n, integer *nrhs, complex *ap, - complex *b, integer *ldb, integer *info, ftnlen uplo_len); -extern int cptcon_(integer *n, real *d__, complex *e, real *anorm, real *rcond, - real *rwork, integer *info); -extern int cpteqr_(char *compz, integer *n, real *d__, real *e, complex *z__, - integer *ldz, real *work, integer *info, ftnlen compz_len); -extern int cptrfs_(char *uplo, integer *n, integer *nrhs, real *d__, complex *e, - real *df, complex *ef, complex *b, integer *ldb, complex *x, - integer *ldx, real *ferr, real *berr, complex *work, - real *rwork, integer *info, ftnlen uplo_len); -extern int cptsv_(integer *n, integer *nrhs, real *d__, complex *e, complex *b, - integer *ldb, integer *info); -extern int cptsvx_(char *fact, integer *n, integer *nrhs, real *d__, complex *e, - real *df, complex *ef, complex *b, integer *ldb, complex *x, - integer *ldx, real *rcond, real *ferr, real *berr, - complex *work, real *rwork, integer *info, ftnlen fact_len); -extern int cpttrf_(integer *n, real *d__, complex *e, integer *info); -extern int cpttrs_(char *uplo, integer *n, integer *nrhs, real *d__, complex *e, - complex *b, integer *ldb, integer *info, ftnlen uplo_len); -extern int cptts2_(integer *iuplo, integer *n, integer *nrhs, real *d__, - complex *e, complex *b, integer *ldb); -extern int crot_(integer *n, complex *cx, integer *incx, complex *cy, - integer *incy, real *c__, complex *s); -extern int cspcon_(char *uplo, integer *n, complex *ap, integer *ipiv, - real *anorm, real *rcond, complex *work, integer *info, - ftnlen uplo_len); -extern int cspmv_(char *uplo, integer *n, complex *alpha, complex *ap, - complex *x, integer *incx, complex *beta, complex *y, - integer *incy, ftnlen uplo_len); -extern int cspr_(char *uplo, integer *n, complex *alpha, complex *x, - integer *incx, complex *ap, ftnlen uplo_len); -extern int csprfs_(char *uplo, integer *n, integer *nrhs, complex *ap, - complex *afp, integer *ipiv, complex *b, integer *ldb, - complex *x, integer *ldx, real *ferr, real *berr, - complex *work, real *rwork, integer *info, ftnlen uplo_len); -extern int cspsv_(char *uplo, integer *n, integer *nrhs, complex *ap, - integer *ipiv, complex *b, integer *ldb, integer *info, - ftnlen uplo_len); -extern int cspsvx_(char *fact, char *uplo, integer *n, integer *nrhs, - complex *ap, complex *afp, integer *ipiv, complex *b, - integer *ldb, complex *x, integer *ldx, real *rcond, - real *ferr, real *berr, complex *work, real *rwork, - integer *info, ftnlen fact_len, ftnlen uplo_len); -extern int csptrf_(char *uplo, integer *n, complex *ap, integer *ipiv, - integer *info, ftnlen uplo_len); -extern int csptri_(char *uplo, integer *n, complex *ap, integer *ipiv, - complex *work, integer *info, ftnlen uplo_len); -extern int csptrs_(char *uplo, integer *n, integer *nrhs, complex *ap, - integer *ipiv, complex *b, integer *ldb, integer *info, - ftnlen uplo_len); -extern int csrot_(integer *n, complex *cx, integer *incx, complex *cy, - integer *incy, real *c__, real *s); -extern int csrscl_(integer *n, real *sa, complex *sx, integer *incx); -extern int cstedc_(char *compz, integer *n, real *d__, real *e, complex *z__, - integer *ldz, complex *work, integer *lwork, real *rwork, - integer *lrwork, integer *iwork, integer *liwork, - integer *info, ftnlen compz_len); -extern int cstegr_(char *jobz, char *range, integer *n, real *d__, real *e, - real *vl, real *vu, integer *il, integer *iu, real *abstol, - integer *m, real *w, complex *z__, integer *ldz, - integer *isuppz, real *work, integer *lwork, integer *iwork, - integer *liwork, integer *info, ftnlen jobz_len, - ftnlen range_len); -extern int cstein_(integer *n, real *d__, real *e, integer *m, real *w, - integer *iblock, integer *isplit, complex *z__, integer *ldz, - real *work, integer *iwork, integer *ifail, integer *info); -extern int csteqr_(char *compz, integer *n, real *d__, real *e, complex *z__, - integer *ldz, real *work, integer *info, ftnlen compz_len); -extern int csycon_(char *uplo, integer *n, complex *a, integer *lda, - integer *ipiv, real *anorm, real *rcond, complex *work, - integer *info, ftnlen uplo_len); -extern int csymv_(char *uplo, integer *n, complex *alpha, complex *a, - integer *lda, complex *x, integer *incx, complex *beta, - complex *y, integer *incy, ftnlen uplo_len); -extern int csyr_(char *uplo, integer *n, complex *alpha, complex *x, - integer *incx, complex *a, integer *lda, ftnlen uplo_len); -extern int csyrfs_(char *uplo, integer *n, integer *nrhs, complex *a, - integer *lda, complex *af, integer *ldaf, integer *ipiv, - complex *b, integer *ldb, complex *x, integer *ldx, - real *ferr, real *berr, complex *work, real *rwork, - integer *info, ftnlen uplo_len); -extern int csysv_(char *uplo, integer *n, integer *nrhs, complex *a, - integer *lda, integer *ipiv, complex *b, integer *ldb, - complex *work, integer *lwork, integer *info, - ftnlen uplo_len); -extern int csysvx_(char *fact, char *uplo, integer *n, integer *nrhs, - complex *a, integer *lda, complex *af, integer *ldaf, - integer *ipiv, complex *b, integer *ldb, complex *x, - integer *ldx, real *rcond, real *ferr, real *berr, - complex *work, integer *lwork, real *rwork, integer *info, - ftnlen fact_len, ftnlen uplo_len); -extern int csytf2_(char *uplo, integer *n, complex *a, integer *lda, - integer *ipiv, integer *info, ftnlen uplo_len); -extern int csytrf_(char *uplo, integer *n, complex *a, integer *lda, - integer *ipiv, complex *work, integer *lwork, integer *info, - ftnlen uplo_len); -extern int csytri_(char *uplo, integer *n, complex *a, integer *lda, - integer *ipiv, complex *work, integer *info, - ftnlen uplo_len); -extern int csytrs_(char *uplo, integer *n, integer *nrhs, complex *a, - integer *lda, integer *ipiv, complex *b, integer *ldb, - integer *info, ftnlen uplo_len); -extern int ctbcon_(char *norm, char *uplo, char *diag, integer *n, integer *kd, - complex *ab, integer *ldab, real *rcond, complex *work, - real *rwork, integer *info, ftnlen norm_len, ftnlen uplo_len, - ftnlen diag_len); -extern int ctbrfs_(char *uplo, char *trans, char *diag, integer *n, integer *kd, - integer *nrhs, complex *ab, integer *ldab, complex *b, - integer *ldb, complex *x, integer *ldx, real *ferr, - real *berr, complex *work, real *rwork, integer *info, - ftnlen uplo_len, ftnlen trans_len, ftnlen diag_len); -extern int ctbtrs_(char *uplo, char *trans, char *diag, integer *n, integer *kd, - integer *nrhs, complex *ab, integer *ldab, complex *b, - integer *ldb, integer *info, ftnlen uplo_len, - ftnlen trans_len, ftnlen diag_len); -extern int ctgevc_(char *side, char *howmny, logical *select, integer *n, - complex *a, integer *lda, complex *b, integer *ldb, - complex *vl, integer *ldvl, complex *vr, integer *ldvr, - integer *mm, integer *m, complex *work, real *rwork, - integer *info, ftnlen side_len, ftnlen howmny_len); -extern int ctgex2_(logical *wantq, logical *wantz, integer *n, complex *a, - integer *lda, complex *b, integer *ldb, complex *q, - integer *ldq, complex *z__, integer *ldz, integer *j1, - integer *info); -extern int ctgexc_(logical *wantq, logical *wantz, integer *n, complex *a, - integer *lda, complex *b, integer *ldb, complex *q, - integer *ldq, complex *z__, integer *ldz, integer *ifst, - integer *ilst, integer *info); -extern int ctgsen_(integer *ijob, logical *wantq, logical *wantz, - logical *select, integer *n, complex *a, integer *lda, - complex *b, integer *ldb, complex *alpha, complex *beta, - complex *q, integer *ldq, complex *z__, integer *ldz, - integer *m, real *pl, real *pr, real *dif, complex *work, - integer *lwork, integer *iwork, integer *liwork, - integer *info); -extern int ctgsja_(char *jobu, char *jobv, char *jobq, integer *m, integer *p, - integer *n, integer *k, integer *l, complex *a, integer *lda, - complex *b, integer *ldb, real *tola, real *tolb, - real *alpha, real *beta, complex *u, integer *ldu, - complex *v, integer *ldv, complex *q, integer *ldq, - complex *work, integer *ncycle, integer *info, - ftnlen jobu_len, ftnlen jobv_len, ftnlen jobq_len); -extern int ctgsna_(char *job, char *howmny, logical *select, integer *n, - complex *a, integer *lda, complex *b, integer *ldb, - complex *vl, integer *ldvl, complex *vr, integer *ldvr, - real *s, real *dif, integer *mm, integer *m, complex *work, - integer *lwork, integer *iwork, integer *info, - ftnlen job_len, ftnlen howmny_len); -extern int ctgsy2_(char *trans, integer *ijob, integer *m, integer *n, - complex *a, integer *lda, complex *b, integer *ldb, - complex *c__, integer *ldc, complex *d__, integer *ldd, - complex *e, integer *lde, complex *f, integer *ldf, - real *scale, real *rdsum, real *rdscal, integer *info, - ftnlen trans_len); -extern int ctgsyl_(char *trans, integer *ijob, integer *m, integer *n, - complex *a, integer *lda, complex *b, integer *ldb, - complex *c__, integer *ldc, complex *d__, integer *ldd, - complex *e, integer *lde, complex *f, integer *ldf, - real *scale, real *dif, complex *work, integer *lwork, - integer *iwork, integer *info, ftnlen trans_len); -extern int ctpcon_(char *norm, char *uplo, char *diag, integer *n, complex *ap, - real *rcond, complex *work, real *rwork, integer *info, - ftnlen norm_len, ftnlen uplo_len, ftnlen diag_len); -extern int ctprfs_(char *uplo, char *trans, char *diag, integer *n, - integer *nrhs, complex *ap, complex *b, integer *ldb, - complex *x, integer *ldx, real *ferr, real *berr, - complex *work, real *rwork, integer *info, ftnlen uplo_len, - ftnlen trans_len, ftnlen diag_len); -extern int ctptri_(char *uplo, char *diag, integer *n, complex *ap, - integer *info, ftnlen uplo_len, ftnlen diag_len); -extern int ctptrs_(char *uplo, char *trans, char *diag, integer *n, - integer *nrhs, complex *ap, complex *b, integer *ldb, - integer *info, ftnlen uplo_len, ftnlen trans_len, - ftnlen diag_len); -extern int ctrcon_(char *norm, char *uplo, char *diag, integer *n, complex *a, - integer *lda, real *rcond, complex *work, real *rwork, - integer *info, ftnlen norm_len, ftnlen uplo_len, - ftnlen diag_len); -extern int ctrevc_(char *side, char *howmny, logical *select, integer *n, - complex *t, integer *ldt, complex *vl, integer *ldvl, - complex *vr, integer *ldvr, integer *mm, integer *m, - complex *work, real *rwork, integer *info, ftnlen side_len, - ftnlen howmny_len); -extern int ctrexc_(char *compq, integer *n, complex *t, integer *ldt, - complex *q, integer *ldq, integer *ifst, integer *ilst, - integer *info, ftnlen compq_len); -extern int ctrrfs_(char *uplo, char *trans, char *diag, integer *n, - integer *nrhs, complex *a, integer *lda, complex *b, - integer *ldb, complex *x, integer *ldx, real *ferr, - real *berr, complex *work, real *rwork, integer *info, - ftnlen uplo_len, ftnlen trans_len, ftnlen diag_len); -extern int ctrsen_(char *job, char *compq, logical *select, integer *n, - complex *t, integer *ldt, complex *q, integer *ldq, - complex *w, integer *m, real *s, real *sep, complex *work, - integer *lwork, integer *info, ftnlen job_len, - ftnlen compq_len); -extern int ctrsna_(char *job, char *howmny, logical *select, integer *n, - complex *t, integer *ldt, complex *vl, integer *ldvl, - complex *vr, integer *ldvr, real *s, real *sep, integer *mm, - integer *m, complex *work, integer *ldwork, real *rwork, - integer *info, ftnlen job_len, ftnlen howmny_len); -extern int ctrsyl_(char *trana, char *tranb, integer *isgn, integer *m, - integer *n, complex *a, integer *lda, complex *b, - integer *ldb, complex *c__, integer *ldc, real *scale, - integer *info, ftnlen trana_len, ftnlen tranb_len); -extern int ctrti2_(char *uplo, char *diag, integer *n, complex *a, integer *lda, - integer *info, ftnlen uplo_len, ftnlen diag_len); -extern int ctrtri_(char *uplo, char *diag, integer *n, complex *a, integer *lda, - integer *info, ftnlen uplo_len, ftnlen diag_len); -extern int ctrtrs_(char *uplo, char *trans, char *diag, integer *n, - integer *nrhs, complex *a, integer *lda, complex *b, - integer *ldb, integer *info, ftnlen uplo_len, - ftnlen trans_len, ftnlen diag_len); -extern int ctzrqf_(integer *m, integer *n, complex *a, integer *lda, - complex *tau, integer *info); -extern int ctzrzf_(integer *m, integer *n, complex *a, integer *lda, - complex *tau, complex *work, integer *lwork, integer *info); -extern int cung2l_(integer *m, integer *n, integer *k, complex *a, integer *lda, - complex *tau, complex *work, integer *info); -extern int cung2r_(integer *m, integer *n, integer *k, complex *a, integer *lda, - complex *tau, complex *work, integer *info); -extern int cungbr_(char *vect, integer *m, integer *n, integer *k, complex *a, - integer *lda, complex *tau, complex *work, integer *lwork, - integer *info, ftnlen vect_len); -extern int cunghr_(integer *n, integer *ilo, integer *ihi, complex *a, - integer *lda, complex *tau, complex *work, integer *lwork, - integer *info); -extern int cungl2_(integer *m, integer *n, integer *k, complex *a, integer *lda, - complex *tau, complex *work, integer *info); -extern int cunglq_(integer *m, integer *n, integer *k, complex *a, integer *lda, - complex *tau, complex *work, integer *lwork, integer *info); -extern int cungql_(integer *m, integer *n, integer *k, complex *a, integer *lda, - complex *tau, complex *work, integer *lwork, integer *info); -extern int cungqr_(integer *m, integer *n, integer *k, complex *a, integer *lda, - complex *tau, complex *work, integer *lwork, integer *info); -extern int cungr2_(integer *m, integer *n, integer *k, complex *a, integer *lda, - complex *tau, complex *work, integer *info); -extern int cungrq_(integer *m, integer *n, integer *k, complex *a, integer *lda, - complex *tau, complex *work, integer *lwork, integer *info); -extern int cungtr_(char *uplo, integer *n, complex *a, integer *lda, - complex *tau, complex *work, integer *lwork, integer *info, - ftnlen uplo_len); -extern int cunm2l_(char *side, char *trans, integer *m, integer *n, integer *k, - complex *a, integer *lda, complex *tau, complex *c__, - integer *ldc, complex *work, integer *info, ftnlen side_len, - ftnlen trans_len); -extern int cunm2r_(char *side, char *trans, integer *m, integer *n, integer *k, - complex *a, integer *lda, complex *tau, complex *c__, - integer *ldc, complex *work, integer *info, ftnlen side_len, - ftnlen trans_len); -extern int cunmbr_(char *vect, char *side, char *trans, integer *m, integer *n, - integer *k, complex *a, integer *lda, complex *tau, - complex *c__, integer *ldc, complex *work, integer *lwork, - integer *info, ftnlen vect_len, ftnlen side_len, - ftnlen trans_len); -extern int cunmhr_(char *side, char *trans, integer *m, integer *n, - integer *ilo, integer *ihi, complex *a, integer *lda, - complex *tau, complex *c__, integer *ldc, complex *work, - integer *lwork, integer *info, ftnlen side_len, - ftnlen trans_len); -extern int cunml2_(char *side, char *trans, integer *m, integer *n, integer *k, - complex *a, integer *lda, complex *tau, complex *c__, - integer *ldc, complex *work, integer *info, ftnlen side_len, - ftnlen trans_len); -extern int cunmlq_(char *side, char *trans, integer *m, integer *n, integer *k, - complex *a, integer *lda, complex *tau, complex *c__, - integer *ldc, complex *work, integer *lwork, integer *info, - ftnlen side_len, ftnlen trans_len); -extern int cunmql_(char *side, char *trans, integer *m, integer *n, integer *k, - complex *a, integer *lda, complex *tau, complex *c__, - integer *ldc, complex *work, integer *lwork, integer *info, - ftnlen side_len, ftnlen trans_len); -extern int cunmqr_(char *side, char *trans, integer *m, integer *n, integer *k, - complex *a, integer *lda, complex *tau, complex *c__, - integer *ldc, complex *work, integer *lwork, integer *info, - ftnlen side_len, ftnlen trans_len); -extern int cunmr2_(char *side, char *trans, integer *m, integer *n, integer *k, - complex *a, integer *lda, complex *tau, complex *c__, - integer *ldc, complex *work, integer *info, ftnlen side_len, - ftnlen trans_len); -extern int cunmr3_(char *side, char *trans, integer *m, integer *n, integer *k, - integer *l, complex *a, integer *lda, complex *tau, - complex *c__, integer *ldc, complex *work, integer *info, - ftnlen side_len, ftnlen trans_len); -extern int cunmrq_(char *side, char *trans, integer *m, integer *n, integer *k, - complex *a, integer *lda, complex *tau, complex *c__, - integer *ldc, complex *work, integer *lwork, integer *info, - ftnlen side_len, ftnlen trans_len); -extern int cunmrz_(char *side, char *trans, integer *m, integer *n, integer *k, - integer *l, complex *a, integer *lda, complex *tau, - complex *c__, integer *ldc, complex *work, integer *lwork, - integer *info, ftnlen side_len, ftnlen trans_len); -extern int cunmtr_(char *side, char *uplo, char *trans, integer *m, integer *n, - complex *a, integer *lda, complex *tau, complex *c__, - integer *ldc, complex *work, integer *lwork, integer *info, - ftnlen side_len, ftnlen uplo_len, ftnlen trans_len); -extern int cupgtr_(char *uplo, integer *n, complex *ap, complex *tau, - complex *q, integer *ldq, complex *work, integer *info, - ftnlen uplo_len); -extern int cupmtr_(char *side, char *uplo, char *trans, integer *m, integer *n, - complex *ap, complex *tau, complex *c__, integer *ldc, - complex *work, integer *info, ftnlen side_len, - ftnlen uplo_len, ftnlen trans_len); -extern int dbdsdc_(char *uplo, char *compq, integer *n, doublereal *d__, - doublereal *e, doublereal *u, integer *ldu, doublereal *vt, - integer *ldvt, doublereal *q, integer *iq, doublereal *work, - integer *iwork, integer *info, ftnlen uplo_len, - ftnlen compq_len); -extern int dbdsqr_(char *uplo, integer *n, integer *ncvt, integer *nru, - integer *ncc, doublereal *d__, doublereal *e, doublereal *vt, - integer *ldvt, doublereal *u, integer *ldu, doublereal *c__, - integer *ldc, doublereal *work, integer *info, - ftnlen uplo_len); -extern int ddisna_(char *job, integer *m, integer *n, doublereal *d__, - doublereal *sep, integer *info, ftnlen job_len); -extern int dgbbrd_(char *vect, integer *m, integer *n, integer *ncc, - integer *kl, integer *ku, doublereal *ab, integer *ldab, - doublereal *d__, doublereal *e, doublereal *q, integer *ldq, - doublereal *pt, integer *ldpt, doublereal *c__, integer *ldc, - doublereal *work, integer *info, ftnlen vect_len); -extern int dgbcon_(char *norm, integer *n, integer *kl, integer *ku, - doublereal *ab, integer *ldab, integer *ipiv, - doublereal *anorm, doublereal *rcond, doublereal *work, - integer *iwork, integer *info, ftnlen norm_len); -extern int dgbequ_(integer *m, integer *n, integer *kl, integer *ku, - doublereal *ab, integer *ldab, doublereal *r__, - doublereal *c__, doublereal *rowcnd, doublereal *colcnd, - doublereal *amax, integer *info); -extern int dgbrfs_(char *trans, integer *n, integer *kl, integer *ku, - integer *nrhs, doublereal *ab, integer *ldab, - doublereal *afb, integer *ldafb, integer *ipiv, - doublereal *b, integer *ldb, doublereal *x, integer *ldx, - doublereal *ferr, doublereal *berr, doublereal *work, - integer *iwork, integer *info, ftnlen trans_len); -extern int dgbsv_(integer *n, integer *kl, integer *ku, integer *nrhs, - doublereal *ab, integer *ldab, integer *ipiv, doublereal *b, - integer *ldb, integer *info); -extern int dgbsvx_(char *fact, char *trans, integer *n, integer *kl, - integer *ku, integer *nrhs, doublereal *ab, integer *ldab, - doublereal *afb, integer *ldafb, integer *ipiv, char *equed, - doublereal *r__, doublereal *c__, doublereal *b, - integer *ldb, doublereal *x, integer *ldx, doublereal *rcond, - doublereal *ferr, doublereal *berr, doublereal *work, - integer *iwork, integer *info, ftnlen fact_len, - ftnlen trans_len, ftnlen equed_len); -extern int dgbtf2_(integer *m, integer *n, integer *kl, integer *ku, - doublereal *ab, integer *ldab, integer *ipiv, integer *info); -extern int dgbtrf_(integer *m, integer *n, integer *kl, integer *ku, - doublereal *ab, integer *ldab, integer *ipiv, integer *info); -extern int dgbtrs_(char *trans, integer *n, integer *kl, integer *ku, - integer *nrhs, doublereal *ab, integer *ldab, integer *ipiv, - doublereal *b, integer *ldb, integer *info, - ftnlen trans_len); -extern int dgebak_(char *job, char *side, integer *n, integer *ilo, - integer *ihi, doublereal *scale, integer *m, doublereal *v, - integer *ldv, integer *info, ftnlen job_len, - ftnlen side_len); -extern int dgebal_(char *job, integer *n, doublereal *a, integer *lda, - integer *ilo, integer *ihi, doublereal *scale, integer *info, - ftnlen job_len); -extern int dgebd2_(integer *m, integer *n, doublereal *a, integer *lda, - doublereal *d__, doublereal *e, doublereal *tauq, - doublereal *taup, doublereal *work, integer *info); -extern int dgebrd_(integer *m, integer *n, doublereal *a, integer *lda, - doublereal *d__, doublereal *e, doublereal *tauq, - doublereal *taup, doublereal *work, integer *lwork, - integer *info); -extern int dgecon_(char *norm, integer *n, doublereal *a, integer *lda, - doublereal *anorm, doublereal *rcond, doublereal *work, - integer *iwork, integer *info, ftnlen norm_len); -extern int dgeequ_(integer *m, integer *n, doublereal *a, integer *lda, - doublereal *r__, doublereal *c__, doublereal *rowcnd, - doublereal *colcnd, doublereal *amax, integer *info); -extern int dgees_(char *jobvs, char *sort, L_fp select, integer *n, - doublereal *a, integer *lda, integer *sdim, doublereal *wr, - doublereal *wi, doublereal *vs, integer *ldvs, - doublereal *work, integer *lwork, logical *bwork, - integer *info, ftnlen jobvs_len, ftnlen sort_len); -extern int dgeesx_(char *jobvs, char *sort, L_fp select, char *sense, - integer *n, doublereal *a, integer *lda, integer *sdim, - doublereal *wr, doublereal *wi, doublereal *vs, - integer *ldvs, doublereal *rconde, doublereal *rcondv, - doublereal *work, integer *lwork, integer *iwork, - integer *liwork, logical *bwork, integer *info, - ftnlen jobvs_len, ftnlen sort_len, ftnlen sense_len); -extern int dgeev_(char *jobvl, char *jobvr, integer *n, doublereal *a, - integer *lda, doublereal *wr, doublereal *wi, doublereal *vl, - integer *ldvl, doublereal *vr, integer *ldvr, - doublereal *work, integer *lwork, integer *info, - ftnlen jobvl_len, ftnlen jobvr_len); -extern int dgeevx_(char *balanc, char *jobvl, char *jobvr, char *sense, - integer *n, doublereal *a, integer *lda, doublereal *wr, - doublereal *wi, doublereal *vl, integer *ldvl, - doublereal *vr, integer *ldvr, integer *ilo, integer *ihi, - doublereal *scale, doublereal *abnrm, doublereal *rconde, - doublereal *rcondv, doublereal *work, integer *lwork, - integer *iwork, integer *info, ftnlen balanc_len, - ftnlen jobvl_len, ftnlen jobvr_len, ftnlen sense_len); -extern int dgegs_(char *jobvsl, char *jobvsr, integer *n, doublereal *a, - integer *lda, doublereal *b, integer *ldb, doublereal *alphar, - doublereal *alphai, doublereal *beta, doublereal *vsl, - integer *ldvsl, doublereal *vsr, integer *ldvsr, - doublereal *work, integer *lwork, integer *info, - ftnlen jobvsl_len, ftnlen jobvsr_len); -extern int dgegv_(char *jobvl, char *jobvr, integer *n, doublereal *a, - integer *lda, doublereal *b, integer *ldb, doublereal *alphar, - doublereal *alphai, doublereal *beta, doublereal *vl, - integer *ldvl, doublereal *vr, integer *ldvr, - doublereal *work, integer *lwork, integer *info, - ftnlen jobvl_len, ftnlen jobvr_len); -extern int dgehd2_(integer *n, integer *ilo, integer *ihi, doublereal *a, - integer *lda, doublereal *tau, doublereal *work, - integer *info); -extern int dgehrd_(integer *n, integer *ilo, integer *ihi, doublereal *a, - integer *lda, doublereal *tau, doublereal *work, - integer *lwork, integer *info); -extern int dgelq2_(integer *m, integer *n, doublereal *a, integer *lda, - doublereal *tau, doublereal *work, integer *info); -extern int dgelqf_(integer *m, integer *n, doublereal *a, integer *lda, - doublereal *tau, doublereal *work, integer *lwork, - integer *info); -extern int dgels_(char *trans, integer *m, integer *n, integer *nrhs, - doublereal *a, integer *lda, doublereal *b, integer *ldb, - doublereal *work, integer *lwork, integer *info, - ftnlen trans_len); -extern int dgelsd_(integer *m, integer *n, integer *nrhs, doublereal *a, - integer *lda, doublereal *b, integer *ldb, doublereal *s, - doublereal *rcond, integer *rank, doublereal *work, - integer *lwork, integer *iwork, integer *info); -extern int dgelss_(integer *m, integer *n, integer *nrhs, doublereal *a, - integer *lda, doublereal *b, integer *ldb, doublereal *s, - doublereal *rcond, integer *rank, doublereal *work, - integer *lwork, integer *info); -extern int dgelsx_(integer *m, integer *n, integer *nrhs, doublereal *a, - integer *lda, doublereal *b, integer *ldb, integer *jpvt, - doublereal *rcond, integer *rank, doublereal *work, - integer *info); -extern int dgelsy_(integer *m, integer *n, integer *nrhs, doublereal *a, - integer *lda, doublereal *b, integer *ldb, integer *jpvt, - doublereal *rcond, integer *rank, doublereal *work, - integer *lwork, integer *info); -extern int dgeql2_(integer *m, integer *n, doublereal *a, integer *lda, - doublereal *tau, doublereal *work, integer *info); -extern int dgeqlf_(integer *m, integer *n, doublereal *a, integer *lda, - doublereal *tau, doublereal *work, integer *lwork, - integer *info); -extern int dgeqp3_(integer *m, integer *n, doublereal *a, integer *lda, - integer *jpvt, doublereal *tau, doublereal *work, - integer *lwork, integer *info); -extern int dgeqpf_(integer *m, integer *n, doublereal *a, integer *lda, - integer *jpvt, doublereal *tau, doublereal *work, - integer *info); -extern int dgeqr2_(integer *m, integer *n, doublereal *a, integer *lda, - doublereal *tau, doublereal *work, integer *info); -extern int dgeqrf_(integer *m, integer *n, doublereal *a, integer *lda, - doublereal *tau, doublereal *work, integer *lwork, - integer *info); -extern int dgerfs_(char *trans, integer *n, integer *nrhs, doublereal *a, - integer *lda, doublereal *af, integer *ldaf, integer *ipiv, - doublereal *b, integer *ldb, doublereal *x, integer *ldx, - doublereal *ferr, doublereal *berr, doublereal *work, - integer *iwork, integer *info, ftnlen trans_len); -extern int dgerq2_(integer *m, integer *n, doublereal *a, integer *lda, - doublereal *tau, doublereal *work, integer *info); -extern int dgerqf_(integer *m, integer *n, doublereal *a, integer *lda, - doublereal *tau, doublereal *work, integer *lwork, - integer *info); -extern int dgesc2_(integer *n, doublereal *a, integer *lda, doublereal *rhs, - integer *ipiv, integer *jpiv, doublereal *scale); -extern int dgesdd_(char *jobz, integer *m, integer *n, doublereal *a, - integer *lda, doublereal *s, doublereal *u, integer *ldu, - doublereal *vt, integer *ldvt, doublereal *work, - integer *lwork, integer *iwork, integer *info, - ftnlen jobz_len); -extern int dgesv_(integer *n, integer *nrhs, doublereal *a, integer *lda, - integer *ipiv, doublereal *b, integer *ldb, integer *info); -extern int dgesvd_(char *jobu, char *jobvt, integer *m, integer *n, - doublereal *a, integer *lda, doublereal *s, doublereal *u, - integer *ldu, doublereal *vt, integer *ldvt, - doublereal *work, integer *lwork, integer *info, - ftnlen jobu_len, ftnlen jobvt_len); -extern int dgesvx_(char *fact, char *trans, integer *n, integer *nrhs, - doublereal *a, integer *lda, doublereal *af, integer *ldaf, - integer *ipiv, char *equed, doublereal *r__, doublereal *c__, - doublereal *b, integer *ldb, doublereal *x, integer *ldx, - doublereal *rcond, doublereal *ferr, doublereal *berr, - doublereal *work, integer *iwork, integer *info, - ftnlen fact_len, ftnlen trans_len, ftnlen equed_len); -extern int dgetc2_(integer *n, doublereal *a, integer *lda, integer *ipiv, - integer *jpiv, integer *info); -extern int dgetf2_(integer *m, integer *n, doublereal *a, integer *lda, - integer *ipiv, integer *info); -extern int dgetrf_(integer *m, integer *n, doublereal *a, integer *lda, - integer *ipiv, integer *info); -extern int dgetri_(integer *n, doublereal *a, integer *lda, integer *ipiv, - doublereal *work, integer *lwork, integer *info); -extern int dgetrs_(char *trans, integer *n, integer *nrhs, doublereal *a, - integer *lda, integer *ipiv, doublereal *b, integer *ldb, - integer *info, ftnlen trans_len); -extern int dggbak_(char *job, char *side, integer *n, integer *ilo, - integer *ihi, doublereal *lscale, doublereal *rscale, - integer *m, doublereal *v, integer *ldv, integer *info, - ftnlen job_len, ftnlen side_len); -extern int dggbal_(char *job, integer *n, doublereal *a, integer *lda, - doublereal *b, integer *ldb, integer *ilo, integer *ihi, - doublereal *lscale, doublereal *rscale, doublereal *work, - integer *info, ftnlen job_len); -extern int dgges_(char *jobvsl, char *jobvsr, char *sort, L_fp delctg, - integer *n, doublereal *a, integer *lda, doublereal *b, - integer *ldb, integer *sdim, doublereal *alphar, - doublereal *alphai, doublereal *beta, doublereal *vsl, - integer *ldvsl, doublereal *vsr, integer *ldvsr, - doublereal *work, integer *lwork, logical *bwork, - integer *info, ftnlen jobvsl_len, ftnlen jobvsr_len, - ftnlen sort_len); -extern int dggesx_(char *jobvsl, char *jobvsr, char *sort, L_fp delctg, - char *sense, integer *n, doublereal *a, integer *lda, - doublereal *b, integer *ldb, integer *sdim, - doublereal *alphar, doublereal *alphai, doublereal *beta, - doublereal *vsl, integer *ldvsl, doublereal *vsr, - integer *ldvsr, doublereal *rconde, doublereal *rcondv, - doublereal *work, integer *lwork, integer *iwork, - integer *liwork, logical *bwork, integer *info, - ftnlen jobvsl_len, ftnlen jobvsr_len, ftnlen sort_len, - ftnlen sense_len); -extern int dggev_(char *jobvl, char *jobvr, integer *n, doublereal *a, - integer *lda, doublereal *b, integer *ldb, doublereal *alphar, - doublereal *alphai, doublereal *beta, doublereal *vl, - integer *ldvl, doublereal *vr, integer *ldvr, - doublereal *work, integer *lwork, integer *info, - ftnlen jobvl_len, ftnlen jobvr_len); -extern int dggevx_(char *balanc, char *jobvl, char *jobvr, char *sense, - integer *n, doublereal *a, integer *lda, doublereal *b, - integer *ldb, doublereal *alphar, doublereal *alphai, - doublereal *beta, doublereal *vl, integer *ldvl, - doublereal *vr, integer *ldvr, integer *ilo, integer *ihi, - doublereal *lscale, doublereal *rscale, doublereal *abnrm, - doublereal *bbnrm, doublereal *rconde, doublereal *rcondv, - doublereal *work, integer *lwork, integer *iwork, - logical *bwork, integer *info, ftnlen balanc_len, - ftnlen jobvl_len, ftnlen jobvr_len, ftnlen sense_len); -extern int dggglm_(integer *n, integer *m, integer *p, doublereal *a, - integer *lda, doublereal *b, integer *ldb, doublereal *d__, - doublereal *x, doublereal *y, doublereal *work, - integer *lwork, integer *info); -extern int dgghrd_(char *compq, char *compz, integer *n, integer *ilo, - integer *ihi, doublereal *a, integer *lda, doublereal *b, - integer *ldb, doublereal *q, integer *ldq, doublereal *z__, - integer *ldz, integer *info, ftnlen compq_len, - ftnlen compz_len); -extern int dgglse_(integer *m, integer *n, integer *p, doublereal *a, - integer *lda, doublereal *b, integer *ldb, doublereal *c__, - doublereal *d__, doublereal *x, doublereal *work, - integer *lwork, integer *info); -extern int dggqrf_(integer *n, integer *m, integer *p, doublereal *a, - integer *lda, doublereal *taua, doublereal *b, integer *ldb, - doublereal *taub, doublereal *work, integer *lwork, - integer *info); -extern int dggrqf_(integer *m, integer *p, integer *n, doublereal *a, - integer *lda, doublereal *taua, doublereal *b, integer *ldb, - doublereal *taub, doublereal *work, integer *lwork, - integer *info); -extern int dggsvd_(char *jobu, char *jobv, char *jobq, integer *m, integer *n, - integer *p, integer *k, integer *l, doublereal *a, - integer *lda, doublereal *b, integer *ldb, doublereal *alpha, - doublereal *beta, doublereal *u, integer *ldu, doublereal *v, - integer *ldv, doublereal *q, integer *ldq, doublereal *work, - integer *iwork, integer *info, ftnlen jobu_len, - ftnlen jobv_len, ftnlen jobq_len); -extern int dggsvp_(char *jobu, char *jobv, char *jobq, integer *m, integer *p, - integer *n, doublereal *a, integer *lda, doublereal *b, - integer *ldb, doublereal *tola, doublereal *tolb, integer *k, - integer *l, doublereal *u, integer *ldu, doublereal *v, - integer *ldv, doublereal *q, integer *ldq, integer *iwork, - doublereal *tau, doublereal *work, integer *info, - ftnlen jobu_len, ftnlen jobv_len, ftnlen jobq_len); -extern int dgtcon_(char *norm, integer *n, doublereal *dl, doublereal *d__, - doublereal *du, doublereal *du2, integer *ipiv, - doublereal *anorm, doublereal *rcond, doublereal *work, - integer *iwork, integer *info, ftnlen norm_len); -extern int dgtrfs_(char *trans, integer *n, integer *nrhs, doublereal *dl, - doublereal *d__, doublereal *du, doublereal *dlf, - doublereal *df, doublereal *duf, doublereal *du2, - integer *ipiv, doublereal *b, integer *ldb, doublereal *x, - integer *ldx, doublereal *ferr, doublereal *berr, - doublereal *work, integer *iwork, integer *info, - ftnlen trans_len); -extern int dgtsv_(integer *n, integer *nrhs, doublereal *dl, doublereal *d__, - doublereal *du, doublereal *b, integer *ldb, integer *info); -extern int dgtsvx_(char *fact, char *trans, integer *n, integer *nrhs, - doublereal *dl, doublereal *d__, doublereal *du, - doublereal *dlf, doublereal *df, doublereal *duf, - doublereal *du2, integer *ipiv, doublereal *b, integer *ldb, - doublereal *x, integer *ldx, doublereal *rcond, - doublereal *ferr, doublereal *berr, doublereal *work, - integer *iwork, integer *info, ftnlen fact_len, - ftnlen trans_len); -extern int dgttrf_(integer *n, doublereal *dl, doublereal *d__, doublereal *du, - doublereal *du2, integer *ipiv, integer *info); -extern int dgttrs_(char *trans, integer *n, integer *nrhs, doublereal *dl, - doublereal *d__, doublereal *du, doublereal *du2, - integer *ipiv, doublereal *b, integer *ldb, integer *info, - ftnlen trans_len); -extern int dgtts2_(integer *itrans, integer *n, integer *nrhs, doublereal *dl, - doublereal *d__, doublereal *du, doublereal *du2, - integer *ipiv, doublereal *b, integer *ldb); -extern int dhgeqz_(char *job, char *compq, char *compz, integer *n, - integer *ilo, integer *ihi, doublereal *a, integer *lda, - doublereal *b, integer *ldb, doublereal *alphar, - doublereal *alphai, doublereal *beta, doublereal *q, - integer *ldq, doublereal *z__, integer *ldz, - doublereal *work, integer *lwork, integer *info, - ftnlen job_len, ftnlen compq_len, ftnlen compz_len); -extern int dhsein_(char *side, char *eigsrc, char *initv, logical *select, - integer *n, doublereal *h__, integer *ldh, doublereal *wr, - doublereal *wi, doublereal *vl, integer *ldvl, - doublereal *vr, integer *ldvr, integer *mm, integer *m, - doublereal *work, integer *ifaill, integer *ifailr, - integer *info, ftnlen side_len, ftnlen eigsrc_len, - ftnlen initv_len); -extern int dhseqr_(char *job, char *compz, integer *n, integer *ilo, - integer *ihi, doublereal *h__, integer *ldh, doublereal *wr, - doublereal *wi, doublereal *z__, integer *ldz, - doublereal *work, integer *lwork, integer *info, - ftnlen job_len, ftnlen compz_len); -extern int dlabad_(doublereal *small, doublereal *large); -extern int dlabrd_(integer *m, integer *n, integer *nb, doublereal *a, - integer *lda, doublereal *d__, doublereal *e, - doublereal *tauq, doublereal *taup, doublereal *x, - integer *ldx, doublereal *y, integer *ldy); -extern int dlacon_(integer *n, doublereal *v, doublereal *x, integer *isgn, - doublereal *est, integer *kase); -extern int dlacpy_(char *uplo, integer *m, integer *n, doublereal *a, - integer *lda, doublereal *b, integer *ldb, ftnlen uplo_len); -extern int dladiv_(doublereal *a, doublereal *b, doublereal *c__, - doublereal *d__, doublereal *p, doublereal *q); -extern int dlae2_(doublereal *a, doublereal *b, doublereal *c__, - doublereal *rt1, doublereal *rt2); -extern int dlaebz_(integer *ijob, integer *nitmax, integer *n, integer *mmax, - integer *minp, integer *nbmin, doublereal *abstol, - doublereal *reltol, doublereal *pivmin, doublereal *d__, - doublereal *e, doublereal *e2, integer *nval, doublereal *ab, - doublereal *c__, integer *mout, integer *nab, - doublereal *work, integer *iwork, integer *info); -extern int dlaed0_(integer *icompq, integer *qsiz, integer *n, doublereal *d__, - doublereal *e, doublereal *q, integer *ldq, - doublereal *qstore, integer *ldqs, doublereal *work, - integer *iwork, integer *info); -extern int dlaed1_(integer *n, doublereal *d__, doublereal *q, integer *ldq, - integer *indxq, doublereal *rho, integer *cutpnt, - doublereal *work, integer *iwork, integer *info); -extern int dlaed2_(integer *k, integer *n, integer *n1, doublereal *d__, - doublereal *q, integer *ldq, integer *indxq, doublereal *rho, - doublereal *z__, doublereal *dlamda, doublereal *w, - doublereal *q2, integer *indx, integer *indxc, - integer *indxp, integer *coltyp, integer *info); -extern int dlaed3_(integer *k, integer *n, integer *n1, doublereal *d__, - doublereal *q, integer *ldq, doublereal *rho, - doublereal *dlamda, doublereal *q2, integer *indx, - integer *ctot, doublereal *w, doublereal *s, integer *info); -extern int dlaed4_(integer *n, integer *i__, doublereal *d__, doublereal *z__, - doublereal *delta, doublereal *rho, doublereal *dlam, - integer *info); -extern int dlaed5_(integer *i__, doublereal *d__, doublereal *z__, - doublereal *delta, doublereal *rho, doublereal *dlam); -extern int dlaed6_(integer *kniter, logical *orgati, doublereal *rho, - doublereal *d__, doublereal *z__, doublereal *finit, - doublereal *tau, integer *info); -extern int dlaed7_(integer *icompq, integer *n, integer *qsiz, integer *tlvls, - integer *curlvl, integer *curpbm, doublereal *d__, - doublereal *q, integer *ldq, integer *indxq, doublereal *rho, - integer *cutpnt, doublereal *qstore, integer *qptr, - integer *prmptr, integer *perm, integer *givptr, - integer *givcol, doublereal *givnum, doublereal *work, - integer *iwork, integer *info); -extern int dlaed8_(integer *icompq, integer *k, integer *n, integer *qsiz, - doublereal *d__, doublereal *q, integer *ldq, integer *indxq, - doublereal *rho, integer *cutpnt, doublereal *z__, - doublereal *dlamda, doublereal *q2, integer *ldq2, - doublereal *w, integer *perm, integer *givptr, - integer *givcol, doublereal *givnum, integer *indxp, - integer *indx, integer *info); -extern int dlaed9_(integer *k, integer *kstart, integer *kstop, integer *n, - doublereal *d__, doublereal *q, integer *ldq, - doublereal *rho, doublereal *dlamda, doublereal *w, - doublereal *s, integer *lds, integer *info); -extern int dlaeda_(integer *n, integer *tlvls, integer *curlvl, integer *curpbm, - integer *prmptr, integer *perm, integer *givptr, - integer *givcol, doublereal *givnum, doublereal *q, - integer *qptr, doublereal *z__, doublereal *ztemp, - integer *info); -extern int dlaein_(logical *rightv, logical *noinit, integer *n, - doublereal *h__, integer *ldh, doublereal *wr, - doublereal *wi, doublereal *vr, doublereal *vi, - doublereal *b, integer *ldb, doublereal *work, - doublereal *eps3, doublereal *smlnum, doublereal *bignum, - integer *info); -extern int dlaev2_(doublereal *a, doublereal *b, doublereal *c__, - doublereal *rt1, doublereal *rt2, doublereal *cs1, - doublereal *sn1); -extern int dlaexc_(logical *wantq, integer *n, doublereal *t, integer *ldt, - doublereal *q, integer *ldq, integer *j1, integer *n1, - integer *n2, doublereal *work, integer *info); -extern int dlag2_(doublereal *a, integer *lda, doublereal *b, integer *ldb, - doublereal *safmin, doublereal *scale1, doublereal *scale2, - doublereal *wr1, doublereal *wr2, doublereal *wi); -extern int dlags2_(logical *upper, doublereal *a1, doublereal *a2, - doublereal *a3, doublereal *b1, doublereal *b2, - doublereal *b3, doublereal *csu, doublereal *snu, - doublereal *csv, doublereal *snv, doublereal *csq, - doublereal *snq); -extern int dlagtf_(integer *n, doublereal *a, doublereal *lambda, doublereal *b, - doublereal *c__, doublereal *tol, doublereal *d__, - integer *in, integer *info); -extern int dlagtm_(char *trans, integer *n, integer *nrhs, doublereal *alpha, - doublereal *dl, doublereal *d__, doublereal *du, - doublereal *x, integer *ldx, doublereal *beta, doublereal *b, - integer *ldb, ftnlen trans_len); -extern int dlagts_(integer *job, integer *n, doublereal *a, doublereal *b, - doublereal *c__, doublereal *d__, integer *in, doublereal *y, - doublereal *tol, integer *info); -extern int dlagv2_(doublereal *a, integer *lda, doublereal *b, integer *ldb, - doublereal *alphar, doublereal *alphai, doublereal *beta, - doublereal *csl, doublereal *snl, doublereal *csr, - doublereal *snr); -extern int dlahqr_(logical *wantt, logical *wantz, integer *n, integer *ilo, - integer *ihi, doublereal *h__, integer *ldh, doublereal *wr, - doublereal *wi, integer *iloz, integer *ihiz, - doublereal *z__, integer *ldz, integer *info); -extern int dlahrd_(integer *n, integer *k, integer *nb, doublereal *a, - integer *lda, doublereal *tau, doublereal *t, integer *ldt, - doublereal *y, integer *ldy); -extern int dlaic1_(integer *job, integer *j, doublereal *x, doublereal *sest, - doublereal *w, doublereal *gamma, doublereal *sestpr, - doublereal *s, doublereal *c__); -extern int dlaln2_(logical *ltrans, integer *na, integer *nw, doublereal *smin, - doublereal *ca, doublereal *a, integer *lda, doublereal *d1, - doublereal *d2, doublereal *b, integer *ldb, doublereal *wr, - doublereal *wi, doublereal *x, integer *ldx, - doublereal *scale, doublereal *xnorm, integer *info); -extern int dlals0_(integer *icompq, integer *nl, integer *nr, integer *sqre, - integer *nrhs, doublereal *b, integer *ldb, doublereal *bx, - integer *ldbx, integer *perm, integer *givptr, - integer *givcol, integer *ldgcol, doublereal *givnum, - integer *ldgnum, doublereal *poles, doublereal *difl, - doublereal *difr, doublereal *z__, integer *k, - doublereal *c__, doublereal *s, doublereal *work, - integer *info); -extern int dlalsa_(integer *icompq, integer *smlsiz, integer *n, integer *nrhs, - doublereal *b, integer *ldb, doublereal *bx, integer *ldbx, - doublereal *u, integer *ldu, doublereal *vt, integer *k, - doublereal *difl, doublereal *difr, doublereal *z__, - doublereal *poles, integer *givptr, integer *givcol, - integer *ldgcol, integer *perm, doublereal *givnum, - doublereal *c__, doublereal *s, doublereal *work, - integer *iwork, integer *info); -extern int dlalsd_(char *uplo, integer *smlsiz, integer *n, integer *nrhs, - doublereal *d__, doublereal *e, doublereal *b, integer *ldb, - doublereal *rcond, integer *rank, doublereal *work, - integer *iwork, integer *info, ftnlen uplo_len); -extern doublereal dlamch_(char *cmach, ftnlen cmach_len); -extern int dlamc1_(integer *beta, integer *t, logical *rnd, logical *ieee1); -extern int dlamc2_(integer *beta, integer *t, logical *rnd, doublereal *eps, - integer *emin, doublereal *rmin, integer *emax, - doublereal *rmax); -extern doublereal dlamc3_(doublereal *a, doublereal *b); -extern int dlamc4_(integer *emin, doublereal *start, integer *base); -extern int dlamc5_(integer *beta, integer *p, integer *emin, logical *ieee, - integer *emax, doublereal *rmax); -extern int dlamrg_(integer *n1, integer *n2, doublereal *a, integer *dtrd1, - integer *dtrd2, integer *index); -extern doublereal dlangb_(char *norm, integer *n, integer *kl, integer *ku, - doublereal *ab, integer *ldab, doublereal *work, - ftnlen norm_len); -extern doublereal dlange_(char *norm, integer *m, integer *n, doublereal *a, - integer *lda, doublereal *work, ftnlen norm_len); -extern doublereal dlangt_(char *norm, integer *n, doublereal *dl, - doublereal *d__, doublereal *du, ftnlen norm_len); -extern doublereal dlanhs_(char *norm, integer *n, doublereal *a, integer *lda, - doublereal *work, ftnlen norm_len); -extern doublereal dlansb_(char *norm, char *uplo, integer *n, integer *k, - doublereal *ab, integer *ldab, doublereal *work, - ftnlen norm_len, ftnlen uplo_len); -extern doublereal dlansp_(char *norm, char *uplo, integer *n, doublereal *ap, - doublereal *work, ftnlen norm_len, ftnlen uplo_len); -extern doublereal dlanst_(char *norm, integer *n, doublereal *d__, - doublereal *e, ftnlen norm_len); -extern doublereal dlansy_(char *norm, char *uplo, integer *n, doublereal *a, - integer *lda, doublereal *work, ftnlen norm_len, - ftnlen uplo_len); -extern doublereal dlantb_(char *norm, char *uplo, char *diag, integer *n, - integer *k, doublereal *ab, integer *ldab, - doublereal *work, ftnlen norm_len, ftnlen uplo_len, - ftnlen diag_len); -extern doublereal dlantp_(char *norm, char *uplo, char *diag, integer *n, - doublereal *ap, doublereal *work, ftnlen norm_len, - ftnlen uplo_len, ftnlen diag_len); -extern doublereal dlantr_(char *norm, char *uplo, char *diag, integer *m, - integer *n, doublereal *a, integer *lda, - doublereal *work, ftnlen norm_len, ftnlen uplo_len, - ftnlen diag_len); -extern int dlanv2_(doublereal *a, doublereal *b, doublereal *c__, - doublereal *d__, doublereal *rt1r, doublereal *rt1i, - doublereal *rt2r, doublereal *rt2i, doublereal *cs, - doublereal *sn); -extern int dlapll_(integer *n, doublereal *x, integer *incx, doublereal *y, - integer *incy, doublereal *ssmin); -extern int dlapmt_(logical *forwrd, integer *m, integer *n, doublereal *x, - integer *ldx, integer *k); -extern doublereal dlapy2_(doublereal *x, doublereal *y); -extern doublereal dlapy3_(doublereal *x, doublereal *y, doublereal *z__); -extern int dlaqgb_(integer *m, integer *n, integer *kl, integer *ku, - doublereal *ab, integer *ldab, doublereal *r__, - doublereal *c__, doublereal *rowcnd, doublereal *colcnd, - doublereal *amax, char *equed, ftnlen equed_len); -extern int dlaqge_(integer *m, integer *n, doublereal *a, integer *lda, - doublereal *r__, doublereal *c__, doublereal *rowcnd, - doublereal *colcnd, doublereal *amax, char *equed, - ftnlen equed_len); -extern int dlaqp2_(integer *m, integer *n, integer *offset, doublereal *a, - integer *lda, integer *jpvt, doublereal *tau, - doublereal *vn1, doublereal *vn2, doublereal *work); -extern int dlaqps_(integer *m, integer *n, integer *offset, integer *nb, - integer *kb, doublereal *a, integer *lda, integer *jpvt, - doublereal *tau, doublereal *vn1, doublereal *vn2, - doublereal *auxv, doublereal *f, integer *ldf); -extern int dlaqsb_(char *uplo, integer *n, integer *kd, doublereal *ab, - integer *ldab, doublereal *s, doublereal *scond, - doublereal *amax, char *equed, ftnlen uplo_len, - ftnlen equed_len); -extern int dlaqsp_(char *uplo, integer *n, doublereal *ap, doublereal *s, - doublereal *scond, doublereal *amax, char *equed, - ftnlen uplo_len, ftnlen equed_len); -extern int dlaqsy_(char *uplo, integer *n, doublereal *a, integer *lda, - doublereal *s, doublereal *scond, doublereal *amax, - char *equed, ftnlen uplo_len, ftnlen equed_len); -extern int dlaqtr_(logical *ltran, logical *lreal, integer *n, doublereal *t, - integer *ldt, doublereal *b, doublereal *w, - doublereal *scale, doublereal *x, doublereal *work, - integer *info); -extern int dlar1v_(integer *n, integer *b1, integer *bn, doublereal *sigma, - doublereal *d__, doublereal *l, doublereal *ld, - doublereal *lld, doublereal *gersch, doublereal *z__, - doublereal *ztz, doublereal *mingma, integer *r__, - integer *isuppz, doublereal *work); -extern int dlar2v_(integer *n, doublereal *x, doublereal *y, doublereal *z__, - integer *incx, doublereal *c__, doublereal *s, - integer *incc); -extern int dlarf_(char *side, integer *m, integer *n, doublereal *v, - integer *incv, doublereal *tau, doublereal *c__, integer *ldc, - doublereal *work, ftnlen side_len); -extern int dlarfb_(char *side, char *trans, char *direct, char *storev, - integer *m, integer *n, integer *k, doublereal *v, - integer *ldv, doublereal *t, integer *ldt, doublereal *c__, - integer *ldc, doublereal *work, integer *ldwork, - ftnlen side_len, ftnlen trans_len, ftnlen direct_len, - ftnlen storev_len); -extern int dlarfg_(integer *n, doublereal *alpha, doublereal *x, integer *incx, - doublereal *tau); -extern int dlarft_(char *direct, char *storev, integer *n, integer *k, - doublereal *v, integer *ldv, doublereal *tau, doublereal *t, - integer *ldt, ftnlen direct_len, ftnlen storev_len); -extern int dlarfx_(char *side, integer *m, integer *n, doublereal *v, - doublereal *tau, doublereal *c__, integer *ldc, - doublereal *work, ftnlen side_len); -extern int dlargv_(integer *n, doublereal *x, integer *incx, doublereal *y, - integer *incy, doublereal *c__, integer *incc); -extern int dlarnv_(integer *idist, integer *iseed, integer *n, doublereal *x); -extern int dlarrb_(integer *n, doublereal *d__, doublereal *l, doublereal *ld, - doublereal *lld, integer *ifirst, integer *ilast, - doublereal *sigma, doublereal *reltol, doublereal *w, - doublereal *wgap, doublereal *werr, doublereal *work, - integer *iwork, integer *info); -extern int dlarre_(integer *n, doublereal *d__, doublereal *e, doublereal *tol, - integer *nsplit, integer *isplit, integer *m, doublereal *w, - doublereal *woff, doublereal *gersch, doublereal *work, - integer *info); -extern int dlarrf_(integer *n, doublereal *d__, doublereal *l, doublereal *ld, - doublereal *lld, integer *ifirst, integer *ilast, - doublereal *w, doublereal *dplus, doublereal *lplus, - doublereal *work, integer *iwork, integer *info); -extern int dlarrv_(integer *n, doublereal *d__, doublereal *l, integer *isplit, - integer *m, doublereal *w, integer *iblock, - doublereal *gersch, doublereal *tol, doublereal *z__, - integer *ldz, integer *isuppz, doublereal *work, - integer *iwork, integer *info); -extern int dlartg_(doublereal *f, doublereal *g, doublereal *cs, doublereal *sn, - doublereal *r__); -extern int dlartv_(integer *n, doublereal *x, integer *incx, doublereal *y, - integer *incy, doublereal *c__, doublereal *s, - integer *incc); -extern int dlaruv_(integer *iseed, integer *n, doublereal *x); -extern int dlarz_(char *side, integer *m, integer *n, integer *l, doublereal *v, - integer *incv, doublereal *tau, doublereal *c__, integer *ldc, - doublereal *work, ftnlen side_len); -extern int dlarzb_(char *side, char *trans, char *direct, char *storev, - integer *m, integer *n, integer *k, integer *l, - doublereal *v, integer *ldv, doublereal *t, integer *ldt, - doublereal *c__, integer *ldc, doublereal *work, - integer *ldwork, ftnlen side_len, ftnlen trans_len, - ftnlen direct_len, ftnlen storev_len); -extern int dlarzt_(char *direct, char *storev, integer *n, integer *k, - doublereal *v, integer *ldv, doublereal *tau, doublereal *t, - integer *ldt, ftnlen direct_len, ftnlen storev_len); -extern int dlas2_(doublereal *f, doublereal *g, doublereal *h__, - doublereal *ssmin, doublereal *ssmax); -extern int dlascl_(char *type__, integer *kl, integer *ku, doublereal *cfrom, - doublereal *cto, integer *m, integer *n, doublereal *a, - integer *lda, integer *info, ftnlen type_len); -extern int dlasd0_(integer *n, integer *sqre, doublereal *d__, doublereal *e, - doublereal *u, integer *ldu, doublereal *vt, integer *ldvt, - integer *smlsiz, integer *iwork, doublereal *work, - integer *info); -extern int dlasd1_(integer *nl, integer *nr, integer *sqre, doublereal *d__, - doublereal *alpha, doublereal *beta, doublereal *u, - integer *ldu, doublereal *vt, integer *ldvt, integer *idxq, - integer *iwork, doublereal *work, integer *info); -extern int dlasd2_(integer *nl, integer *nr, integer *sqre, integer *k, - doublereal *d__, doublereal *z__, doublereal *alpha, - doublereal *beta, doublereal *u, integer *ldu, - doublereal *vt, integer *ldvt, doublereal *dsigma, - doublereal *u2, integer *ldu2, doublereal *vt2, - integer *ldvt2, integer *idxp, integer *idx, integer *idxc, - integer *idxq, integer *coltyp, integer *info); -extern int dlasd3_(integer *nl, integer *nr, integer *sqre, integer *k, - doublereal *d__, doublereal *q, integer *ldq, - doublereal *dsigma, doublereal *u, integer *ldu, - doublereal *u2, integer *ldu2, doublereal *vt, integer *ldvt, - doublereal *vt2, integer *ldvt2, integer *idxc, - integer *ctot, doublereal *z__, integer *info); -extern int dlasd4_(integer *n, integer *i__, doublereal *d__, doublereal *z__, - doublereal *delta, doublereal *rho, doublereal *sigma, - doublereal *work, integer *info); -extern int dlasd5_(integer *i__, doublereal *d__, doublereal *z__, - doublereal *delta, doublereal *rho, doublereal *dsigma, - doublereal *work); -extern int dlasd6_(integer *icompq, integer *nl, integer *nr, integer *sqre, - doublereal *d__, doublereal *vf, doublereal *vl, - doublereal *alpha, doublereal *beta, integer *idxq, - integer *perm, integer *givptr, integer *givcol, - integer *ldgcol, doublereal *givnum, integer *ldgnum, - doublereal *poles, doublereal *difl, doublereal *difr, - doublereal *z__, integer *k, doublereal *c__, doublereal *s, - doublereal *work, integer *iwork, integer *info); -extern int dlasd7_(integer *icompq, integer *nl, integer *nr, integer *sqre, - integer *k, doublereal *d__, doublereal *z__, doublereal *zw, - doublereal *vf, doublereal *vfw, doublereal *vl, - doublereal *vlw, doublereal *alpha, doublereal *beta, - doublereal *dsigma, integer *idx, integer *idxp, - integer *idxq, integer *perm, integer *givptr, - integer *givcol, integer *ldgcol, doublereal *givnum, - integer *ldgnum, doublereal *c__, doublereal *s, - integer *info); -extern int dlasd8_(integer *icompq, integer *k, doublereal *d__, - doublereal *z__, doublereal *vf, doublereal *vl, - doublereal *difl, doublereal *difr, integer *lddifr, - doublereal *dsigma, doublereal *work, integer *info); -extern int dlasd9_(integer *icompq, integer *ldu, integer *k, doublereal *d__, - doublereal *z__, doublereal *vf, doublereal *vl, - doublereal *difl, doublereal *difr, doublereal *dsigma, - doublereal *work, integer *info); -extern int dlasda_(integer *icompq, integer *smlsiz, integer *n, integer *sqre, - doublereal *d__, doublereal *e, doublereal *u, integer *ldu, - doublereal *vt, integer *k, doublereal *difl, - doublereal *difr, doublereal *z__, doublereal *poles, - integer *givptr, integer *givcol, integer *ldgcol, - integer *perm, doublereal *givnum, doublereal *c__, - doublereal *s, doublereal *work, integer *iwork, - integer *info); -extern int dlasdq_(char *uplo, integer *sqre, integer *n, integer *ncvt, - integer *nru, integer *ncc, doublereal *d__, doublereal *e, - doublereal *vt, integer *ldvt, doublereal *u, integer *ldu, - doublereal *c__, integer *ldc, doublereal *work, - integer *info, ftnlen uplo_len); -extern int dlasdt_(integer *n, integer *lvl, integer *nd, integer *inode, - integer *ndiml, integer *ndimr, integer *msub); -extern int dlaset_(char *uplo, integer *m, integer *n, doublereal *alpha, - doublereal *beta, doublereal *a, integer *lda, - ftnlen uplo_len); -extern int dlasq1_(integer *n, doublereal *d__, doublereal *e, doublereal *work, - integer *info); -extern int dlasq2_(integer *n, doublereal *z__, integer *info); -extern int dlasq3_(integer *i0, integer *n0, doublereal *z__, integer *pp, - doublereal *dmin__, doublereal *sigma, doublereal *desig, - doublereal *qmax, integer *nfail, integer *iter, - integer *ndiv); -extern int dlasq4_(integer *i0, integer *n0, doublereal *z__, integer *pp, - integer *n0in, doublereal *dmin__, doublereal *dmin1, - doublereal *dmin2, doublereal *dn, doublereal *dn1, - doublereal *dn2, doublereal *tau, integer *ttype); -extern int dlasq5_(integer *i0, integer *n0, doublereal *z__, integer *pp, - doublereal *tau, doublereal *dmin__, doublereal *dmin1, - doublereal *dmin2, doublereal *dn, doublereal *dnm1, - doublereal *dnm2); -extern int dlasq6_(integer *i0, integer *n0, doublereal *z__, integer *pp, - doublereal *dmin__, doublereal *dmin1, doublereal *dmin2, - doublereal *dn, doublereal *dnm1, doublereal *dnm2); -extern int dlasr_(char *side, char *pivot, char *direct, integer *m, integer *n, - doublereal *c__, doublereal *s, doublereal *a, integer *lda, - ftnlen side_len, ftnlen pivot_len, ftnlen direct_len); -extern int dlasrt_(char *id, integer *n, doublereal *d__, integer *info, - ftnlen id_len); -extern int dlassq_(integer *n, doublereal *x, integer *incx, doublereal *scale, - doublereal *sumsq); -extern int dlasv2_(doublereal *f, doublereal *g, doublereal *h__, - doublereal *ssmin, doublereal *ssmax, doublereal *snr, - doublereal *csr, doublereal *snl, doublereal *csl); -extern int dlaswp_(integer *n, doublereal *a, integer *lda, integer *k1, - integer *k2, integer *ipiv, integer *incx); -extern int dlasy2_(logical *ltranl, logical *ltranr, integer *isgn, integer *n1, - integer *n2, doublereal *tl, integer *ldtl, doublereal *tr, - integer *ldtr, doublereal *b, integer *ldb, - doublereal *scale, doublereal *x, integer *ldx, - doublereal *xnorm, integer *info); -extern int dlasyf_(char *uplo, integer *n, integer *nb, integer *kb, - doublereal *a, integer *lda, integer *ipiv, doublereal *w, - integer *ldw, integer *info, ftnlen uplo_len); -extern int dlatbs_(char *uplo, char *trans, char *diag, char *normin, - integer *n, integer *kd, doublereal *ab, integer *ldab, - doublereal *x, doublereal *scale, doublereal *cnorm, - integer *info, ftnlen uplo_len, ftnlen trans_len, - ftnlen diag_len, ftnlen normin_len); -extern int dlatdf_(integer *ijob, integer *n, doublereal *z__, integer *ldz, - doublereal *rhs, doublereal *rdsum, doublereal *rdscal, - integer *ipiv, integer *jpiv); -extern int dlatps_(char *uplo, char *trans, char *diag, char *normin, - integer *n, doublereal *ap, doublereal *x, doublereal *scale, - doublereal *cnorm, integer *info, ftnlen uplo_len, - ftnlen trans_len, ftnlen diag_len, ftnlen normin_len); -extern int dlatrd_(char *uplo, integer *n, integer *nb, doublereal *a, - integer *lda, doublereal *e, doublereal *tau, doublereal *w, - integer *ldw, ftnlen uplo_len); -extern int dlatrs_(char *uplo, char *trans, char *diag, char *normin, - integer *n, doublereal *a, integer *lda, doublereal *x, - doublereal *scale, doublereal *cnorm, integer *info, - ftnlen uplo_len, ftnlen trans_len, ftnlen diag_len, - ftnlen normin_len); -extern int dlatrz_(integer *m, integer *n, integer *l, doublereal *a, - integer *lda, doublereal *tau, doublereal *work); -extern int dlatzm_(char *side, integer *m, integer *n, doublereal *v, - integer *incv, doublereal *tau, doublereal *c1, - doublereal *c2, integer *ldc, doublereal *work, - ftnlen side_len); -extern int dlauu2_(char *uplo, integer *n, doublereal *a, integer *lda, - integer *info, ftnlen uplo_len); -extern int dlauum_(char *uplo, integer *n, doublereal *a, integer *lda, - integer *info, ftnlen uplo_len); -extern int dopgtr_(char *uplo, integer *n, doublereal *ap, doublereal *tau, - doublereal *q, integer *ldq, doublereal *work, integer *info, - ftnlen uplo_len); -extern int dopmtr_(char *side, char *uplo, char *trans, integer *m, integer *n, - doublereal *ap, doublereal *tau, doublereal *c__, - integer *ldc, doublereal *work, integer *info, - ftnlen side_len, ftnlen uplo_len, ftnlen trans_len); -extern int dorg2l_(integer *m, integer *n, integer *k, doublereal *a, - integer *lda, doublereal *tau, doublereal *work, - integer *info); -extern int dorg2r_(integer *m, integer *n, integer *k, doublereal *a, - integer *lda, doublereal *tau, doublereal *work, - integer *info); -extern int dorgbr_(char *vect, integer *m, integer *n, integer *k, - doublereal *a, integer *lda, doublereal *tau, - doublereal *work, integer *lwork, integer *info, - ftnlen vect_len); -extern int dorghr_(integer *n, integer *ilo, integer *ihi, doublereal *a, - integer *lda, doublereal *tau, doublereal *work, - integer *lwork, integer *info); -extern int dorgl2_(integer *m, integer *n, integer *k, doublereal *a, - integer *lda, doublereal *tau, doublereal *work, - integer *info); -extern int dorglq_(integer *m, integer *n, integer *k, doublereal *a, - integer *lda, doublereal *tau, doublereal *work, - integer *lwork, integer *info); -extern int dorgql_(integer *m, integer *n, integer *k, doublereal *a, - integer *lda, doublereal *tau, doublereal *work, - integer *lwork, integer *info); -extern int dorgqr_(integer *m, integer *n, integer *k, doublereal *a, - integer *lda, doublereal *tau, doublereal *work, - integer *lwork, integer *info); -extern int dorgr2_(integer *m, integer *n, integer *k, doublereal *a, - integer *lda, doublereal *tau, doublereal *work, - integer *info); -extern int dorgrq_(integer *m, integer *n, integer *k, doublereal *a, - integer *lda, doublereal *tau, doublereal *work, - integer *lwork, integer *info); -extern int dorgtr_(char *uplo, integer *n, doublereal *a, integer *lda, - doublereal *tau, doublereal *work, integer *lwork, - integer *info, ftnlen uplo_len); -extern int dorm2l_(char *side, char *trans, integer *m, integer *n, integer *k, - doublereal *a, integer *lda, doublereal *tau, - doublereal *c__, integer *ldc, doublereal *work, - integer *info, ftnlen side_len, ftnlen trans_len); -extern int dorm2r_(char *side, char *trans, integer *m, integer *n, integer *k, - doublereal *a, integer *lda, doublereal *tau, - doublereal *c__, integer *ldc, doublereal *work, - integer *info, ftnlen side_len, ftnlen trans_len); -extern int dormbr_(char *vect, char *side, char *trans, integer *m, integer *n, - integer *k, doublereal *a, integer *lda, doublereal *tau, - doublereal *c__, integer *ldc, doublereal *work, - integer *lwork, integer *info, ftnlen vect_len, - ftnlen side_len, ftnlen trans_len); -extern int dormhr_(char *side, char *trans, integer *m, integer *n, - integer *ilo, integer *ihi, doublereal *a, integer *lda, - doublereal *tau, doublereal *c__, integer *ldc, - doublereal *work, integer *lwork, integer *info, - ftnlen side_len, ftnlen trans_len); -extern int dorml2_(char *side, char *trans, integer *m, integer *n, integer *k, - doublereal *a, integer *lda, doublereal *tau, - doublereal *c__, integer *ldc, doublereal *work, - integer *info, ftnlen side_len, ftnlen trans_len); -extern int dormlq_(char *side, char *trans, integer *m, integer *n, integer *k, - doublereal *a, integer *lda, doublereal *tau, - doublereal *c__, integer *ldc, doublereal *work, - integer *lwork, integer *info, ftnlen side_len, - ftnlen trans_len); -extern int dormql_(char *side, char *trans, integer *m, integer *n, integer *k, - doublereal *a, integer *lda, doublereal *tau, - doublereal *c__, integer *ldc, doublereal *work, - integer *lwork, integer *info, ftnlen side_len, - ftnlen trans_len); -extern int dormqr_(char *side, char *trans, integer *m, integer *n, integer *k, - doublereal *a, integer *lda, doublereal *tau, - doublereal *c__, integer *ldc, doublereal *work, - integer *lwork, integer *info, ftnlen side_len, - ftnlen trans_len); -extern int dormr2_(char *side, char *trans, integer *m, integer *n, integer *k, - doublereal *a, integer *lda, doublereal *tau, - doublereal *c__, integer *ldc, doublereal *work, - integer *info, ftnlen side_len, ftnlen trans_len); -extern int dormr3_(char *side, char *trans, integer *m, integer *n, integer *k, - integer *l, doublereal *a, integer *lda, doublereal *tau, - doublereal *c__, integer *ldc, doublereal *work, - integer *info, ftnlen side_len, ftnlen trans_len); -extern int dormrq_(char *side, char *trans, integer *m, integer *n, integer *k, - doublereal *a, integer *lda, doublereal *tau, - doublereal *c__, integer *ldc, doublereal *work, - integer *lwork, integer *info, ftnlen side_len, - ftnlen trans_len); -extern int dormrz_(char *side, char *trans, integer *m, integer *n, integer *k, - integer *l, doublereal *a, integer *lda, doublereal *tau, - doublereal *c__, integer *ldc, doublereal *work, - integer *lwork, integer *info, ftnlen side_len, - ftnlen trans_len); -extern int dormtr_(char *side, char *uplo, char *trans, integer *m, integer *n, - doublereal *a, integer *lda, doublereal *tau, - doublereal *c__, integer *ldc, doublereal *work, - integer *lwork, integer *info, ftnlen side_len, - ftnlen uplo_len, ftnlen trans_len); -extern int dpbcon_(char *uplo, integer *n, integer *kd, doublereal *ab, - integer *ldab, doublereal *anorm, doublereal *rcond, - doublereal *work, integer *iwork, integer *info, - ftnlen uplo_len); -extern int dpbequ_(char *uplo, integer *n, integer *kd, doublereal *ab, - integer *ldab, doublereal *s, doublereal *scond, - doublereal *amax, integer *info, ftnlen uplo_len); -extern int dpbrfs_(char *uplo, integer *n, integer *kd, integer *nrhs, - doublereal *ab, integer *ldab, doublereal *afb, - integer *ldafb, doublereal *b, integer *ldb, doublereal *x, - integer *ldx, doublereal *ferr, doublereal *berr, - doublereal *work, integer *iwork, integer *info, - ftnlen uplo_len); -extern int dpbstf_(char *uplo, integer *n, integer *kd, doublereal *ab, - integer *ldab, integer *info, ftnlen uplo_len); -extern int dpbsv_(char *uplo, integer *n, integer *kd, integer *nrhs, - doublereal *ab, integer *ldab, doublereal *b, integer *ldb, - integer *info, ftnlen uplo_len); -extern int dpbsvx_(char *fact, char *uplo, integer *n, integer *kd, - integer *nrhs, doublereal *ab, integer *ldab, - doublereal *afb, integer *ldafb, char *equed, doublereal *s, - doublereal *b, integer *ldb, doublereal *x, integer *ldx, - doublereal *rcond, doublereal *ferr, doublereal *berr, - doublereal *work, integer *iwork, integer *info, - ftnlen fact_len, ftnlen uplo_len, ftnlen equed_len); -extern int dpbtf2_(char *uplo, integer *n, integer *kd, doublereal *ab, - integer *ldab, integer *info, ftnlen uplo_len); -extern int dpbtrf_(char *uplo, integer *n, integer *kd, doublereal *ab, - integer *ldab, integer *info, ftnlen uplo_len); -extern int dpbtrs_(char *uplo, integer *n, integer *kd, integer *nrhs, - doublereal *ab, integer *ldab, doublereal *b, integer *ldb, - integer *info, ftnlen uplo_len); -extern int dpocon_(char *uplo, integer *n, doublereal *a, integer *lda, - doublereal *anorm, doublereal *rcond, doublereal *work, - integer *iwork, integer *info, ftnlen uplo_len); -extern int dpoequ_(integer *n, doublereal *a, integer *lda, doublereal *s, - doublereal *scond, doublereal *amax, integer *info); -extern int dporfs_(char *uplo, integer *n, integer *nrhs, doublereal *a, - integer *lda, doublereal *af, integer *ldaf, doublereal *b, - integer *ldb, doublereal *x, integer *ldx, doublereal *ferr, - doublereal *berr, doublereal *work, integer *iwork, - integer *info, ftnlen uplo_len); -extern int dposv_(char *uplo, integer *n, integer *nrhs, doublereal *a, - integer *lda, doublereal *b, integer *ldb, integer *info, - ftnlen uplo_len); -extern int dposvx_(char *fact, char *uplo, integer *n, integer *nrhs, - doublereal *a, integer *lda, doublereal *af, integer *ldaf, - char *equed, doublereal *s, doublereal *b, integer *ldb, - doublereal *x, integer *ldx, doublereal *rcond, - doublereal *ferr, doublereal *berr, doublereal *work, - integer *iwork, integer *info, ftnlen fact_len, - ftnlen uplo_len, ftnlen equed_len); -extern int dpotf2_(char *uplo, integer *n, doublereal *a, integer *lda, - integer *info, ftnlen uplo_len); -extern int dpotrf_(char *uplo, integer *n, doublereal *a, integer *lda, - integer *info, ftnlen uplo_len); -extern int dpotri_(char *uplo, integer *n, doublereal *a, integer *lda, - integer *info, ftnlen uplo_len); -extern int dpotrs_(char *uplo, integer *n, integer *nrhs, doublereal *a, - integer *lda, doublereal *b, integer *ldb, integer *info, - ftnlen uplo_len); -extern int dppcon_(char *uplo, integer *n, doublereal *ap, doublereal *anorm, - doublereal *rcond, doublereal *work, integer *iwork, - integer *info, ftnlen uplo_len); -extern int dppequ_(char *uplo, integer *n, doublereal *ap, doublereal *s, - doublereal *scond, doublereal *amax, integer *info, - ftnlen uplo_len); -extern int dpprfs_(char *uplo, integer *n, integer *nrhs, doublereal *ap, - doublereal *afp, doublereal *b, integer *ldb, doublereal *x, - integer *ldx, doublereal *ferr, doublereal *berr, - doublereal *work, integer *iwork, integer *info, - ftnlen uplo_len); -extern int dppsv_(char *uplo, integer *n, integer *nrhs, doublereal *ap, - doublereal *b, integer *ldb, integer *info, ftnlen uplo_len); -extern int dppsvx_(char *fact, char *uplo, integer *n, integer *nrhs, - doublereal *ap, doublereal *afp, char *equed, doublereal *s, - doublereal *b, integer *ldb, doublereal *x, integer *ldx, - doublereal *rcond, doublereal *ferr, doublereal *berr, - doublereal *work, integer *iwork, integer *info, - ftnlen fact_len, ftnlen uplo_len, ftnlen equed_len); -extern int dpptrf_(char *uplo, integer *n, doublereal *ap, integer *info, - ftnlen uplo_len); -extern int dpptri_(char *uplo, integer *n, doublereal *ap, integer *info, - ftnlen uplo_len); -extern int dpptrs_(char *uplo, integer *n, integer *nrhs, doublereal *ap, - doublereal *b, integer *ldb, integer *info, ftnlen uplo_len); -extern int dptcon_(integer *n, doublereal *d__, doublereal *e, - doublereal *anorm, doublereal *rcond, doublereal *work, - integer *info); -extern int dpteqr_(char *compz, integer *n, doublereal *d__, doublereal *e, - doublereal *z__, integer *ldz, doublereal *work, - integer *info, ftnlen compz_len); -extern int dptrfs_(integer *n, integer *nrhs, doublereal *d__, doublereal *e, - doublereal *df, doublereal *ef, doublereal *b, integer *ldb, - doublereal *x, integer *ldx, doublereal *ferr, - doublereal *berr, doublereal *work, integer *info); -extern int dptsv_(integer *n, integer *nrhs, doublereal *d__, doublereal *e, - doublereal *b, integer *ldb, integer *info); -extern int dptsvx_(char *fact, integer *n, integer *nrhs, doublereal *d__, - doublereal *e, doublereal *df, doublereal *ef, doublereal *b, - integer *ldb, doublereal *x, integer *ldx, doublereal *rcond, - doublereal *ferr, doublereal *berr, doublereal *work, - integer *info, ftnlen fact_len); -extern int dpttrf_(integer *n, doublereal *d__, doublereal *e, integer *info); -extern int dpttrs_(integer *n, integer *nrhs, doublereal *d__, doublereal *e, - doublereal *b, integer *ldb, integer *info); -extern int dptts2_(integer *n, integer *nrhs, doublereal *d__, doublereal *e, - doublereal *b, integer *ldb); -extern int drscl_(integer *n, doublereal *sa, doublereal *sx, integer *incx); -extern int dsbev_(char *jobz, char *uplo, integer *n, integer *kd, - doublereal *ab, integer *ldab, doublereal *w, doublereal *z__, - integer *ldz, doublereal *work, integer *info, - ftnlen jobz_len, ftnlen uplo_len); -extern int dsbevd_(char *jobz, char *uplo, integer *n, integer *kd, - doublereal *ab, integer *ldab, doublereal *w, - doublereal *z__, integer *ldz, doublereal *work, - integer *lwork, integer *iwork, integer *liwork, - integer *info, ftnlen jobz_len, ftnlen uplo_len); -extern int dsbevx_(char *jobz, char *range, char *uplo, integer *n, integer *kd, - doublereal *ab, integer *ldab, doublereal *q, integer *ldq, - doublereal *vl, doublereal *vu, integer *il, integer *iu, - doublereal *abstol, integer *m, doublereal *w, - doublereal *z__, integer *ldz, doublereal *work, - integer *iwork, integer *ifail, integer *info, - ftnlen jobz_len, ftnlen range_len, ftnlen uplo_len); -extern int dsbgst_(char *vect, char *uplo, integer *n, integer *ka, integer *kb, - doublereal *ab, integer *ldab, doublereal *bb, integer *ldbb, - doublereal *x, integer *ldx, doublereal *work, integer *info, - ftnlen vect_len, ftnlen uplo_len); -extern int dsbgv_(char *jobz, char *uplo, integer *n, integer *ka, integer *kb, - doublereal *ab, integer *ldab, doublereal *bb, integer *ldbb, - doublereal *w, doublereal *z__, integer *ldz, - doublereal *work, integer *info, ftnlen jobz_len, - ftnlen uplo_len); -extern int dsbgvd_(char *jobz, char *uplo, integer *n, integer *ka, integer *kb, - doublereal *ab, integer *ldab, doublereal *bb, integer *ldbb, - doublereal *w, doublereal *z__, integer *ldz, - doublereal *work, integer *lwork, integer *iwork, - integer *liwork, integer *info, ftnlen jobz_len, - ftnlen uplo_len); -extern int dsbgvx_(char *jobz, char *range, char *uplo, integer *n, integer *ka, - integer *kb, doublereal *ab, integer *ldab, doublereal *bb, - integer *ldbb, doublereal *q, integer *ldq, doublereal *vl, - doublereal *vu, integer *il, integer *iu, doublereal *abstol, - integer *m, doublereal *w, doublereal *z__, integer *ldz, - doublereal *work, integer *iwork, integer *ifail, - integer *info, ftnlen jobz_len, ftnlen range_len, - ftnlen uplo_len); -extern int dsbtrd_(char *vect, char *uplo, integer *n, integer *kd, - doublereal *ab, integer *ldab, doublereal *d__, - doublereal *e, doublereal *q, integer *ldq, doublereal *work, - integer *info, ftnlen vect_len, ftnlen uplo_len); -extern doublereal dsecnd_(void); -extern int dspcon_(char *uplo, integer *n, doublereal *ap, integer *ipiv, - doublereal *anorm, doublereal *rcond, doublereal *work, - integer *iwork, integer *info, ftnlen uplo_len); -extern int dspev_(char *jobz, char *uplo, integer *n, doublereal *ap, - doublereal *w, doublereal *z__, integer *ldz, - doublereal *work, integer *info, ftnlen jobz_len, - ftnlen uplo_len); -extern int dspevd_(char *jobz, char *uplo, integer *n, doublereal *ap, - doublereal *w, doublereal *z__, integer *ldz, - doublereal *work, integer *lwork, integer *iwork, - integer *liwork, integer *info, ftnlen jobz_len, - ftnlen uplo_len); -extern int dspevx_(char *jobz, char *range, char *uplo, integer *n, - doublereal *ap, doublereal *vl, doublereal *vu, integer *il, - integer *iu, doublereal *abstol, integer *m, doublereal *w, - doublereal *z__, integer *ldz, doublereal *work, - integer *iwork, integer *ifail, integer *info, - ftnlen jobz_len, ftnlen range_len, ftnlen uplo_len); -extern int dspgst_(integer *itype, char *uplo, integer *n, doublereal *ap, - doublereal *bp, integer *info, ftnlen uplo_len); -extern int dspgv_(integer *itype, char *jobz, char *uplo, integer *n, - doublereal *ap, doublereal *bp, doublereal *w, - doublereal *z__, integer *ldz, doublereal *work, - integer *info, ftnlen jobz_len, ftnlen uplo_len); -extern int dspgvd_(integer *itype, char *jobz, char *uplo, integer *n, - doublereal *ap, doublereal *bp, doublereal *w, - doublereal *z__, integer *ldz, doublereal *work, - integer *lwork, integer *iwork, integer *liwork, - integer *info, ftnlen jobz_len, ftnlen uplo_len); -extern int dspgvx_(integer *itype, char *jobz, char *range, char *uplo, - integer *n, doublereal *ap, doublereal *bp, doublereal *vl, - doublereal *vu, integer *il, integer *iu, doublereal *abstol, - integer *m, doublereal *w, doublereal *z__, integer *ldz, - doublereal *work, integer *iwork, integer *ifail, - integer *info, ftnlen jobz_len, ftnlen range_len, - ftnlen uplo_len); -extern int dsprfs_(char *uplo, integer *n, integer *nrhs, doublereal *ap, - doublereal *afp, integer *ipiv, doublereal *b, integer *ldb, - doublereal *x, integer *ldx, doublereal *ferr, - doublereal *berr, doublereal *work, integer *iwork, - integer *info, ftnlen uplo_len); -extern int dspsv_(char *uplo, integer *n, integer *nrhs, doublereal *ap, - integer *ipiv, doublereal *b, integer *ldb, integer *info, - ftnlen uplo_len); -extern int dspsvx_(char *fact, char *uplo, integer *n, integer *nrhs, - doublereal *ap, doublereal *afp, integer *ipiv, - doublereal *b, integer *ldb, doublereal *x, integer *ldx, - doublereal *rcond, doublereal *ferr, doublereal *berr, - doublereal *work, integer *iwork, integer *info, - ftnlen fact_len, ftnlen uplo_len); -extern int dsptrd_(char *uplo, integer *n, doublereal *ap, doublereal *d__, - doublereal *e, doublereal *tau, integer *info, - ftnlen uplo_len); -extern int dsptrf_(char *uplo, integer *n, doublereal *ap, integer *ipiv, - integer *info, ftnlen uplo_len); -extern int dsptri_(char *uplo, integer *n, doublereal *ap, integer *ipiv, - doublereal *work, integer *info, ftnlen uplo_len); -extern int dsptrs_(char *uplo, integer *n, integer *nrhs, doublereal *ap, - integer *ipiv, doublereal *b, integer *ldb, integer *info, - ftnlen uplo_len); -extern int dstebz_(char *range, char *order, integer *n, doublereal *vl, - doublereal *vu, integer *il, integer *iu, doublereal *abstol, - doublereal *d__, doublereal *e, integer *m, integer *nsplit, - doublereal *w, integer *iblock, integer *isplit, - doublereal *work, integer *iwork, integer *info, - ftnlen range_len, ftnlen order_len); -extern int dstedc_(char *compz, integer *n, doublereal *d__, doublereal *e, - doublereal *z__, integer *ldz, doublereal *work, - integer *lwork, integer *iwork, integer *liwork, - integer *info, ftnlen compz_len); -extern int dstegr_(char *jobz, char *range, integer *n, doublereal *d__, - doublereal *e, doublereal *vl, doublereal *vu, integer *il, - integer *iu, doublereal *abstol, integer *m, doublereal *w, - doublereal *z__, integer *ldz, integer *isuppz, - doublereal *work, integer *lwork, integer *iwork, - integer *liwork, integer *info, ftnlen jobz_len, - ftnlen range_len); -extern int dstein_(integer *n, doublereal *d__, doublereal *e, integer *m, - doublereal *w, integer *iblock, integer *isplit, - doublereal *z__, integer *ldz, doublereal *work, - integer *iwork, integer *ifail, integer *info); -extern int dsteqr_(char *compz, integer *n, doublereal *d__, doublereal *e, - doublereal *z__, integer *ldz, doublereal *work, - integer *info, ftnlen compz_len); -extern int dsterf_(integer *n, doublereal *d__, doublereal *e, integer *info); -extern int dstev_(char *jobz, integer *n, doublereal *d__, doublereal *e, - doublereal *z__, integer *ldz, doublereal *work, - integer *info, ftnlen jobz_len); -extern int dstevd_(char *jobz, integer *n, doublereal *d__, doublereal *e, - doublereal *z__, integer *ldz, doublereal *work, - integer *lwork, integer *iwork, integer *liwork, - integer *info, ftnlen jobz_len); -extern int dstevr_(char *jobz, char *range, integer *n, doublereal *d__, - doublereal *e, doublereal *vl, doublereal *vu, integer *il, - integer *iu, doublereal *abstol, integer *m, doublereal *w, - doublereal *z__, integer *ldz, integer *isuppz, - doublereal *work, integer *lwork, integer *iwork, - integer *liwork, integer *info, ftnlen jobz_len, - ftnlen range_len); -extern int dstevx_(char *jobz, char *range, integer *n, doublereal *d__, - doublereal *e, doublereal *vl, doublereal *vu, integer *il, - integer *iu, doublereal *abstol, integer *m, doublereal *w, - doublereal *z__, integer *ldz, doublereal *work, - integer *iwork, integer *ifail, integer *info, - ftnlen jobz_len, ftnlen range_len); -extern int dsycon_(char *uplo, integer *n, doublereal *a, integer *lda, - integer *ipiv, doublereal *anorm, doublereal *rcond, - doublereal *work, integer *iwork, integer *info, - ftnlen uplo_len); -extern int dsyev_(char *jobz, char *uplo, integer *n, doublereal *a, - integer *lda, doublereal *w, doublereal *work, integer *lwork, - integer *info, ftnlen jobz_len, ftnlen uplo_len); -extern int dsyevd_(char *jobz, char *uplo, integer *n, doublereal *a, - integer *lda, doublereal *w, doublereal *work, - integer *lwork, integer *iwork, integer *liwork, - integer *info, ftnlen jobz_len, ftnlen uplo_len); -extern int dsyevr_(char *jobz, char *range, char *uplo, integer *n, - doublereal *a, integer *lda, doublereal *vl, doublereal *vu, - integer *il, integer *iu, doublereal *abstol, integer *m, - doublereal *w, doublereal *z__, integer *ldz, - integer *isuppz, doublereal *work, integer *lwork, - integer *iwork, integer *liwork, integer *info, - ftnlen jobz_len, ftnlen range_len, ftnlen uplo_len); -extern int dsyevx_(char *jobz, char *range, char *uplo, integer *n, - doublereal *a, integer *lda, doublereal *vl, doublereal *vu, - integer *il, integer *iu, doublereal *abstol, integer *m, - doublereal *w, doublereal *z__, integer *ldz, - doublereal *work, integer *lwork, integer *iwork, - integer *ifail, integer *info, ftnlen jobz_len, - ftnlen range_len, ftnlen uplo_len); -extern int dsygs2_(integer *itype, char *uplo, integer *n, doublereal *a, - integer *lda, doublereal *b, integer *ldb, integer *info, - ftnlen uplo_len); -extern int dsygst_(integer *itype, char *uplo, integer *n, doublereal *a, - integer *lda, doublereal *b, integer *ldb, integer *info, - ftnlen uplo_len); -extern int dsygv_(integer *itype, char *jobz, char *uplo, integer *n, - doublereal *a, integer *lda, doublereal *b, integer *ldb, - doublereal *w, doublereal *work, integer *lwork, - integer *info, ftnlen jobz_len, ftnlen uplo_len); -extern int dsygvd_(integer *itype, char *jobz, char *uplo, integer *n, - doublereal *a, integer *lda, doublereal *b, integer *ldb, - doublereal *w, doublereal *work, integer *lwork, - integer *iwork, integer *liwork, integer *info, - ftnlen jobz_len, ftnlen uplo_len); -extern int dsygvx_(integer *itype, char *jobz, char *range, char *uplo, - integer *n, doublereal *a, integer *lda, doublereal *b, - integer *ldb, doublereal *vl, doublereal *vu, integer *il, - integer *iu, doublereal *abstol, integer *m, doublereal *w, - doublereal *z__, integer *ldz, doublereal *work, - integer *lwork, integer *iwork, integer *ifail, - integer *info, ftnlen jobz_len, ftnlen range_len, - ftnlen uplo_len); -extern int dsyrfs_(char *uplo, integer *n, integer *nrhs, doublereal *a, - integer *lda, doublereal *af, integer *ldaf, integer *ipiv, - doublereal *b, integer *ldb, doublereal *x, integer *ldx, - doublereal *ferr, doublereal *berr, doublereal *work, - integer *iwork, integer *info, ftnlen uplo_len); -extern int dsysv_(char *uplo, integer *n, integer *nrhs, doublereal *a, - integer *lda, integer *ipiv, doublereal *b, integer *ldb, - doublereal *work, integer *lwork, integer *info, - ftnlen uplo_len); -extern int dsysvx_(char *fact, char *uplo, integer *n, integer *nrhs, - doublereal *a, integer *lda, doublereal *af, integer *ldaf, - integer *ipiv, doublereal *b, integer *ldb, doublereal *x, - integer *ldx, doublereal *rcond, doublereal *ferr, - doublereal *berr, doublereal *work, integer *lwork, - integer *iwork, integer *info, ftnlen fact_len, - ftnlen uplo_len); -extern int dsytd2_(char *uplo, integer *n, doublereal *a, integer *lda, - doublereal *d__, doublereal *e, doublereal *tau, - integer *info, ftnlen uplo_len); -extern int dsytf2_(char *uplo, integer *n, doublereal *a, integer *lda, - integer *ipiv, integer *info, ftnlen uplo_len); -extern int dsytrd_(char *uplo, integer *n, doublereal *a, integer *lda, - doublereal *d__, doublereal *e, doublereal *tau, - doublereal *work, integer *lwork, integer *info, - ftnlen uplo_len); -extern int dsytrf_(char *uplo, integer *n, doublereal *a, integer *lda, - integer *ipiv, doublereal *work, integer *lwork, - integer *info, ftnlen uplo_len); -extern int dsytri_(char *uplo, integer *n, doublereal *a, integer *lda, - integer *ipiv, doublereal *work, integer *info, - ftnlen uplo_len); -extern int dsytrs_(char *uplo, integer *n, integer *nrhs, doublereal *a, - integer *lda, integer *ipiv, doublereal *b, integer *ldb, - integer *info, ftnlen uplo_len); -extern int dtbcon_(char *norm, char *uplo, char *diag, integer *n, integer *kd, - doublereal *ab, integer *ldab, doublereal *rcond, - doublereal *work, integer *iwork, integer *info, - ftnlen norm_len, ftnlen uplo_len, ftnlen diag_len); -extern int dtbrfs_(char *uplo, char *trans, char *diag, integer *n, integer *kd, - integer *nrhs, doublereal *ab, integer *ldab, doublereal *b, - integer *ldb, doublereal *x, integer *ldx, doublereal *ferr, - doublereal *berr, doublereal *work, integer *iwork, - integer *info, ftnlen uplo_len, ftnlen trans_len, - ftnlen diag_len); -extern int dtbtrs_(char *uplo, char *trans, char *diag, integer *n, integer *kd, - integer *nrhs, doublereal *ab, integer *ldab, doublereal *b, - integer *ldb, integer *info, ftnlen uplo_len, - ftnlen trans_len, ftnlen diag_len); -extern int dtgevc_(char *side, char *howmny, logical *select, integer *n, - doublereal *a, integer *lda, doublereal *b, integer *ldb, - doublereal *vl, integer *ldvl, doublereal *vr, integer *ldvr, - integer *mm, integer *m, doublereal *work, integer *info, - ftnlen side_len, ftnlen howmny_len); -extern int dtgex2_(logical *wantq, logical *wantz, integer *n, doublereal *a, - integer *lda, doublereal *b, integer *ldb, doublereal *q, - integer *ldq, doublereal *z__, integer *ldz, integer *j1, - integer *n1, integer *n2, doublereal *work, integer *lwork, - integer *info); -extern int dtgexc_(logical *wantq, logical *wantz, integer *n, doublereal *a, - integer *lda, doublereal *b, integer *ldb, doublereal *q, - integer *ldq, doublereal *z__, integer *ldz, integer *ifst, - integer *ilst, doublereal *work, integer *lwork, - integer *info); -extern int dtgsen_(integer *ijob, logical *wantq, logical *wantz, - logical *select, integer *n, doublereal *a, integer *lda, - doublereal *b, integer *ldb, doublereal *alphar, - doublereal *alphai, doublereal *beta, doublereal *q, - integer *ldq, doublereal *z__, integer *ldz, integer *m, - doublereal *pl, doublereal *pr, doublereal *dif, - doublereal *work, integer *lwork, integer *iwork, - integer *liwork, integer *info); -extern int dtgsja_(char *jobu, char *jobv, char *jobq, integer *m, integer *p, - integer *n, integer *k, integer *l, doublereal *a, - integer *lda, doublereal *b, integer *ldb, doublereal *tola, - doublereal *tolb, doublereal *alpha, doublereal *beta, - doublereal *u, integer *ldu, doublereal *v, integer *ldv, - doublereal *q, integer *ldq, doublereal *work, - integer *ncycle, integer *info, ftnlen jobu_len, - ftnlen jobv_len, ftnlen jobq_len); -extern int dtgsna_(char *job, char *howmny, logical *select, integer *n, - doublereal *a, integer *lda, doublereal *b, integer *ldb, - doublereal *vl, integer *ldvl, doublereal *vr, integer *ldvr, - doublereal *s, doublereal *dif, integer *mm, integer *m, - doublereal *work, integer *lwork, integer *iwork, - integer *info, ftnlen job_len, ftnlen howmny_len); -extern int dtgsy2_(char *trans, integer *ijob, integer *m, integer *n, - doublereal *a, integer *lda, doublereal *b, integer *ldb, - doublereal *c__, integer *ldc, doublereal *d__, integer *ldd, - doublereal *e, integer *lde, doublereal *f, integer *ldf, - doublereal *scale, doublereal *rdsum, doublereal *rdscal, - integer *iwork, integer *pq, integer *info, - ftnlen trans_len); -extern int dtgsyl_(char *trans, integer *ijob, integer *m, integer *n, - doublereal *a, integer *lda, doublereal *b, integer *ldb, - doublereal *c__, integer *ldc, doublereal *d__, integer *ldd, - doublereal *e, integer *lde, doublereal *f, integer *ldf, - doublereal *scale, doublereal *dif, doublereal *work, - integer *lwork, integer *iwork, integer *info, - ftnlen trans_len); -extern int dtpcon_(char *norm, char *uplo, char *diag, integer *n, - doublereal *ap, doublereal *rcond, doublereal *work, - integer *iwork, integer *info, ftnlen norm_len, - ftnlen uplo_len, ftnlen diag_len); -extern int dtprfs_(char *uplo, char *trans, char *diag, integer *n, - integer *nrhs, doublereal *ap, doublereal *b, integer *ldb, - doublereal *x, integer *ldx, doublereal *ferr, - doublereal *berr, doublereal *work, integer *iwork, - integer *info, ftnlen uplo_len, ftnlen trans_len, - ftnlen diag_len); -extern int dtptri_(char *uplo, char *diag, integer *n, doublereal *ap, - integer *info, ftnlen uplo_len, ftnlen diag_len); -extern int dtptrs_(char *uplo, char *trans, char *diag, integer *n, - integer *nrhs, doublereal *ap, doublereal *b, integer *ldb, - integer *info, ftnlen uplo_len, ftnlen trans_len, - ftnlen diag_len); -extern int dtrcon_(char *norm, char *uplo, char *diag, integer *n, - doublereal *a, integer *lda, doublereal *rcond, - doublereal *work, integer *iwork, integer *info, - ftnlen norm_len, ftnlen uplo_len, ftnlen diag_len); -extern int dtrevc_(char *side, char *howmny, logical *select, integer *n, - doublereal *t, integer *ldt, doublereal *vl, integer *ldvl, - doublereal *vr, integer *ldvr, integer *mm, integer *m, - doublereal *work, integer *info, ftnlen side_len, - ftnlen howmny_len); -extern int dtrexc_(char *compq, integer *n, doublereal *t, integer *ldt, - doublereal *q, integer *ldq, integer *ifst, integer *ilst, - doublereal *work, integer *info, ftnlen compq_len); -extern int dtrrfs_(char *uplo, char *trans, char *diag, integer *n, - integer *nrhs, doublereal *a, integer *lda, doublereal *b, - integer *ldb, doublereal *x, integer *ldx, doublereal *ferr, - doublereal *berr, doublereal *work, integer *iwork, - integer *info, ftnlen uplo_len, ftnlen trans_len, - ftnlen diag_len); -extern int dtrsen_(char *job, char *compq, logical *select, integer *n, - doublereal *t, integer *ldt, doublereal *q, integer *ldq, - doublereal *wr, doublereal *wi, integer *m, doublereal *s, - doublereal *sep, doublereal *work, integer *lwork, - integer *iwork, integer *liwork, integer *info, - ftnlen job_len, ftnlen compq_len); -extern int dtrsna_(char *job, char *howmny, logical *select, integer *n, - doublereal *t, integer *ldt, doublereal *vl, integer *ldvl, - doublereal *vr, integer *ldvr, doublereal *s, - doublereal *sep, integer *mm, integer *m, doublereal *work, - integer *ldwork, integer *iwork, integer *info, - ftnlen job_len, ftnlen howmny_len); -extern int dtrsyl_(char *trana, char *tranb, integer *isgn, integer *m, - integer *n, doublereal *a, integer *lda, doublereal *b, - integer *ldb, doublereal *c__, integer *ldc, - doublereal *scale, integer *info, ftnlen trana_len, - ftnlen tranb_len); -extern int dtrti2_(char *uplo, char *diag, integer *n, doublereal *a, - integer *lda, integer *info, ftnlen uplo_len, - ftnlen diag_len); -extern int dtrtri_(char *uplo, char *diag, integer *n, doublereal *a, - integer *lda, integer *info, ftnlen uplo_len, - ftnlen diag_len); -extern int dtrtrs_(char *uplo, char *trans, char *diag, integer *n, - integer *nrhs, doublereal *a, integer *lda, doublereal *b, - integer *ldb, integer *info, ftnlen uplo_len, - ftnlen trans_len, ftnlen diag_len); -extern int dtzrqf_(integer *m, integer *n, doublereal *a, integer *lda, - doublereal *tau, integer *info); -extern int dtzrzf_(integer *m, integer *n, doublereal *a, integer *lda, - doublereal *tau, doublereal *work, integer *lwork, - integer *info); -extern doublereal dzsum1_(integer *n, doublecomplex *cx, integer *incx); -extern integer icmax1_(integer *n, complex *cx, integer *incx); -extern integer ieeeck_(integer *ispec, real *zero, real *one); -extern integer ilaenv_(integer *ispec, char *name__, char *opts, integer *n1, - integer *n2, integer *n3, integer *n4, ftnlen name_len, - ftnlen opts_len); -extern integer izmax1_(integer *n, doublecomplex *cx, integer *incx); -extern logical lsame_(char *ca, char *cb, ftnlen ca_len, ftnlen cb_len); -extern logical lsamen_(integer *n, char *ca, char *cb, ftnlen ca_len, - ftnlen cb_len); -extern int sbdsdc_(char *uplo, char *compq, integer *n, real *d__, real *e, - real *u, integer *ldu, real *vt, integer *ldvt, real *q, - integer *iq, real *work, integer *iwork, integer *info, - ftnlen uplo_len, ftnlen compq_len); -extern int sbdsqr_(char *uplo, integer *n, integer *ncvt, integer *nru, - integer *ncc, real *d__, real *e, real *vt, integer *ldvt, - real *u, integer *ldu, real *c__, integer *ldc, real *work, - integer *info, ftnlen uplo_len); -extern E_f scsum1_(integer *n, complex *cx, integer *incx); -extern int sdisna_(char *job, integer *m, integer *n, real *d__, real *sep, - integer *info, ftnlen job_len); -extern E_f second_(void); -extern int sgbbrd_(char *vect, integer *m, integer *n, integer *ncc, - integer *kl, integer *ku, real *ab, integer *ldab, real *d__, - real *e, real *q, integer *ldq, real *pt, integer *ldpt, - real *c__, integer *ldc, real *work, integer *info, - ftnlen vect_len); -extern int sgbcon_(char *norm, integer *n, integer *kl, integer *ku, real *ab, - integer *ldab, integer *ipiv, real *anorm, real *rcond, - real *work, integer *iwork, integer *info, ftnlen norm_len); -extern int sgbequ_(integer *m, integer *n, integer *kl, integer *ku, real *ab, - integer *ldab, real *r__, real *c__, real *rowcnd, - real *colcnd, real *amax, integer *info); -extern int sgbrfs_(char *trans, integer *n, integer *kl, integer *ku, - integer *nrhs, real *ab, integer *ldab, real *afb, - integer *ldafb, integer *ipiv, real *b, integer *ldb, - real *x, integer *ldx, real *ferr, real *berr, real *work, - integer *iwork, integer *info, ftnlen trans_len); -extern int sgbsv_(integer *n, integer *kl, integer *ku, integer *nrhs, real *ab, - integer *ldab, integer *ipiv, real *b, integer *ldb, - integer *info); -extern int sgbsvx_(char *fact, char *trans, integer *n, integer *kl, - integer *ku, integer *nrhs, real *ab, integer *ldab, - real *afb, integer *ldafb, integer *ipiv, char *equed, - real *r__, real *c__, real *b, integer *ldb, real *x, - integer *ldx, real *rcond, real *ferr, real *berr, - real *work, integer *iwork, integer *info, ftnlen fact_len, - ftnlen trans_len, ftnlen equed_len); -extern int sgbtf2_(integer *m, integer *n, integer *kl, integer *ku, real *ab, - integer *ldab, integer *ipiv, integer *info); -extern int sgbtrf_(integer *m, integer *n, integer *kl, integer *ku, real *ab, - integer *ldab, integer *ipiv, integer *info); -extern int sgbtrs_(char *trans, integer *n, integer *kl, integer *ku, - integer *nrhs, real *ab, integer *ldab, integer *ipiv, - real *b, integer *ldb, integer *info, ftnlen trans_len); -extern int sgebak_(char *job, char *side, integer *n, integer *ilo, - integer *ihi, real *scale, integer *m, real *v, integer *ldv, - integer *info, ftnlen job_len, ftnlen side_len); -extern int sgebal_(char *job, integer *n, real *a, integer *lda, integer *ilo, - integer *ihi, real *scale, integer *info, ftnlen job_len); -extern int sgebd2_(integer *m, integer *n, real *a, integer *lda, real *d__, - real *e, real *tauq, real *taup, real *work, integer *info); -extern int sgebrd_(integer *m, integer *n, real *a, integer *lda, real *d__, - real *e, real *tauq, real *taup, real *work, integer *lwork, - integer *info); -extern int sgecon_(char *norm, integer *n, real *a, integer *lda, real *anorm, - real *rcond, real *work, integer *iwork, integer *info, - ftnlen norm_len); -extern int sgeequ_(integer *m, integer *n, real *a, integer *lda, real *r__, - real *c__, real *rowcnd, real *colcnd, real *amax, - integer *info); -extern int sgees_(char *jobvs, char *sort, L_fp select, integer *n, real *a, - integer *lda, integer *sdim, real *wr, real *wi, real *vs, - integer *ldvs, real *work, integer *lwork, logical *bwork, - integer *info, ftnlen jobvs_len, ftnlen sort_len); -extern int sgeesx_(char *jobvs, char *sort, L_fp select, char *sense, - integer *n, real *a, integer *lda, integer *sdim, real *wr, - real *wi, real *vs, integer *ldvs, real *rconde, - real *rcondv, real *work, integer *lwork, integer *iwork, - integer *liwork, logical *bwork, integer *info, - ftnlen jobvs_len, ftnlen sort_len, ftnlen sense_len); -extern int sgeev_(char *jobvl, char *jobvr, integer *n, real *a, integer *lda, - real *wr, real *wi, real *vl, integer *ldvl, real *vr, - integer *ldvr, real *work, integer *lwork, integer *info, - ftnlen jobvl_len, ftnlen jobvr_len); -extern int sgeevx_(char *balanc, char *jobvl, char *jobvr, char *sense, - integer *n, real *a, integer *lda, real *wr, real *wi, - real *vl, integer *ldvl, real *vr, integer *ldvr, - integer *ilo, integer *ihi, real *scale, real *abnrm, - real *rconde, real *rcondv, real *work, integer *lwork, - integer *iwork, integer *info, ftnlen balanc_len, - ftnlen jobvl_len, ftnlen jobvr_len, ftnlen sense_len); -extern int sgegs_(char *jobvsl, char *jobvsr, integer *n, real *a, integer *lda, - real *b, integer *ldb, real *alphar, real *alphai, real *beta, - real *vsl, integer *ldvsl, real *vsr, integer *ldvsr, - real *work, integer *lwork, integer *info, ftnlen jobvsl_len, - ftnlen jobvsr_len); -extern int sgegv_(char *jobvl, char *jobvr, integer *n, real *a, integer *lda, - real *b, integer *ldb, real *alphar, real *alphai, real *beta, - real *vl, integer *ldvl, real *vr, integer *ldvr, real *work, - integer *lwork, integer *info, ftnlen jobvl_len, - ftnlen jobvr_len); -extern int sgehd2_(integer *n, integer *ilo, integer *ihi, real *a, - integer *lda, real *tau, real *work, integer *info); -extern int sgehrd_(integer *n, integer *ilo, integer *ihi, real *a, - integer *lda, real *tau, real *work, integer *lwork, - integer *info); -extern int sgelq2_(integer *m, integer *n, real *a, integer *lda, real *tau, - real *work, integer *info); -extern int sgelqf_(integer *m, integer *n, real *a, integer *lda, real *tau, - real *work, integer *lwork, integer *info); -extern int sgels_(char *trans, integer *m, integer *n, integer *nrhs, real *a, - integer *lda, real *b, integer *ldb, real *work, - integer *lwork, integer *info, ftnlen trans_len); -extern int sgelsd_(integer *m, integer *n, integer *nrhs, real *a, integer *lda, - real *b, integer *ldb, real *s, real *rcond, integer *rank, - real *work, integer *lwork, integer *iwork, integer *info); -extern int sgelss_(integer *m, integer *n, integer *nrhs, real *a, integer *lda, - real *b, integer *ldb, real *s, real *rcond, integer *rank, - real *work, integer *lwork, integer *info); -extern int sgelsx_(integer *m, integer *n, integer *nrhs, real *a, integer *lda, - real *b, integer *ldb, integer *jpvt, real *rcond, - integer *rank, real *work, integer *info); -extern int sgelsy_(integer *m, integer *n, integer *nrhs, real *a, integer *lda, - real *b, integer *ldb, integer *jpvt, real *rcond, - integer *rank, real *work, integer *lwork, integer *info); -extern int sgeql2_(integer *m, integer *n, real *a, integer *lda, real *tau, - real *work, integer *info); -extern int sgeqlf_(integer *m, integer *n, real *a, integer *lda, real *tau, - real *work, integer *lwork, integer *info); -extern int sgeqp3_(integer *m, integer *n, real *a, integer *lda, integer *jpvt, - real *tau, real *work, integer *lwork, integer *info); -extern int sgeqpf_(integer *m, integer *n, real *a, integer *lda, integer *jpvt, - real *tau, real *work, integer *info); -extern int sgeqr2_(integer *m, integer *n, real *a, integer *lda, real *tau, - real *work, integer *info); -extern int sgeqrf_(integer *m, integer *n, real *a, integer *lda, real *tau, - real *work, integer *lwork, integer *info); -extern int sgerfs_(char *trans, integer *n, integer *nrhs, real *a, - integer *lda, real *af, integer *ldaf, integer *ipiv, - real *b, integer *ldb, real *x, integer *ldx, real *ferr, - real *berr, real *work, integer *iwork, integer *info, - ftnlen trans_len); -extern int sgerq2_(integer *m, integer *n, real *a, integer *lda, real *tau, - real *work, integer *info); -extern int sgerqf_(integer *m, integer *n, real *a, integer *lda, real *tau, - real *work, integer *lwork, integer *info); -extern int sgesc2_(integer *n, real *a, integer *lda, real *rhs, integer *ipiv, - integer *jpiv, real *scale); -extern int sgesdd_(char *jobz, integer *m, integer *n, real *a, integer *lda, - real *s, real *u, integer *ldu, real *vt, integer *ldvt, - real *work, integer *lwork, integer *iwork, integer *info, - ftnlen jobz_len); -extern int sgesv_(integer *n, integer *nrhs, real *a, integer *lda, - integer *ipiv, real *b, integer *ldb, integer *info); -extern int sgesvd_(char *jobu, char *jobvt, integer *m, integer *n, real *a, - integer *lda, real *s, real *u, integer *ldu, real *vt, - integer *ldvt, real *work, integer *lwork, integer *info, - ftnlen jobu_len, ftnlen jobvt_len); -extern int sgesvx_(char *fact, char *trans, integer *n, integer *nrhs, real *a, - integer *lda, real *af, integer *ldaf, integer *ipiv, - char *equed, real *r__, real *c__, real *b, integer *ldb, - real *x, integer *ldx, real *rcond, real *ferr, real *berr, - real *work, integer *iwork, integer *info, ftnlen fact_len, - ftnlen trans_len, ftnlen equed_len); -extern int sgetc2_(integer *n, real *a, integer *lda, integer *ipiv, - integer *jpiv, integer *info); -extern int sgetf2_(integer *m, integer *n, real *a, integer *lda, integer *ipiv, - integer *info); -extern int sgetrf_(integer *m, integer *n, real *a, integer *lda, integer *ipiv, - integer *info); -extern int sgetri_(integer *n, real *a, integer *lda, integer *ipiv, real *work, - integer *lwork, integer *info); -extern int sgetrs_(char *trans, integer *n, integer *nrhs, real *a, - integer *lda, integer *ipiv, real *b, integer *ldb, - integer *info, ftnlen trans_len); -extern int sggbak_(char *job, char *side, integer *n, integer *ilo, - integer *ihi, real *lscale, real *rscale, integer *m, - real *v, integer *ldv, integer *info, ftnlen job_len, - ftnlen side_len); -extern int sggbal_(char *job, integer *n, real *a, integer *lda, real *b, - integer *ldb, integer *ilo, integer *ihi, real *lscale, - real *rscale, real *work, integer *info, ftnlen job_len); -extern int sgges_(char *jobvsl, char *jobvsr, char *sort, L_fp selctg, - integer *n, real *a, integer *lda, real *b, integer *ldb, - integer *sdim, real *alphar, real *alphai, real *beta, - real *vsl, integer *ldvsl, real *vsr, integer *ldvsr, - real *work, integer *lwork, logical *bwork, integer *info, - ftnlen jobvsl_len, ftnlen jobvsr_len, ftnlen sort_len); -extern int sggesx_(char *jobvsl, char *jobvsr, char *sort, L_fp selctg, - char *sense, integer *n, real *a, integer *lda, real *b, - integer *ldb, integer *sdim, real *alphar, real *alphai, - real *beta, real *vsl, integer *ldvsl, real *vsr, - integer *ldvsr, real *rconde, real *rcondv, real *work, - integer *lwork, integer *iwork, integer *liwork, - logical *bwork, integer *info, ftnlen jobvsl_len, - ftnlen jobvsr_len, ftnlen sort_len, ftnlen sense_len); -extern int sggev_(char *jobvl, char *jobvr, integer *n, real *a, integer *lda, - real *b, integer *ldb, real *alphar, real *alphai, real *beta, - real *vl, integer *ldvl, real *vr, integer *ldvr, real *work, - integer *lwork, integer *info, ftnlen jobvl_len, - ftnlen jobvr_len); -extern int sggevx_(char *balanc, char *jobvl, char *jobvr, char *sense, - integer *n, real *a, integer *lda, real *b, integer *ldb, - real *alphar, real *alphai, real *beta, real *vl, - integer *ldvl, real *vr, integer *ldvr, integer *ilo, - integer *ihi, real *lscale, real *rscale, real *abnrm, - real *bbnrm, real *rconde, real *rcondv, real *work, - integer *lwork, integer *iwork, logical *bwork, - integer *info, ftnlen balanc_len, ftnlen jobvl_len, - ftnlen jobvr_len, ftnlen sense_len); -extern int sggglm_(integer *n, integer *m, integer *p, real *a, integer *lda, - real *b, integer *ldb, real *d__, real *x, real *y, - real *work, integer *lwork, integer *info); -extern int sgghrd_(char *compq, char *compz, integer *n, integer *ilo, - integer *ihi, real *a, integer *lda, real *b, integer *ldb, - real *q, integer *ldq, real *z__, integer *ldz, - integer *info, ftnlen compq_len, ftnlen compz_len); -extern int sgglse_(integer *m, integer *n, integer *p, real *a, integer *lda, - real *b, integer *ldb, real *c__, real *d__, real *x, - real *work, integer *lwork, integer *info); -extern int sggqrf_(integer *n, integer *m, integer *p, real *a, integer *lda, - real *taua, real *b, integer *ldb, real *taub, real *work, - integer *lwork, integer *info); -extern int sggrqf_(integer *m, integer *p, integer *n, real *a, integer *lda, - real *taua, real *b, integer *ldb, real *taub, real *work, - integer *lwork, integer *info); -extern int sggsvd_(char *jobu, char *jobv, char *jobq, integer *m, integer *n, - integer *p, integer *k, integer *l, real *a, integer *lda, - real *b, integer *ldb, real *alpha, real *beta, real *u, - integer *ldu, real *v, integer *ldv, real *q, integer *ldq, - real *work, integer *iwork, integer *info, ftnlen jobu_len, - ftnlen jobv_len, ftnlen jobq_len); -extern int sggsvp_(char *jobu, char *jobv, char *jobq, integer *m, integer *p, - integer *n, real *a, integer *lda, real *b, integer *ldb, - real *tola, real *tolb, integer *k, integer *l, real *u, - integer *ldu, real *v, integer *ldv, real *q, integer *ldq, - integer *iwork, real *tau, real *work, integer *info, - ftnlen jobu_len, ftnlen jobv_len, ftnlen jobq_len); -extern int sgtcon_(char *norm, integer *n, real *dl, real *d__, real *du, - real *du2, integer *ipiv, real *anorm, real *rcond, - real *work, integer *iwork, integer *info, ftnlen norm_len); -extern int sgtrfs_(char *trans, integer *n, integer *nrhs, real *dl, real *d__, - real *du, real *dlf, real *df, real *duf, real *du2, - integer *ipiv, real *b, integer *ldb, real *x, integer *ldx, - real *ferr, real *berr, real *work, integer *iwork, - integer *info, ftnlen trans_len); -extern int sgtsv_(integer *n, integer *nrhs, real *dl, real *d__, real *du, - real *b, integer *ldb, integer *info); -extern int sgtsvx_(char *fact, char *trans, integer *n, integer *nrhs, real *dl, - real *d__, real *du, real *dlf, real *df, real *duf, - real *du2, integer *ipiv, real *b, integer *ldb, real *x, - integer *ldx, real *rcond, real *ferr, real *berr, - real *work, integer *iwork, integer *info, ftnlen fact_len, - ftnlen trans_len); -extern int sgttrf_(integer *n, real *dl, real *d__, real *du, real *du2, - integer *ipiv, integer *info); -extern int sgttrs_(char *trans, integer *n, integer *nrhs, real *dl, real *d__, - real *du, real *du2, integer *ipiv, real *b, integer *ldb, - integer *info, ftnlen trans_len); -extern int sgtts2_(integer *itrans, integer *n, integer *nrhs, real *dl, - real *d__, real *du, real *du2, integer *ipiv, real *b, - integer *ldb); -extern int shgeqz_(char *job, char *compq, char *compz, integer *n, - integer *ilo, integer *ihi, real *a, integer *lda, real *b, - integer *ldb, real *alphar, real *alphai, real *beta, - real *q, integer *ldq, real *z__, integer *ldz, real *work, - integer *lwork, integer *info, ftnlen job_len, - ftnlen compq_len, ftnlen compz_len); -extern int shsein_(char *side, char *eigsrc, char *initv, logical *select, - integer *n, real *h__, integer *ldh, real *wr, real *wi, - real *vl, integer *ldvl, real *vr, integer *ldvr, - integer *mm, integer *m, real *work, integer *ifaill, - integer *ifailr, integer *info, ftnlen side_len, - ftnlen eigsrc_len, ftnlen initv_len); -extern int shseqr_(char *job, char *compz, integer *n, integer *ilo, - integer *ihi, real *h__, integer *ldh, real *wr, real *wi, - real *z__, integer *ldz, real *work, integer *lwork, - integer *info, ftnlen job_len, ftnlen compz_len); -extern int slabad_(real *small, real *large); -extern int slabrd_(integer *m, integer *n, integer *nb, real *a, integer *lda, - real *d__, real *e, real *tauq, real *taup, real *x, - integer *ldx, real *y, integer *ldy); -extern int slacon_(integer *n, real *v, real *x, integer *isgn, real *est, - integer *kase); -extern int slacpy_(char *uplo, integer *m, integer *n, real *a, integer *lda, - real *b, integer *ldb, ftnlen uplo_len); -extern int sladiv_(real *a, real *b, real *c__, real *d__, real *p, real *q); -extern int slae2_(real *a, real *b, real *c__, real *rt1, real *rt2); -extern int slaebz_(integer *ijob, integer *nitmax, integer *n, integer *mmax, - integer *minp, integer *nbmin, real *abstol, real *reltol, - real *pivmin, real *d__, real *e, real *e2, integer *nval, - real *ab, real *c__, integer *mout, integer *nab, real *work, - integer *iwork, integer *info); -extern int slaed0_(integer *icompq, integer *qsiz, integer *n, real *d__, - real *e, real *q, integer *ldq, real *qstore, integer *ldqs, - real *work, integer *iwork, integer *info); -extern int slaed1_(integer *n, real *d__, real *q, integer *ldq, integer *indxq, - real *rho, integer *cutpnt, real *work, integer *iwork, - integer *info); -extern int slaed2_(integer *k, integer *n, integer *n1, real *d__, real *q, - integer *ldq, integer *indxq, real *rho, real *z__, - real *dlamda, real *w, real *q2, integer *indx, - integer *indxc, integer *indxp, integer *coltyp, - integer *info); -extern int slaed3_(integer *k, integer *n, integer *n1, real *d__, real *q, - integer *ldq, real *rho, real *dlamda, real *q2, - integer *indx, integer *ctot, real *w, real *s, - integer *info); -extern int slaed4_(integer *n, integer *i__, real *d__, real *z__, real *delta, - real *rho, real *dlam, integer *info); -extern int slaed5_(integer *i__, real *d__, real *z__, real *delta, real *rho, - real *dlam); -extern int slaed6_(integer *kniter, logical *orgati, real *rho, real *d__, - real *z__, real *finit, real *tau, integer *info); -extern int slaed7_(integer *icompq, integer *n, integer *qsiz, integer *tlvls, - integer *curlvl, integer *curpbm, real *d__, real *q, - integer *ldq, integer *indxq, real *rho, integer *cutpnt, - real *qstore, integer *qptr, integer *prmptr, integer *perm, - integer *givptr, integer *givcol, real *givnum, real *work, - integer *iwork, integer *info); -extern int slaed8_(integer *icompq, integer *k, integer *n, integer *qsiz, - real *d__, real *q, integer *ldq, integer *indxq, real *rho, - integer *cutpnt, real *z__, real *dlamda, real *q2, - integer *ldq2, real *w, integer *perm, integer *givptr, - integer *givcol, real *givnum, integer *indxp, integer *indx, - integer *info); -extern int slaed9_(integer *k, integer *kstart, integer *kstop, integer *n, - real *d__, real *q, integer *ldq, real *rho, real *dlamda, - real *w, real *s, integer *lds, integer *info); -extern int slaeda_(integer *n, integer *tlvls, integer *curlvl, integer *curpbm, - integer *prmptr, integer *perm, integer *givptr, - integer *givcol, real *givnum, real *q, integer *qptr, - real *z__, real *ztemp, integer *info); -extern int slaein_(logical *rightv, logical *noinit, integer *n, real *h__, - integer *ldh, real *wr, real *wi, real *vr, real *vi, - real *b, integer *ldb, real *work, real *eps3, real *smlnum, - real *bignum, integer *info); -extern int slaev2_(real *a, real *b, real *c__, real *rt1, real *rt2, real *cs1, - real *sn1); -extern int slaexc_(logical *wantq, integer *n, real *t, integer *ldt, real *q, - integer *ldq, integer *j1, integer *n1, integer *n2, - real *work, integer *info); -extern int slag2_(real *a, integer *lda, real *b, integer *ldb, real *safmin, - real *scale1, real *scale2, real *wr1, real *wr2, real *wi); -extern int slags2_(logical *upper, real *a1, real *a2, real *a3, real *b1, - real *b2, real *b3, real *csu, real *snu, real *csv, - real *snv, real *csq, real *snq); -extern int slagtf_(integer *n, real *a, real *lambda, real *b, real *c__, - real *tol, real *d__, integer *in, integer *info); -extern int slagtm_(char *trans, integer *n, integer *nrhs, real *alpha, - real *dl, real *d__, real *du, real *x, integer *ldx, - real *beta, real *b, integer *ldb, ftnlen trans_len); -extern int slagts_(integer *job, integer *n, real *a, real *b, real *c__, - real *d__, integer *in, real *y, real *tol, integer *info); -extern int slagv2_(real *a, integer *lda, real *b, integer *ldb, real *alphar, - real *alphai, real *beta, real *csl, real *snl, real *csr, - real *snr); -extern int slahqr_(logical *wantt, logical *wantz, integer *n, integer *ilo, - integer *ihi, real *h__, integer *ldh, real *wr, real *wi, - integer *iloz, integer *ihiz, real *z__, integer *ldz, - integer *info); -extern int slahrd_(integer *n, integer *k, integer *nb, real *a, integer *lda, - real *tau, real *t, integer *ldt, real *y, integer *ldy); -extern int slaic1_(integer *job, integer *j, real *x, real *sest, real *w, - real *gamma, real *sestpr, real *s, real *c__); -extern int slaln2_(logical *ltrans, integer *na, integer *nw, real *smin, - real *ca, real *a, integer *lda, real *d1, real *d2, real *b, - integer *ldb, real *wr, real *wi, real *x, integer *ldx, - real *scale, real *xnorm, integer *info); -extern int slals0_(integer *icompq, integer *nl, integer *nr, integer *sqre, - integer *nrhs, real *b, integer *ldb, real *bx, - integer *ldbx, integer *perm, integer *givptr, - integer *givcol, integer *ldgcol, real *givnum, - integer *ldgnum, real *poles, real *difl, real *difr, - real *z__, integer *k, real *c__, real *s, real *work, - integer *info); -extern int slalsa_(integer *icompq, integer *smlsiz, integer *n, integer *nrhs, - real *b, integer *ldb, real *bx, integer *ldbx, real *u, - integer *ldu, real *vt, integer *k, real *difl, real *difr, - real *z__, real *poles, integer *givptr, integer *givcol, - integer *ldgcol, integer *perm, real *givnum, real *c__, - real *s, real *work, integer *iwork, integer *info); -extern int slalsd_(char *uplo, integer *smlsiz, integer *n, integer *nrhs, - real *d__, real *e, real *b, integer *ldb, real *rcond, - integer *rank, real *work, integer *iwork, integer *info, - ftnlen uplo_len); -extern E_f slamch_(char *cmach, ftnlen cmach_len); -extern int slamc1_(integer *beta, integer *t, logical *rnd, logical *ieee1); -extern int slamc2_(integer *beta, integer *t, logical *rnd, real *eps, - integer *emin, real *rmin, integer *emax, real *rmax); -extern E_f slamc3_(real *a, real *b); -extern int slamc4_(integer *emin, real *start, integer *base); -extern int slamc5_(integer *beta, integer *p, integer *emin, logical *ieee, - integer *emax, real *rmax); -extern int slamrg_(integer *n1, integer *n2, real *a, integer *strd1, - integer *strd2, integer *index); -extern E_f slangb_(char *norm, integer *n, integer *kl, integer *ku, real *ab, - integer *ldab, real *work, ftnlen norm_len); -extern E_f slange_(char *norm, integer *m, integer *n, real *a, integer *lda, - real *work, ftnlen norm_len); -extern E_f slangt_(char *norm, integer *n, real *dl, real *d__, real *du, - ftnlen norm_len); -extern E_f slanhs_(char *norm, integer *n, real *a, integer *lda, real *work, - ftnlen norm_len); -extern E_f slansb_(char *norm, char *uplo, integer *n, integer *k, real *ab, - integer *ldab, real *work, ftnlen norm_len, ftnlen uplo_len); -extern E_f slansp_(char *norm, char *uplo, integer *n, real *ap, real *work, - ftnlen norm_len, ftnlen uplo_len); -extern E_f slanst_(char *norm, integer *n, real *d__, real *e, ftnlen norm_len); -extern E_f slansy_(char *norm, char *uplo, integer *n, real *a, integer *lda, - real *work, ftnlen norm_len, ftnlen uplo_len); -extern E_f slantb_(char *norm, char *uplo, char *diag, integer *n, integer *k, - real *ab, integer *ldab, real *work, ftnlen norm_len, - ftnlen uplo_len, ftnlen diag_len); -extern E_f slantp_(char *norm, char *uplo, char *diag, integer *n, real *ap, - real *work, ftnlen norm_len, ftnlen uplo_len, - ftnlen diag_len); -extern E_f slantr_(char *norm, char *uplo, char *diag, integer *m, integer *n, - real *a, integer *lda, real *work, ftnlen norm_len, - ftnlen uplo_len, ftnlen diag_len); -extern int slanv2_(real *a, real *b, real *c__, real *d__, real *rt1r, - real *rt1i, real *rt2r, real *rt2i, real *cs, real *sn); -extern int slapll_(integer *n, real *x, integer *incx, real *y, integer *incy, - real *ssmin); -extern int slapmt_(logical *forwrd, integer *m, integer *n, real *x, - integer *ldx, integer *k); -extern E_f slapy2_(real *x, real *y); -extern E_f slapy3_(real *x, real *y, real *z__); -extern int slaqgb_(integer *m, integer *n, integer *kl, integer *ku, real *ab, - integer *ldab, real *r__, real *c__, real *rowcnd, - real *colcnd, real *amax, char *equed, ftnlen equed_len); -extern int slaqge_(integer *m, integer *n, real *a, integer *lda, real *r__, - real *c__, real *rowcnd, real *colcnd, real *amax, - char *equed, ftnlen equed_len); -extern int slaqp2_(integer *m, integer *n, integer *offset, real *a, - integer *lda, integer *jpvt, real *tau, real *vn1, real *vn2, - real *work); -extern int slaqps_(integer *m, integer *n, integer *offset, integer *nb, - integer *kb, real *a, integer *lda, integer *jpvt, real *tau, - real *vn1, real *vn2, real *auxv, real *f, integer *ldf); -extern int slaqsb_(char *uplo, integer *n, integer *kd, real *ab, integer *ldab, - real *s, real *scond, real *amax, char *equed, - ftnlen uplo_len, ftnlen equed_len); -extern int slaqsp_(char *uplo, integer *n, real *ap, real *s, real *scond, - real *amax, char *equed, ftnlen uplo_len, ftnlen equed_len); -extern int slaqsy_(char *uplo, integer *n, real *a, integer *lda, real *s, - real *scond, real *amax, char *equed, ftnlen uplo_len, - ftnlen equed_len); -extern int slaqtr_(logical *ltran, logical *lreal, integer *n, real *t, - integer *ldt, real *b, real *w, real *scale, real *x, - real *work, integer *info); -extern int slar1v_(integer *n, integer *b1, integer *bn, real *sigma, real *d__, - real *l, real *ld, real *lld, real *gersch, real *z__, - real *ztz, real *mingma, integer *r__, integer *isuppz, - real *work); -extern int slar2v_(integer *n, real *x, real *y, real *z__, integer *incx, - real *c__, real *s, integer *incc); -extern int slarf_(char *side, integer *m, integer *n, real *v, integer *incv, - real *tau, real *c__, integer *ldc, real *work, - ftnlen side_len); -extern int slarfb_(char *side, char *trans, char *direct, char *storev, - integer *m, integer *n, integer *k, real *v, integer *ldv, - real *t, integer *ldt, real *c__, integer *ldc, real *work, - integer *ldwork, ftnlen side_len, ftnlen trans_len, - ftnlen direct_len, ftnlen storev_len); -extern int slarfg_(integer *n, real *alpha, real *x, integer *incx, real *tau); -extern int slarft_(char *direct, char *storev, integer *n, integer *k, real *v, - integer *ldv, real *tau, real *t, integer *ldt, - ftnlen direct_len, ftnlen storev_len); -extern int slarfx_(char *side, integer *m, integer *n, real *v, real *tau, - real *c__, integer *ldc, real *work, ftnlen side_len); -extern int slargv_(integer *n, real *x, integer *incx, real *y, integer *incy, - real *c__, integer *incc); -extern int slarnv_(integer *idist, integer *iseed, integer *n, real *x); -extern int slarrb_(integer *n, real *d__, real *l, real *ld, real *lld, - integer *ifirst, integer *ilast, real *sigma, real *reltol, - real *w, real *wgap, real *werr, real *work, integer *iwork, - integer *info); -extern int slarre_(integer *n, real *d__, real *e, real *tol, integer *nsplit, - integer *isplit, integer *m, real *w, real *woff, - real *gersch, real *work, integer *info); -extern int slarrf_(integer *n, real *d__, real *l, real *ld, real *lld, - integer *ifirst, integer *ilast, real *w, real *dplus, - real *lplus, real *work, integer *iwork, integer *info); -extern int slarrv_(integer *n, real *d__, real *l, integer *isplit, integer *m, - real *w, integer *iblock, real *gersch, real *tol, real *z__, - integer *ldz, integer *isuppz, real *work, integer *iwork, - integer *info); -extern int slartg_(real *f, real *g, real *cs, real *sn, real *r__); -extern int slartv_(integer *n, real *x, integer *incx, real *y, integer *incy, - real *c__, real *s, integer *incc); -extern int slaruv_(integer *iseed, integer *n, real *x); -extern int slarz_(char *side, integer *m, integer *n, integer *l, real *v, - integer *incv, real *tau, real *c__, integer *ldc, real *work, - ftnlen side_len); -extern int slarzb_(char *side, char *trans, char *direct, char *storev, - integer *m, integer *n, integer *k, integer *l, real *v, - integer *ldv, real *t, integer *ldt, real *c__, integer *ldc, - real *work, integer *ldwork, ftnlen side_len, - ftnlen trans_len, ftnlen direct_len, ftnlen storev_len); -extern int slarzt_(char *direct, char *storev, integer *n, integer *k, real *v, - integer *ldv, real *tau, real *t, integer *ldt, - ftnlen direct_len, ftnlen storev_len); -extern int slas2_(real *f, real *g, real *h__, real *ssmin, real *ssmax); -extern int slascl_(char *type__, integer *kl, integer *ku, real *cfrom, - real *cto, integer *m, integer *n, real *a, integer *lda, - integer *info, ftnlen type_len); -extern int slasd0_(integer *n, integer *sqre, real *d__, real *e, real *u, - integer *ldu, real *vt, integer *ldvt, integer *smlsiz, - integer *iwork, real *work, integer *info); -extern int slasd1_(integer *nl, integer *nr, integer *sqre, real *d__, - real *alpha, real *beta, real *u, integer *ldu, real *vt, - integer *ldvt, integer *idxq, integer *iwork, real *work, - integer *info); -extern int slasd2_(integer *nl, integer *nr, integer *sqre, integer *k, - real *d__, real *z__, real *alpha, real *beta, real *u, - integer *ldu, real *vt, integer *ldvt, real *dsigma, - real *u2, integer *ldu2, real *vt2, integer *ldvt2, - integer *idxp, integer *idx, integer *idxc, integer *idxq, - integer *coltyp, integer *info); -extern int slasd3_(integer *nl, integer *nr, integer *sqre, integer *k, - real *d__, real *q, integer *ldq, real *dsigma, real *u, - integer *ldu, real *u2, integer *ldu2, real *vt, - integer *ldvt, real *vt2, integer *ldvt2, integer *idxc, - integer *ctot, real *z__, integer *info); -extern int slasd4_(integer *n, integer *i__, real *d__, real *z__, real *delta, - real *rho, real *sigma, real *work, integer *info); -extern int slasd5_(integer *i__, real *d__, real *z__, real *delta, real *rho, - real *dsigma, real *work); -extern int slasd6_(integer *icompq, integer *nl, integer *nr, integer *sqre, - real *d__, real *vf, real *vl, real *alpha, real *beta, - integer *idxq, integer *perm, integer *givptr, - integer *givcol, integer *ldgcol, real *givnum, - integer *ldgnum, real *poles, real *difl, real *difr, - real *z__, integer *k, real *c__, real *s, real *work, - integer *iwork, integer *info); -extern int slasd7_(integer *icompq, integer *nl, integer *nr, integer *sqre, - integer *k, real *d__, real *z__, real *zw, real *vf, - real *vfw, real *vl, real *vlw, real *alpha, real *beta, - real *dsigma, integer *idx, integer *idxp, integer *idxq, - integer *perm, integer *givptr, integer *givcol, - integer *ldgcol, real *givnum, integer *ldgnum, real *c__, - real *s, integer *info); -extern int slasd8_(integer *icompq, integer *k, real *d__, real *z__, real *vf, - real *vl, real *difl, real *difr, integer *lddifr, - real *dsigma, real *work, integer *info); -extern int slasd9_(integer *icompq, integer *ldu, integer *k, real *d__, - real *z__, real *vf, real *vl, real *difl, real *difr, - real *dsigma, real *work, integer *info); -extern int slasda_(integer *icompq, integer *smlsiz, integer *n, integer *sqre, - real *d__, real *e, real *u, integer *ldu, real *vt, - integer *k, real *difl, real *difr, real *z__, real *poles, - integer *givptr, integer *givcol, integer *ldgcol, - integer *perm, real *givnum, real *c__, real *s, real *work, - integer *iwork, integer *info); -extern int slasdq_(char *uplo, integer *sqre, integer *n, integer *ncvt, - integer *nru, integer *ncc, real *d__, real *e, real *vt, - integer *ldvt, real *u, integer *ldu, real *c__, - integer *ldc, real *work, integer *info, ftnlen uplo_len); -extern int slasdt_(integer *n, integer *lvl, integer *nd, integer *inode, - integer *ndiml, integer *ndimr, integer *msub); -extern int slaset_(char *uplo, integer *m, integer *n, real *alpha, real *beta, - real *a, integer *lda, ftnlen uplo_len); -extern int slasq1_(integer *n, real *d__, real *e, real *work, integer *info); -extern int slasq2_(integer *n, real *z__, integer *info); -extern int slasq3_(integer *i0, integer *n0, real *z__, integer *pp, - real *dmin__, real *sigma, real *desig, real *qmax, - integer *nfail, integer *iter, integer *ndiv); -extern int slasq4_(integer *i0, integer *n0, real *z__, integer *pp, - integer *n0in, real *dmin__, real *dmin1, real *dmin2, - real *dn, real *dn1, real *dn2, real *tau, integer *ttype); -extern int slasq5_(integer *i0, integer *n0, real *z__, integer *pp, real *tau, - real *dmin__, real *dmin1, real *dmin2, real *dn, real *dnm1, - real *dnm2); -extern int slasq6_(integer *i0, integer *n0, real *z__, integer *pp, - real *dmin__, real *dmin1, real *dmin2, real *dn, real *dnm1, - real *dnm2); -extern int slasr_(char *side, char *pivot, char *direct, integer *m, integer *n, - real *c__, real *s, real *a, integer *lda, ftnlen side_len, - ftnlen pivot_len, ftnlen direct_len); -extern int slasrt_(char *id, integer *n, real *d__, integer *info, - ftnlen id_len); -extern int slassq_(integer *n, real *x, integer *incx, real *scale, - real *sumsq); -extern int slasv2_(real *f, real *g, real *h__, real *ssmin, real *ssmax, - real *snr, real *csr, real *snl, real *csl); -extern int slaswp_(integer *n, real *a, integer *lda, integer *k1, integer *k2, - integer *ipiv, integer *incx); -extern int slasy2_(logical *ltranl, logical *ltranr, integer *isgn, integer *n1, - integer *n2, real *tl, integer *ldtl, real *tr, - integer *ldtr, real *b, integer *ldb, real *scale, real *x, - integer *ldx, real *xnorm, integer *info); -extern int slasyf_(char *uplo, integer *n, integer *nb, integer *kb, real *a, - integer *lda, integer *ipiv, real *w, integer *ldw, - integer *info, ftnlen uplo_len); -extern int slatbs_(char *uplo, char *trans, char *diag, char *normin, - integer *n, integer *kd, real *ab, integer *ldab, real *x, - real *scale, real *cnorm, integer *info, ftnlen uplo_len, - ftnlen trans_len, ftnlen diag_len, ftnlen normin_len); -extern int slatdf_(integer *ijob, integer *n, real *z__, integer *ldz, - real *rhs, real *rdsum, real *rdscal, integer *ipiv, - integer *jpiv); -extern int slatps_(char *uplo, char *trans, char *diag, char *normin, - integer *n, real *ap, real *x, real *scale, real *cnorm, - integer *info, ftnlen uplo_len, ftnlen trans_len, - ftnlen diag_len, ftnlen normin_len); -extern int slatrd_(char *uplo, integer *n, integer *nb, real *a, integer *lda, - real *e, real *tau, real *w, integer *ldw, ftnlen uplo_len); -extern int slatrs_(char *uplo, char *trans, char *diag, char *normin, - integer *n, real *a, integer *lda, real *x, real *scale, - real *cnorm, integer *info, ftnlen uplo_len, - ftnlen trans_len, ftnlen diag_len, ftnlen normin_len); -extern int slatrz_(integer *m, integer *n, integer *l, real *a, integer *lda, - real *tau, real *work); -extern int slatzm_(char *side, integer *m, integer *n, real *v, integer *incv, - real *tau, real *c1, real *c2, integer *ldc, real *work, - ftnlen side_len); -extern int slauu2_(char *uplo, integer *n, real *a, integer *lda, integer *info, - ftnlen uplo_len); -extern int slauum_(char *uplo, integer *n, real *a, integer *lda, integer *info, - ftnlen uplo_len); -extern int sopgtr_(char *uplo, integer *n, real *ap, real *tau, real *q, - integer *ldq, real *work, integer *info, ftnlen uplo_len); -extern int sopmtr_(char *side, char *uplo, char *trans, integer *m, integer *n, - real *ap, real *tau, real *c__, integer *ldc, real *work, - integer *info, ftnlen side_len, ftnlen uplo_len, - ftnlen trans_len); -extern int sorg2l_(integer *m, integer *n, integer *k, real *a, integer *lda, - real *tau, real *work, integer *info); -extern int sorg2r_(integer *m, integer *n, integer *k, real *a, integer *lda, - real *tau, real *work, integer *info); -extern int sorgbr_(char *vect, integer *m, integer *n, integer *k, real *a, - integer *lda, real *tau, real *work, integer *lwork, - integer *info, ftnlen vect_len); -extern int sorghr_(integer *n, integer *ilo, integer *ihi, real *a, - integer *lda, real *tau, real *work, integer *lwork, - integer *info); -extern int sorgl2_(integer *m, integer *n, integer *k, real *a, integer *lda, - real *tau, real *work, integer *info); -extern int sorglq_(integer *m, integer *n, integer *k, real *a, integer *lda, - real *tau, real *work, integer *lwork, integer *info); -extern int sorgql_(integer *m, integer *n, integer *k, real *a, integer *lda, - real *tau, real *work, integer *lwork, integer *info); -extern int sorgqr_(integer *m, integer *n, integer *k, real *a, integer *lda, - real *tau, real *work, integer *lwork, integer *info); -extern int sorgr2_(integer *m, integer *n, integer *k, real *a, integer *lda, - real *tau, real *work, integer *info); -extern int sorgrq_(integer *m, integer *n, integer *k, real *a, integer *lda, - real *tau, real *work, integer *lwork, integer *info); -extern int sorgtr_(char *uplo, integer *n, real *a, integer *lda, real *tau, - real *work, integer *lwork, integer *info, ftnlen uplo_len); -extern int sorm2l_(char *side, char *trans, integer *m, integer *n, integer *k, - real *a, integer *lda, real *tau, real *c__, integer *ldc, - real *work, integer *info, ftnlen side_len, - ftnlen trans_len); -extern int sorm2r_(char *side, char *trans, integer *m, integer *n, integer *k, - real *a, integer *lda, real *tau, real *c__, integer *ldc, - real *work, integer *info, ftnlen side_len, - ftnlen trans_len); -extern int sormbr_(char *vect, char *side, char *trans, integer *m, integer *n, - integer *k, real *a, integer *lda, real *tau, real *c__, - integer *ldc, real *work, integer *lwork, integer *info, - ftnlen vect_len, ftnlen side_len, ftnlen trans_len); -extern int sormhr_(char *side, char *trans, integer *m, integer *n, - integer *ilo, integer *ihi, real *a, integer *lda, real *tau, - real *c__, integer *ldc, real *work, integer *lwork, - integer *info, ftnlen side_len, ftnlen trans_len); -extern int sorml2_(char *side, char *trans, integer *m, integer *n, integer *k, - real *a, integer *lda, real *tau, real *c__, integer *ldc, - real *work, integer *info, ftnlen side_len, - ftnlen trans_len); -extern int sormlq_(char *side, char *trans, integer *m, integer *n, integer *k, - real *a, integer *lda, real *tau, real *c__, integer *ldc, - real *work, integer *lwork, integer *info, ftnlen side_len, - ftnlen trans_len); -extern int sormql_(char *side, char *trans, integer *m, integer *n, integer *k, - real *a, integer *lda, real *tau, real *c__, integer *ldc, - real *work, integer *lwork, integer *info, ftnlen side_len, - ftnlen trans_len); -extern int sormqr_(char *side, char *trans, integer *m, integer *n, integer *k, - real *a, integer *lda, real *tau, real *c__, integer *ldc, - real *work, integer *lwork, integer *info, ftnlen side_len, - ftnlen trans_len); -extern int sormr2_(char *side, char *trans, integer *m, integer *n, integer *k, - real *a, integer *lda, real *tau, real *c__, integer *ldc, - real *work, integer *info, ftnlen side_len, - ftnlen trans_len); -extern int sormr3_(char *side, char *trans, integer *m, integer *n, integer *k, - integer *l, real *a, integer *lda, real *tau, real *c__, - integer *ldc, real *work, integer *info, ftnlen side_len, - ftnlen trans_len); -extern int sormrq_(char *side, char *trans, integer *m, integer *n, integer *k, - real *a, integer *lda, real *tau, real *c__, integer *ldc, - real *work, integer *lwork, integer *info, ftnlen side_len, - ftnlen trans_len); -extern int sormrz_(char *side, char *trans, integer *m, integer *n, integer *k, - integer *l, real *a, integer *lda, real *tau, real *c__, - integer *ldc, real *work, integer *lwork, integer *info, - ftnlen side_len, ftnlen trans_len); -extern int sormtr_(char *side, char *uplo, char *trans, integer *m, integer *n, - real *a, integer *lda, real *tau, real *c__, integer *ldc, - real *work, integer *lwork, integer *info, ftnlen side_len, - ftnlen uplo_len, ftnlen trans_len); -extern int spbcon_(char *uplo, integer *n, integer *kd, real *ab, integer *ldab, - real *anorm, real *rcond, real *work, integer *iwork, - integer *info, ftnlen uplo_len); -extern int spbequ_(char *uplo, integer *n, integer *kd, real *ab, integer *ldab, - real *s, real *scond, real *amax, integer *info, - ftnlen uplo_len); -extern int spbrfs_(char *uplo, integer *n, integer *kd, integer *nrhs, real *ab, - integer *ldab, real *afb, integer *ldafb, real *b, - integer *ldb, real *x, integer *ldx, real *ferr, real *berr, - real *work, integer *iwork, integer *info, ftnlen uplo_len); -extern int spbstf_(char *uplo, integer *n, integer *kd, real *ab, integer *ldab, - integer *info, ftnlen uplo_len); -extern int spbsv_(char *uplo, integer *n, integer *kd, integer *nrhs, real *ab, - integer *ldab, real *b, integer *ldb, integer *info, - ftnlen uplo_len); -extern int spbsvx_(char *fact, char *uplo, integer *n, integer *kd, - integer *nrhs, real *ab, integer *ldab, real *afb, - integer *ldafb, char *equed, real *s, real *b, integer *ldb, - real *x, integer *ldx, real *rcond, real *ferr, real *berr, - real *work, integer *iwork, integer *info, ftnlen fact_len, - ftnlen uplo_len, ftnlen equed_len); -extern int spbtf2_(char *uplo, integer *n, integer *kd, real *ab, integer *ldab, - integer *info, ftnlen uplo_len); -extern int spbtrf_(char *uplo, integer *n, integer *kd, real *ab, integer *ldab, - integer *info, ftnlen uplo_len); -extern int spbtrs_(char *uplo, integer *n, integer *kd, integer *nrhs, real *ab, - integer *ldab, real *b, integer *ldb, integer *info, - ftnlen uplo_len); -extern int spocon_(char *uplo, integer *n, real *a, integer *lda, real *anorm, - real *rcond, real *work, integer *iwork, integer *info, - ftnlen uplo_len); -extern int spoequ_(integer *n, real *a, integer *lda, real *s, real *scond, - real *amax, integer *info); -extern int sporfs_(char *uplo, integer *n, integer *nrhs, real *a, integer *lda, - real *af, integer *ldaf, real *b, integer *ldb, real *x, - integer *ldx, real *ferr, real *berr, real *work, - integer *iwork, integer *info, ftnlen uplo_len); -extern int sposv_(char *uplo, integer *n, integer *nrhs, real *a, integer *lda, - real *b, integer *ldb, integer *info, ftnlen uplo_len); -extern int sposvx_(char *fact, char *uplo, integer *n, integer *nrhs, real *a, - integer *lda, real *af, integer *ldaf, char *equed, real *s, - real *b, integer *ldb, real *x, integer *ldx, real *rcond, - real *ferr, real *berr, real *work, integer *iwork, - integer *info, ftnlen fact_len, ftnlen uplo_len, - ftnlen equed_len); -extern int spotf2_(char *uplo, integer *n, real *a, integer *lda, integer *info, - ftnlen uplo_len); -extern int spotrf_(char *uplo, integer *n, real *a, integer *lda, integer *info, - ftnlen uplo_len); -extern int spotri_(char *uplo, integer *n, real *a, integer *lda, integer *info, - ftnlen uplo_len); -extern int spotrs_(char *uplo, integer *n, integer *nrhs, real *a, integer *lda, - real *b, integer *ldb, integer *info, ftnlen uplo_len); -extern int sppcon_(char *uplo, integer *n, real *ap, real *anorm, real *rcond, - real *work, integer *iwork, integer *info, ftnlen uplo_len); -extern int sppequ_(char *uplo, integer *n, real *ap, real *s, real *scond, - real *amax, integer *info, ftnlen uplo_len); -extern int spprfs_(char *uplo, integer *n, integer *nrhs, real *ap, real *afp, - real *b, integer *ldb, real *x, integer *ldx, real *ferr, - real *berr, real *work, integer *iwork, integer *info, - ftnlen uplo_len); -extern int sppsv_(char *uplo, integer *n, integer *nrhs, real *ap, real *b, - integer *ldb, integer *info, ftnlen uplo_len); -extern int sppsvx_(char *fact, char *uplo, integer *n, integer *nrhs, real *ap, - real *afp, char *equed, real *s, real *b, integer *ldb, - real *x, integer *ldx, real *rcond, real *ferr, real *berr, - real *work, integer *iwork, integer *info, ftnlen fact_len, - ftnlen uplo_len, ftnlen equed_len); -extern int spptrf_(char *uplo, integer *n, real *ap, integer *info, - ftnlen uplo_len); -extern int spptri_(char *uplo, integer *n, real *ap, integer *info, - ftnlen uplo_len); -extern int spptrs_(char *uplo, integer *n, integer *nrhs, real *ap, real *b, - integer *ldb, integer *info, ftnlen uplo_len); -extern int sptcon_(integer *n, real *d__, real *e, real *anorm, real *rcond, - real *work, integer *info); -extern int spteqr_(char *compz, integer *n, real *d__, real *e, real *z__, - integer *ldz, real *work, integer *info, ftnlen compz_len); -extern int sptrfs_(integer *n, integer *nrhs, real *d__, real *e, real *df, - real *ef, real *b, integer *ldb, real *x, integer *ldx, - real *ferr, real *berr, real *work, integer *info); -extern int sptsv_(integer *n, integer *nrhs, real *d__, real *e, real *b, - integer *ldb, integer *info); -extern int sptsvx_(char *fact, integer *n, integer *nrhs, real *d__, real *e, - real *df, real *ef, real *b, integer *ldb, real *x, - integer *ldx, real *rcond, real *ferr, real *berr, - real *work, integer *info, ftnlen fact_len); -extern int spttrf_(integer *n, real *d__, real *e, integer *info); -extern int spttrs_(integer *n, integer *nrhs, real *d__, real *e, real *b, - integer *ldb, integer *info); -extern int sptts2_(integer *n, integer *nrhs, real *d__, real *e, real *b, - integer *ldb); -extern int srscl_(integer *n, real *sa, real *sx, integer *incx); -extern int ssbev_(char *jobz, char *uplo, integer *n, integer *kd, real *ab, - integer *ldab, real *w, real *z__, integer *ldz, real *work, - integer *info, ftnlen jobz_len, ftnlen uplo_len); -extern int ssbevd_(char *jobz, char *uplo, integer *n, integer *kd, real *ab, - integer *ldab, real *w, real *z__, integer *ldz, real *work, - integer *lwork, integer *iwork, integer *liwork, - integer *info, ftnlen jobz_len, ftnlen uplo_len); -extern int ssbevx_(char *jobz, char *range, char *uplo, integer *n, integer *kd, - real *ab, integer *ldab, real *q, integer *ldq, real *vl, - real *vu, integer *il, integer *iu, real *abstol, integer *m, - real *w, real *z__, integer *ldz, real *work, integer *iwork, - integer *ifail, integer *info, ftnlen jobz_len, - ftnlen range_len, ftnlen uplo_len); -extern int ssbgst_(char *vect, char *uplo, integer *n, integer *ka, integer *kb, - real *ab, integer *ldab, real *bb, integer *ldbb, real *x, - integer *ldx, real *work, integer *info, ftnlen vect_len, - ftnlen uplo_len); -extern int ssbgv_(char *jobz, char *uplo, integer *n, integer *ka, integer *kb, - real *ab, integer *ldab, real *bb, integer *ldbb, real *w, - real *z__, integer *ldz, real *work, integer *info, - ftnlen jobz_len, ftnlen uplo_len); -extern int ssbgvd_(char *jobz, char *uplo, integer *n, integer *ka, integer *kb, - real *ab, integer *ldab, real *bb, integer *ldbb, real *w, - real *z__, integer *ldz, real *work, integer *lwork, - integer *iwork, integer *liwork, integer *info, - ftnlen jobz_len, ftnlen uplo_len); -extern int ssbgvx_(char *jobz, char *range, char *uplo, integer *n, integer *ka, - integer *kb, real *ab, integer *ldab, real *bb, - integer *ldbb, real *q, integer *ldq, real *vl, real *vu, - integer *il, integer *iu, real *abstol, integer *m, real *w, - real *z__, integer *ldz, real *work, integer *iwork, - integer *ifail, integer *info, ftnlen jobz_len, - ftnlen range_len, ftnlen uplo_len); -extern int ssbtrd_(char *vect, char *uplo, integer *n, integer *kd, real *ab, - integer *ldab, real *d__, real *e, real *q, integer *ldq, - real *work, integer *info, ftnlen vect_len, ftnlen uplo_len); -extern int sspcon_(char *uplo, integer *n, real *ap, integer *ipiv, real *anorm, - real *rcond, real *work, integer *iwork, integer *info, - ftnlen uplo_len); -extern int sspev_(char *jobz, char *uplo, integer *n, real *ap, real *w, - real *z__, integer *ldz, real *work, integer *info, - ftnlen jobz_len, ftnlen uplo_len); -extern int sspevd_(char *jobz, char *uplo, integer *n, real *ap, real *w, - real *z__, integer *ldz, real *work, integer *lwork, - integer *iwork, integer *liwork, integer *info, - ftnlen jobz_len, ftnlen uplo_len); -extern int sspevx_(char *jobz, char *range, char *uplo, integer *n, real *ap, - real *vl, real *vu, integer *il, integer *iu, real *abstol, - integer *m, real *w, real *z__, integer *ldz, real *work, - integer *iwork, integer *ifail, integer *info, - ftnlen jobz_len, ftnlen range_len, ftnlen uplo_len); -extern int sspgst_(integer *itype, char *uplo, integer *n, real *ap, real *bp, - integer *info, ftnlen uplo_len); -extern int sspgv_(integer *itype, char *jobz, char *uplo, integer *n, real *ap, - real *bp, real *w, real *z__, integer *ldz, real *work, - integer *info, ftnlen jobz_len, ftnlen uplo_len); -extern int sspgvd_(integer *itype, char *jobz, char *uplo, integer *n, real *ap, - real *bp, real *w, real *z__, integer *ldz, real *work, - integer *lwork, integer *iwork, integer *liwork, - integer *info, ftnlen jobz_len, ftnlen uplo_len); -extern int sspgvx_(integer *itype, char *jobz, char *range, char *uplo, - integer *n, real *ap, real *bp, real *vl, real *vu, - integer *il, integer *iu, real *abstol, integer *m, real *w, - real *z__, integer *ldz, real *work, integer *iwork, - integer *ifail, integer *info, ftnlen jobz_len, - ftnlen range_len, ftnlen uplo_len); -extern int ssprfs_(char *uplo, integer *n, integer *nrhs, real *ap, real *afp, - integer *ipiv, real *b, integer *ldb, real *x, integer *ldx, - real *ferr, real *berr, real *work, integer *iwork, - integer *info, ftnlen uplo_len); -extern int sspsv_(char *uplo, integer *n, integer *nrhs, real *ap, - integer *ipiv, real *b, integer *ldb, integer *info, - ftnlen uplo_len); -extern int sspsvx_(char *fact, char *uplo, integer *n, integer *nrhs, real *ap, - real *afp, integer *ipiv, real *b, integer *ldb, real *x, - integer *ldx, real *rcond, real *ferr, real *berr, - real *work, integer *iwork, integer *info, ftnlen fact_len, - ftnlen uplo_len); -extern int ssptrd_(char *uplo, integer *n, real *ap, real *d__, real *e, - real *tau, integer *info, ftnlen uplo_len); -extern int ssptrf_(char *uplo, integer *n, real *ap, integer *ipiv, - integer *info, ftnlen uplo_len); -extern int ssptri_(char *uplo, integer *n, real *ap, integer *ipiv, real *work, - integer *info, ftnlen uplo_len); -extern int ssptrs_(char *uplo, integer *n, integer *nrhs, real *ap, - integer *ipiv, real *b, integer *ldb, integer *info, - ftnlen uplo_len); -extern int sstebz_(char *range, char *order, integer *n, real *vl, real *vu, - integer *il, integer *iu, real *abstol, real *d__, real *e, - integer *m, integer *nsplit, real *w, integer *iblock, - integer *isplit, real *work, integer *iwork, integer *info, - ftnlen range_len, ftnlen order_len); -extern int sstedc_(char *compz, integer *n, real *d__, real *e, real *z__, - integer *ldz, real *work, integer *lwork, integer *iwork, - integer *liwork, integer *info, ftnlen compz_len); -extern int sstegr_(char *jobz, char *range, integer *n, real *d__, real *e, - real *vl, real *vu, integer *il, integer *iu, real *abstol, - integer *m, real *w, real *z__, integer *ldz, - integer *isuppz, real *work, integer *lwork, integer *iwork, - integer *liwork, integer *info, ftnlen jobz_len, - ftnlen range_len); -extern int sstein_(integer *n, real *d__, real *e, integer *m, real *w, - integer *iblock, integer *isplit, real *z__, integer *ldz, - real *work, integer *iwork, integer *ifail, integer *info); -extern int ssteqr_(char *compz, integer *n, real *d__, real *e, real *z__, - integer *ldz, real *work, integer *info, ftnlen compz_len); -extern int ssterf_(integer *n, real *d__, real *e, integer *info); -extern int sstev_(char *jobz, integer *n, real *d__, real *e, real *z__, - integer *ldz, real *work, integer *info, ftnlen jobz_len); -extern int sstevd_(char *jobz, integer *n, real *d__, real *e, real *z__, - integer *ldz, real *work, integer *lwork, integer *iwork, - integer *liwork, integer *info, ftnlen jobz_len); -extern int sstevr_(char *jobz, char *range, integer *n, real *d__, real *e, - real *vl, real *vu, integer *il, integer *iu, real *abstol, - integer *m, real *w, real *z__, integer *ldz, - integer *isuppz, real *work, integer *lwork, integer *iwork, - integer *liwork, integer *info, ftnlen jobz_len, - ftnlen range_len); -extern int sstevx_(char *jobz, char *range, integer *n, real *d__, real *e, - real *vl, real *vu, integer *il, integer *iu, real *abstol, - integer *m, real *w, real *z__, integer *ldz, real *work, - integer *iwork, integer *ifail, integer *info, - ftnlen jobz_len, ftnlen range_len); -extern int ssycon_(char *uplo, integer *n, real *a, integer *lda, integer *ipiv, - real *anorm, real *rcond, real *work, integer *iwork, - integer *info, ftnlen uplo_len); -extern int ssyev_(char *jobz, char *uplo, integer *n, real *a, integer *lda, - real *w, real *work, integer *lwork, integer *info, - ftnlen jobz_len, ftnlen uplo_len); -extern int ssyevd_(char *jobz, char *uplo, integer *n, real *a, integer *lda, - real *w, real *work, integer *lwork, integer *iwork, - integer *liwork, integer *info, ftnlen jobz_len, - ftnlen uplo_len); -extern int ssyevr_(char *jobz, char *range, char *uplo, integer *n, real *a, - integer *lda, real *vl, real *vu, integer *il, integer *iu, - real *abstol, integer *m, real *w, real *z__, integer *ldz, - integer *isuppz, real *work, integer *lwork, integer *iwork, - integer *liwork, integer *info, ftnlen jobz_len, - ftnlen range_len, ftnlen uplo_len); -extern int ssyevx_(char *jobz, char *range, char *uplo, integer *n, real *a, - integer *lda, real *vl, real *vu, integer *il, integer *iu, - real *abstol, integer *m, real *w, real *z__, integer *ldz, - real *work, integer *lwork, integer *iwork, integer *ifail, - integer *info, ftnlen jobz_len, ftnlen range_len, - ftnlen uplo_len); -extern int ssygs2_(integer *itype, char *uplo, integer *n, real *a, - integer *lda, real *b, integer *ldb, integer *info, - ftnlen uplo_len); -extern int ssygst_(integer *itype, char *uplo, integer *n, real *a, - integer *lda, real *b, integer *ldb, integer *info, - ftnlen uplo_len); -extern int ssygv_(integer *itype, char *jobz, char *uplo, integer *n, real *a, - integer *lda, real *b, integer *ldb, real *w, real *work, - integer *lwork, integer *info, ftnlen jobz_len, - ftnlen uplo_len); -extern int ssygvd_(integer *itype, char *jobz, char *uplo, integer *n, real *a, - integer *lda, real *b, integer *ldb, real *w, real *work, - integer *lwork, integer *iwork, integer *liwork, - integer *info, ftnlen jobz_len, ftnlen uplo_len); -extern int ssygvx_(integer *itype, char *jobz, char *range, char *uplo, - integer *n, real *a, integer *lda, real *b, integer *ldb, - real *vl, real *vu, integer *il, integer *iu, real *abstol, - integer *m, real *w, real *z__, integer *ldz, real *work, - integer *lwork, integer *iwork, integer *ifail, - integer *info, ftnlen jobz_len, ftnlen range_len, - ftnlen uplo_len); -extern int ssyrfs_(char *uplo, integer *n, integer *nrhs, real *a, integer *lda, - real *af, integer *ldaf, integer *ipiv, real *b, - integer *ldb, real *x, integer *ldx, real *ferr, real *berr, - real *work, integer *iwork, integer *info, ftnlen uplo_len); -extern int ssysv_(char *uplo, integer *n, integer *nrhs, real *a, integer *lda, - integer *ipiv, real *b, integer *ldb, real *work, - integer *lwork, integer *info, ftnlen uplo_len); -extern int ssysvx_(char *fact, char *uplo, integer *n, integer *nrhs, real *a, - integer *lda, real *af, integer *ldaf, integer *ipiv, - real *b, integer *ldb, real *x, integer *ldx, real *rcond, - real *ferr, real *berr, real *work, integer *lwork, - integer *iwork, integer *info, ftnlen fact_len, - ftnlen uplo_len); -extern int ssytd2_(char *uplo, integer *n, real *a, integer *lda, real *d__, - real *e, real *tau, integer *info, ftnlen uplo_len); -extern int ssytf2_(char *uplo, integer *n, real *a, integer *lda, integer *ipiv, - integer *info, ftnlen uplo_len); -extern int ssytrd_(char *uplo, integer *n, real *a, integer *lda, real *d__, - real *e, real *tau, real *work, integer *lwork, - integer *info, ftnlen uplo_len); -extern int ssytrf_(char *uplo, integer *n, real *a, integer *lda, integer *ipiv, - real *work, integer *lwork, integer *info, ftnlen uplo_len); -extern int ssytri_(char *uplo, integer *n, real *a, integer *lda, integer *ipiv, - real *work, integer *info, ftnlen uplo_len); -extern int ssytrs_(char *uplo, integer *n, integer *nrhs, real *a, integer *lda, - integer *ipiv, real *b, integer *ldb, integer *info, - ftnlen uplo_len); -extern int stbcon_(char *norm, char *uplo, char *diag, integer *n, integer *kd, - real *ab, integer *ldab, real *rcond, real *work, - integer *iwork, integer *info, ftnlen norm_len, - ftnlen uplo_len, ftnlen diag_len); -extern int stbrfs_(char *uplo, char *trans, char *diag, integer *n, integer *kd, - integer *nrhs, real *ab, integer *ldab, real *b, - integer *ldb, real *x, integer *ldx, real *ferr, real *berr, - real *work, integer *iwork, integer *info, ftnlen uplo_len, - ftnlen trans_len, ftnlen diag_len); -extern int stbtrs_(char *uplo, char *trans, char *diag, integer *n, integer *kd, - integer *nrhs, real *ab, integer *ldab, real *b, - integer *ldb, integer *info, ftnlen uplo_len, - ftnlen trans_len, ftnlen diag_len); -extern int stgevc_(char *side, char *howmny, logical *select, integer *n, - real *a, integer *lda, real *b, integer *ldb, real *vl, - integer *ldvl, real *vr, integer *ldvr, integer *mm, - integer *m, real *work, integer *info, ftnlen side_len, - ftnlen howmny_len); -extern int stgex2_(logical *wantq, logical *wantz, integer *n, real *a, - integer *lda, real *b, integer *ldb, real *q, integer *ldq, - real *z__, integer *ldz, integer *j1, integer *n1, - integer *n2, real *work, integer *lwork, integer *info); -extern int stgexc_(logical *wantq, logical *wantz, integer *n, real *a, - integer *lda, real *b, integer *ldb, real *q, integer *ldq, - real *z__, integer *ldz, integer *ifst, integer *ilst, - real *work, integer *lwork, integer *info); -extern int stgsen_(integer *ijob, logical *wantq, logical *wantz, - logical *select, integer *n, real *a, integer *lda, real *b, - integer *ldb, real *alphar, real *alphai, real *beta, - real *q, integer *ldq, real *z__, integer *ldz, integer *m, - real *pl, real *pr, real *dif, real *work, integer *lwork, - integer *iwork, integer *liwork, integer *info); -extern int stgsja_(char *jobu, char *jobv, char *jobq, integer *m, integer *p, - integer *n, integer *k, integer *l, real *a, integer *lda, - real *b, integer *ldb, real *tola, real *tolb, real *alpha, - real *beta, real *u, integer *ldu, real *v, integer *ldv, - real *q, integer *ldq, real *work, integer *ncycle, - integer *info, ftnlen jobu_len, ftnlen jobv_len, - ftnlen jobq_len); -extern int stgsna_(char *job, char *howmny, logical *select, integer *n, - real *a, integer *lda, real *b, integer *ldb, real *vl, - integer *ldvl, real *vr, integer *ldvr, real *s, real *dif, - integer *mm, integer *m, real *work, integer *lwork, - integer *iwork, integer *info, ftnlen job_len, - ftnlen howmny_len); -extern int stgsy2_(char *trans, integer *ijob, integer *m, integer *n, real *a, - integer *lda, real *b, integer *ldb, real *c__, integer *ldc, - real *d__, integer *ldd, real *e, integer *lde, real *f, - integer *ldf, real *scale, real *rdsum, real *rdscal, - integer *iwork, integer *pq, integer *info, - ftnlen trans_len); -extern int stgsyl_(char *trans, integer *ijob, integer *m, integer *n, real *a, - integer *lda, real *b, integer *ldb, real *c__, integer *ldc, - real *d__, integer *ldd, real *e, integer *lde, real *f, - integer *ldf, real *scale, real *dif, real *work, - integer *lwork, integer *iwork, integer *info, - ftnlen trans_len); -extern int stpcon_(char *norm, char *uplo, char *diag, integer *n, real *ap, - real *rcond, real *work, integer *iwork, integer *info, - ftnlen norm_len, ftnlen uplo_len, ftnlen diag_len); -extern int stprfs_(char *uplo, char *trans, char *diag, integer *n, - integer *nrhs, real *ap, real *b, integer *ldb, real *x, - integer *ldx, real *ferr, real *berr, real *work, - integer *iwork, integer *info, ftnlen uplo_len, - ftnlen trans_len, ftnlen diag_len); -extern int stptri_(char *uplo, char *diag, integer *n, real *ap, integer *info, - ftnlen uplo_len, ftnlen diag_len); -extern int stptrs_(char *uplo, char *trans, char *diag, integer *n, - integer *nrhs, real *ap, real *b, integer *ldb, - integer *info, ftnlen uplo_len, ftnlen trans_len, - ftnlen diag_len); -extern int strcon_(char *norm, char *uplo, char *diag, integer *n, real *a, - integer *lda, real *rcond, real *work, integer *iwork, - integer *info, ftnlen norm_len, ftnlen uplo_len, - ftnlen diag_len); -extern int strevc_(char *side, char *howmny, logical *select, integer *n, - real *t, integer *ldt, real *vl, integer *ldvl, real *vr, - integer *ldvr, integer *mm, integer *m, real *work, - integer *info, ftnlen side_len, ftnlen howmny_len); -extern int strexc_(char *compq, integer *n, real *t, integer *ldt, real *q, - integer *ldq, integer *ifst, integer *ilst, real *work, - integer *info, ftnlen compq_len); -extern int strrfs_(char *uplo, char *trans, char *diag, integer *n, - integer *nrhs, real *a, integer *lda, real *b, integer *ldb, - real *x, integer *ldx, real *ferr, real *berr, real *work, - integer *iwork, integer *info, ftnlen uplo_len, - ftnlen trans_len, ftnlen diag_len); -extern int strsen_(char *job, char *compq, logical *select, integer *n, real *t, - integer *ldt, real *q, integer *ldq, real *wr, real *wi, - integer *m, real *s, real *sep, real *work, integer *lwork, - integer *iwork, integer *liwork, integer *info, - ftnlen job_len, ftnlen compq_len); -extern int strsna_(char *job, char *howmny, logical *select, integer *n, - real *t, integer *ldt, real *vl, integer *ldvl, real *vr, - integer *ldvr, real *s, real *sep, integer *mm, integer *m, - real *work, integer *ldwork, integer *iwork, integer *info, - ftnlen job_len, ftnlen howmny_len); -extern int strsyl_(char *trana, char *tranb, integer *isgn, integer *m, - integer *n, real *a, integer *lda, real *b, integer *ldb, - real *c__, integer *ldc, real *scale, integer *info, - ftnlen trana_len, ftnlen tranb_len); -extern int strti2_(char *uplo, char *diag, integer *n, real *a, integer *lda, - integer *info, ftnlen uplo_len, ftnlen diag_len); -extern int strtri_(char *uplo, char *diag, integer *n, real *a, integer *lda, - integer *info, ftnlen uplo_len, ftnlen diag_len); -extern int strtrs_(char *uplo, char *trans, char *diag, integer *n, - integer *nrhs, real *a, integer *lda, real *b, integer *ldb, - integer *info, ftnlen uplo_len, ftnlen trans_len, - ftnlen diag_len); -extern int stzrqf_(integer *m, integer *n, real *a, integer *lda, real *tau, - integer *info); -extern int stzrzf_(integer *m, integer *n, real *a, integer *lda, real *tau, - real *work, integer *lwork, integer *info); -extern int xerbla_(char *srname, integer *info, ftnlen srname_len); -extern int zbdsqr_(char *uplo, integer *n, integer *ncvt, integer *nru, - integer *ncc, doublereal *d__, doublereal *e, - doublecomplex *vt, integer *ldvt, doublecomplex *u, - integer *ldu, doublecomplex *c__, integer *ldc, - doublereal *rwork, integer *info, ftnlen uplo_len); -extern int zdrot_(integer *n, doublecomplex *cx, integer *incx, - doublecomplex *cy, integer *incy, doublereal *c__, - doublereal *s); -extern int zdrscl_(integer *n, doublereal *sa, doublecomplex *sx, - integer *incx); -extern int zgbbrd_(char *vect, integer *m, integer *n, integer *ncc, - integer *kl, integer *ku, doublecomplex *ab, integer *ldab, - doublereal *d__, doublereal *e, doublecomplex *q, - integer *ldq, doublecomplex *pt, integer *ldpt, - doublecomplex *c__, integer *ldc, doublecomplex *work, - doublereal *rwork, integer *info, ftnlen vect_len); -extern int zgbcon_(char *norm, integer *n, integer *kl, integer *ku, - doublecomplex *ab, integer *ldab, integer *ipiv, - doublereal *anorm, doublereal *rcond, doublecomplex *work, - doublereal *rwork, integer *info, ftnlen norm_len); -extern int zgbequ_(integer *m, integer *n, integer *kl, integer *ku, - doublecomplex *ab, integer *ldab, doublereal *r__, - doublereal *c__, doublereal *rowcnd, doublereal *colcnd, - doublereal *amax, integer *info); -extern int zgbrfs_(char *trans, integer *n, integer *kl, integer *ku, - integer *nrhs, doublecomplex *ab, integer *ldab, - doublecomplex *afb, integer *ldafb, integer *ipiv, - doublecomplex *b, integer *ldb, doublecomplex *x, - integer *ldx, doublereal *ferr, doublereal *berr, - doublecomplex *work, doublereal *rwork, integer *info, - ftnlen trans_len); -extern int zgbsv_(integer *n, integer *kl, integer *ku, integer *nrhs, - doublecomplex *ab, integer *ldab, integer *ipiv, - doublecomplex *b, integer *ldb, integer *info); -extern int zgbsvx_(char *fact, char *trans, integer *n, integer *kl, - integer *ku, integer *nrhs, doublecomplex *ab, integer *ldab, - doublecomplex *afb, integer *ldafb, integer *ipiv, - char *equed, doublereal *r__, doublereal *c__, - doublecomplex *b, integer *ldb, doublecomplex *x, - integer *ldx, doublereal *rcond, doublereal *ferr, - doublereal *berr, doublecomplex *work, doublereal *rwork, - integer *info, ftnlen fact_len, ftnlen trans_len, - ftnlen equed_len); -extern int zgbtf2_(integer *m, integer *n, integer *kl, integer *ku, - doublecomplex *ab, integer *ldab, integer *ipiv, - integer *info); -extern int zgbtrf_(integer *m, integer *n, integer *kl, integer *ku, - doublecomplex *ab, integer *ldab, integer *ipiv, - integer *info); -extern int zgbtrs_(char *trans, integer *n, integer *kl, integer *ku, - integer *nrhs, doublecomplex *ab, integer *ldab, - integer *ipiv, doublecomplex *b, integer *ldb, integer *info, - ftnlen trans_len); -extern int zgebak_(char *job, char *side, integer *n, integer *ilo, - integer *ihi, doublereal *scale, integer *m, - doublecomplex *v, integer *ldv, integer *info, - ftnlen job_len, ftnlen side_len); -extern int zgebal_(char *job, integer *n, doublecomplex *a, integer *lda, - integer *ilo, integer *ihi, doublereal *scale, integer *info, - ftnlen job_len); -extern int zgebd2_(integer *m, integer *n, doublecomplex *a, integer *lda, - doublereal *d__, doublereal *e, doublecomplex *tauq, - doublecomplex *taup, doublecomplex *work, integer *info); -extern int zgebrd_(integer *m, integer *n, doublecomplex *a, integer *lda, - doublereal *d__, doublereal *e, doublecomplex *tauq, - doublecomplex *taup, doublecomplex *work, integer *lwork, - integer *info); -extern int zgecon_(char *norm, integer *n, doublecomplex *a, integer *lda, - doublereal *anorm, doublereal *rcond, doublecomplex *work, - doublereal *rwork, integer *info, ftnlen norm_len); -extern int zgeequ_(integer *m, integer *n, doublecomplex *a, integer *lda, - doublereal *r__, doublereal *c__, doublereal *rowcnd, - doublereal *colcnd, doublereal *amax, integer *info); -extern int zgees_(char *jobvs, char *sort, L_fp select, integer *n, - doublecomplex *a, integer *lda, integer *sdim, - doublecomplex *w, doublecomplex *vs, integer *ldvs, - doublecomplex *work, integer *lwork, doublereal *rwork, - logical *bwork, integer *info, ftnlen jobvs_len, - ftnlen sort_len); -extern int zgeesx_(char *jobvs, char *sort, L_fp select, char *sense, - integer *n, doublecomplex *a, integer *lda, integer *sdim, - doublecomplex *w, doublecomplex *vs, integer *ldvs, - doublereal *rconde, doublereal *rcondv, doublecomplex *work, - integer *lwork, doublereal *rwork, logical *bwork, - integer *info, ftnlen jobvs_len, ftnlen sort_len, - ftnlen sense_len); -extern int zgeev_(char *jobvl, char *jobvr, integer *n, doublecomplex *a, - integer *lda, doublecomplex *w, doublecomplex *vl, - integer *ldvl, doublecomplex *vr, integer *ldvr, - doublecomplex *work, integer *lwork, doublereal *rwork, - integer *info, ftnlen jobvl_len, ftnlen jobvr_len); -extern int zgeevx_(char *balanc, char *jobvl, char *jobvr, char *sense, - integer *n, doublecomplex *a, integer *lda, doublecomplex *w, - doublecomplex *vl, integer *ldvl, doublecomplex *vr, - integer *ldvr, integer *ilo, integer *ihi, doublereal *scale, - doublereal *abnrm, doublereal *rconde, doublereal *rcondv, - doublecomplex *work, integer *lwork, doublereal *rwork, - integer *info, ftnlen balanc_len, ftnlen jobvl_len, - ftnlen jobvr_len, ftnlen sense_len); -extern int zgegs_(char *jobvsl, char *jobvsr, integer *n, doublecomplex *a, - integer *lda, doublecomplex *b, integer *ldb, - doublecomplex *alpha, doublecomplex *beta, doublecomplex *vsl, - integer *ldvsl, doublecomplex *vsr, integer *ldvsr, - doublecomplex *work, integer *lwork, doublereal *rwork, - integer *info, ftnlen jobvsl_len, ftnlen jobvsr_len); -extern int zgegv_(char *jobvl, char *jobvr, integer *n, doublecomplex *a, - integer *lda, doublecomplex *b, integer *ldb, - doublecomplex *alpha, doublecomplex *beta, doublecomplex *vl, - integer *ldvl, doublecomplex *vr, integer *ldvr, - doublecomplex *work, integer *lwork, doublereal *rwork, - integer *info, ftnlen jobvl_len, ftnlen jobvr_len); -extern int zgehd2_(integer *n, integer *ilo, integer *ihi, doublecomplex *a, - integer *lda, doublecomplex *tau, doublecomplex *work, - integer *info); -extern int zgehrd_(integer *n, integer *ilo, integer *ihi, doublecomplex *a, - integer *lda, doublecomplex *tau, doublecomplex *work, - integer *lwork, integer *info); -extern int zgelq2_(integer *m, integer *n, doublecomplex *a, integer *lda, - doublecomplex *tau, doublecomplex *work, integer *info); -extern int zgelqf_(integer *m, integer *n, doublecomplex *a, integer *lda, - doublecomplex *tau, doublecomplex *work, integer *lwork, - integer *info); -extern int zgels_(char *trans, integer *m, integer *n, integer *nrhs, - doublecomplex *a, integer *lda, doublecomplex *b, - integer *ldb, doublecomplex *work, integer *lwork, - integer *info, ftnlen trans_len); -extern int zgelsd_(integer *m, integer *n, integer *nrhs, doublecomplex *a, - integer *lda, doublecomplex *b, integer *ldb, doublereal *s, - doublereal *rcond, integer *rank, doublecomplex *work, - integer *lwork, doublereal *rwork, integer *iwork, - integer *info); -extern int zgelss_(integer *m, integer *n, integer *nrhs, doublecomplex *a, - integer *lda, doublecomplex *b, integer *ldb, doublereal *s, - doublereal *rcond, integer *rank, doublecomplex *work, - integer *lwork, doublereal *rwork, integer *info); -extern int zgelsx_(integer *m, integer *n, integer *nrhs, doublecomplex *a, - integer *lda, doublecomplex *b, integer *ldb, integer *jpvt, - doublereal *rcond, integer *rank, doublecomplex *work, - doublereal *rwork, integer *info); -extern int zgelsy_(integer *m, integer *n, integer *nrhs, doublecomplex *a, - integer *lda, doublecomplex *b, integer *ldb, integer *jpvt, - doublereal *rcond, integer *rank, doublecomplex *work, - integer *lwork, doublereal *rwork, integer *info); -extern int zgeql2_(integer *m, integer *n, doublecomplex *a, integer *lda, - doublecomplex *tau, doublecomplex *work, integer *info); -extern int zgeqlf_(integer *m, integer *n, doublecomplex *a, integer *lda, - doublecomplex *tau, doublecomplex *work, integer *lwork, - integer *info); -extern int zgeqp3_(integer *m, integer *n, doublecomplex *a, integer *lda, - integer *jpvt, doublecomplex *tau, doublecomplex *work, - integer *lwork, doublereal *rwork, integer *info); -extern int zgeqpf_(integer *m, integer *n, doublecomplex *a, integer *lda, - integer *jpvt, doublecomplex *tau, doublecomplex *work, - doublereal *rwork, integer *info); -extern int zgeqr2_(integer *m, integer *n, doublecomplex *a, integer *lda, - doublecomplex *tau, doublecomplex *work, integer *info); -extern int zgeqrf_(integer *m, integer *n, doublecomplex *a, integer *lda, - doublecomplex *tau, doublecomplex *work, integer *lwork, - integer *info); -extern int zgerfs_(char *trans, integer *n, integer *nrhs, doublecomplex *a, - integer *lda, doublecomplex *af, integer *ldaf, - integer *ipiv, doublecomplex *b, integer *ldb, - doublecomplex *x, integer *ldx, doublereal *ferr, - doublereal *berr, doublecomplex *work, doublereal *rwork, - integer *info, ftnlen trans_len); -extern int zgerq2_(integer *m, integer *n, doublecomplex *a, integer *lda, - doublecomplex *tau, doublecomplex *work, integer *info); -extern int zgerqf_(integer *m, integer *n, doublecomplex *a, integer *lda, - doublecomplex *tau, doublecomplex *work, integer *lwork, - integer *info); -extern int zgesc2_(integer *n, doublecomplex *a, integer *lda, - doublecomplex *rhs, integer *ipiv, integer *jpiv, - doublereal *scale); -extern int zgesdd_(char *jobz, integer *m, integer *n, doublecomplex *a, - integer *lda, doublereal *s, doublecomplex *u, integer *ldu, - doublecomplex *vt, integer *ldvt, doublecomplex *work, - integer *lwork, doublereal *rwork, integer *iwork, - integer *info, ftnlen jobz_len); -extern int zgesv_(integer *n, integer *nrhs, doublecomplex *a, integer *lda, - integer *ipiv, doublecomplex *b, integer *ldb, integer *info); -extern int zgesvd_(char *jobu, char *jobvt, integer *m, integer *n, - doublecomplex *a, integer *lda, doublereal *s, - doublecomplex *u, integer *ldu, doublecomplex *vt, - integer *ldvt, doublecomplex *work, integer *lwork, - doublereal *rwork, integer *info, ftnlen jobu_len, - ftnlen jobvt_len); -extern int zgesvx_(char *fact, char *trans, integer *n, integer *nrhs, - doublecomplex *a, integer *lda, doublecomplex *af, - integer *ldaf, integer *ipiv, char *equed, doublereal *r__, - doublereal *c__, doublecomplex *b, integer *ldb, - doublecomplex *x, integer *ldx, doublereal *rcond, - doublereal *ferr, doublereal *berr, doublecomplex *work, - doublereal *rwork, integer *info, ftnlen fact_len, - ftnlen trans_len, ftnlen equed_len); -extern int zgetc2_(integer *n, doublecomplex *a, integer *lda, integer *ipiv, - integer *jpiv, integer *info); -extern int zgetf2_(integer *m, integer *n, doublecomplex *a, integer *lda, - integer *ipiv, integer *info); -extern int zgetrf_(integer *m, integer *n, doublecomplex *a, integer *lda, - integer *ipiv, integer *info); -extern int zgetri_(integer *n, doublecomplex *a, integer *lda, integer *ipiv, - doublecomplex *work, integer *lwork, integer *info); -extern int zgetrs_(char *trans, integer *n, integer *nrhs, doublecomplex *a, - integer *lda, integer *ipiv, doublecomplex *b, integer *ldb, - integer *info, ftnlen trans_len); -extern int zggbak_(char *job, char *side, integer *n, integer *ilo, - integer *ihi, doublereal *lscale, doublereal *rscale, - integer *m, doublecomplex *v, integer *ldv, integer *info, - ftnlen job_len, ftnlen side_len); -extern int zggbal_(char *job, integer *n, doublecomplex *a, integer *lda, - doublecomplex *b, integer *ldb, integer *ilo, integer *ihi, - doublereal *lscale, doublereal *rscale, doublereal *work, - integer *info, ftnlen job_len); -extern int zgges_(char *jobvsl, char *jobvsr, char *sort, L_fp delctg, - integer *n, doublecomplex *a, integer *lda, doublecomplex *b, - integer *ldb, integer *sdim, doublecomplex *alpha, - doublecomplex *beta, doublecomplex *vsl, integer *ldvsl, - doublecomplex *vsr, integer *ldvsr, doublecomplex *work, - integer *lwork, doublereal *rwork, logical *bwork, - integer *info, ftnlen jobvsl_len, ftnlen jobvsr_len, - ftnlen sort_len); -extern int zggesx_(char *jobvsl, char *jobvsr, char *sort, L_fp delctg, - char *sense, integer *n, doublecomplex *a, integer *lda, - doublecomplex *b, integer *ldb, integer *sdim, - doublecomplex *alpha, doublecomplex *beta, - doublecomplex *vsl, integer *ldvsl, doublecomplex *vsr, - integer *ldvsr, doublereal *rconde, doublereal *rcondv, - doublecomplex *work, integer *lwork, doublereal *rwork, - integer *iwork, integer *liwork, logical *bwork, - integer *info, ftnlen jobvsl_len, ftnlen jobvsr_len, - ftnlen sort_len, ftnlen sense_len); -extern int zggev_(char *jobvl, char *jobvr, integer *n, doublecomplex *a, - integer *lda, doublecomplex *b, integer *ldb, - doublecomplex *alpha, doublecomplex *beta, doublecomplex *vl, - integer *ldvl, doublecomplex *vr, integer *ldvr, - doublecomplex *work, integer *lwork, doublereal *rwork, - integer *info, ftnlen jobvl_len, ftnlen jobvr_len); -extern int zggevx_(char *balanc, char *jobvl, char *jobvr, char *sense, - integer *n, doublecomplex *a, integer *lda, doublecomplex *b, - integer *ldb, doublecomplex *alpha, doublecomplex *beta, - doublecomplex *vl, integer *ldvl, doublecomplex *vr, - integer *ldvr, integer *ilo, integer *ihi, - doublereal *lscale, doublereal *rscale, doublereal *abnrm, - doublereal *bbnrm, doublereal *rconde, doublereal *rcondv, - doublecomplex *work, integer *lwork, doublereal *rwork, - integer *iwork, logical *bwork, integer *info, - ftnlen balanc_len, ftnlen jobvl_len, ftnlen jobvr_len, - ftnlen sense_len); -extern int zggglm_(integer *n, integer *m, integer *p, doublecomplex *a, - integer *lda, doublecomplex *b, integer *ldb, - doublecomplex *d__, doublecomplex *x, doublecomplex *y, - doublecomplex *work, integer *lwork, integer *info); -extern int zgghrd_(char *compq, char *compz, integer *n, integer *ilo, - integer *ihi, doublecomplex *a, integer *lda, - doublecomplex *b, integer *ldb, doublecomplex *q, - integer *ldq, doublecomplex *z__, integer *ldz, - integer *info, ftnlen compq_len, ftnlen compz_len); -extern int zgglse_(integer *m, integer *n, integer *p, doublecomplex *a, - integer *lda, doublecomplex *b, integer *ldb, - doublecomplex *c__, doublecomplex *d__, doublecomplex *x, - doublecomplex *work, integer *lwork, integer *info); -extern int zggqrf_(integer *n, integer *m, integer *p, doublecomplex *a, - integer *lda, doublecomplex *taua, doublecomplex *b, - integer *ldb, doublecomplex *taub, doublecomplex *work, - integer *lwork, integer *info); -extern int zggrqf_(integer *m, integer *p, integer *n, doublecomplex *a, - integer *lda, doublecomplex *taua, doublecomplex *b, - integer *ldb, doublecomplex *taub, doublecomplex *work, - integer *lwork, integer *info); -extern int zggsvd_(char *jobu, char *jobv, char *jobq, integer *m, integer *n, - integer *p, integer *k, integer *l, doublecomplex *a, - integer *lda, doublecomplex *b, integer *ldb, - doublereal *alpha, doublereal *beta, doublecomplex *u, - integer *ldu, doublecomplex *v, integer *ldv, - doublecomplex *q, integer *ldq, doublecomplex *work, - doublereal *rwork, integer *iwork, integer *info, - ftnlen jobu_len, ftnlen jobv_len, ftnlen jobq_len); -extern int zggsvp_(char *jobu, char *jobv, char *jobq, integer *m, integer *p, - integer *n, doublecomplex *a, integer *lda, doublecomplex *b, - integer *ldb, doublereal *tola, doublereal *tolb, integer *k, - integer *l, doublecomplex *u, integer *ldu, doublecomplex *v, - integer *ldv, doublecomplex *q, integer *ldq, integer *iwork, - doublereal *rwork, doublecomplex *tau, doublecomplex *work, - integer *info, ftnlen jobu_len, ftnlen jobv_len, - ftnlen jobq_len); -extern int zgtcon_(char *norm, integer *n, doublecomplex *dl, - doublecomplex *d__, doublecomplex *du, doublecomplex *du2, - integer *ipiv, doublereal *anorm, doublereal *rcond, - doublecomplex *work, integer *info, ftnlen norm_len); -extern int zgtrfs_(char *trans, integer *n, integer *nrhs, doublecomplex *dl, - doublecomplex *d__, doublecomplex *du, doublecomplex *dlf, - doublecomplex *df, doublecomplex *duf, doublecomplex *du2, - integer *ipiv, doublecomplex *b, integer *ldb, - doublecomplex *x, integer *ldx, doublereal *ferr, - doublereal *berr, doublecomplex *work, doublereal *rwork, - integer *info, ftnlen trans_len); -extern int zgtsv_(integer *n, integer *nrhs, doublecomplex *dl, - doublecomplex *d__, doublecomplex *du, doublecomplex *b, - integer *ldb, integer *info); -extern int zgtsvx_(char *fact, char *trans, integer *n, integer *nrhs, - doublecomplex *dl, doublecomplex *d__, doublecomplex *du, - doublecomplex *dlf, doublecomplex *df, doublecomplex *duf, - doublecomplex *du2, integer *ipiv, doublecomplex *b, - integer *ldb, doublecomplex *x, integer *ldx, - doublereal *rcond, doublereal *ferr, doublereal *berr, - doublecomplex *work, doublereal *rwork, integer *info, - ftnlen fact_len, ftnlen trans_len); -extern int zgttrf_(integer *n, doublecomplex *dl, doublecomplex *d__, - doublecomplex *du, doublecomplex *du2, integer *ipiv, - integer *info); -extern int zgttrs_(char *trans, integer *n, integer *nrhs, doublecomplex *dl, - doublecomplex *d__, doublecomplex *du, doublecomplex *du2, - integer *ipiv, doublecomplex *b, integer *ldb, integer *info, - ftnlen trans_len); -extern int zgtts2_(integer *itrans, integer *n, integer *nrhs, - doublecomplex *dl, doublecomplex *d__, doublecomplex *du, - doublecomplex *du2, integer *ipiv, doublecomplex *b, - integer *ldb); -extern int zhbev_(char *jobz, char *uplo, integer *n, integer *kd, - doublecomplex *ab, integer *ldab, doublereal *w, - doublecomplex *z__, integer *ldz, doublecomplex *work, - doublereal *rwork, integer *info, ftnlen jobz_len, - ftnlen uplo_len); -extern int zhbevd_(char *jobz, char *uplo, integer *n, integer *kd, - doublecomplex *ab, integer *ldab, doublereal *w, - doublecomplex *z__, integer *ldz, doublecomplex *work, - integer *lwork, doublereal *rwork, integer *lrwork, - integer *iwork, integer *liwork, integer *info, - ftnlen jobz_len, ftnlen uplo_len); -extern int zhbevx_(char *jobz, char *range, char *uplo, integer *n, integer *kd, - doublecomplex *ab, integer *ldab, doublecomplex *q, - integer *ldq, doublereal *vl, doublereal *vu, integer *il, - integer *iu, doublereal *abstol, integer *m, doublereal *w, - doublecomplex *z__, integer *ldz, doublecomplex *work, - doublereal *rwork, integer *iwork, integer *ifail, - integer *info, ftnlen jobz_len, ftnlen range_len, - ftnlen uplo_len); -extern int zhbgst_(char *vect, char *uplo, integer *n, integer *ka, integer *kb, - doublecomplex *ab, integer *ldab, doublecomplex *bb, - integer *ldbb, doublecomplex *x, integer *ldx, - doublecomplex *work, doublereal *rwork, integer *info, - ftnlen vect_len, ftnlen uplo_len); -extern int zhbgv_(char *jobz, char *uplo, integer *n, integer *ka, integer *kb, - doublecomplex *ab, integer *ldab, doublecomplex *bb, - integer *ldbb, doublereal *w, doublecomplex *z__, - integer *ldz, doublecomplex *work, doublereal *rwork, - integer *info, ftnlen jobz_len, ftnlen uplo_len); -extern int zhbgvd_(char *jobz, char *uplo, integer *n, integer *ka, integer *kb, - doublecomplex *ab, integer *ldab, doublecomplex *bb, - integer *ldbb, doublereal *w, doublecomplex *z__, - integer *ldz, doublecomplex *work, integer *lwork, - doublereal *rwork, integer *lrwork, integer *iwork, - integer *liwork, integer *info, ftnlen jobz_len, - ftnlen uplo_len); -extern int zhbgvx_(char *jobz, char *range, char *uplo, integer *n, integer *ka, - integer *kb, doublecomplex *ab, integer *ldab, - doublecomplex *bb, integer *ldbb, doublecomplex *q, - integer *ldq, doublereal *vl, doublereal *vu, integer *il, - integer *iu, doublereal *abstol, integer *m, doublereal *w, - doublecomplex *z__, integer *ldz, doublecomplex *work, - doublereal *rwork, integer *iwork, integer *ifail, - integer *info, ftnlen jobz_len, ftnlen range_len, - ftnlen uplo_len); -extern int zhbtrd_(char *vect, char *uplo, integer *n, integer *kd, - doublecomplex *ab, integer *ldab, doublereal *d__, - doublereal *e, doublecomplex *q, integer *ldq, - doublecomplex *work, integer *info, ftnlen vect_len, - ftnlen uplo_len); -extern int zhecon_(char *uplo, integer *n, doublecomplex *a, integer *lda, - integer *ipiv, doublereal *anorm, doublereal *rcond, - doublecomplex *work, integer *info, ftnlen uplo_len); -extern int zheev_(char *jobz, char *uplo, integer *n, doublecomplex *a, - integer *lda, doublereal *w, doublecomplex *work, - integer *lwork, doublereal *rwork, integer *info, - ftnlen jobz_len, ftnlen uplo_len); -extern int zheevd_(char *jobz, char *uplo, integer *n, doublecomplex *a, - integer *lda, doublereal *w, doublecomplex *work, - integer *lwork, doublereal *rwork, integer *lrwork, - integer *iwork, integer *liwork, integer *info, - ftnlen jobz_len, ftnlen uplo_len); -extern int zheevr_(char *jobz, char *range, char *uplo, integer *n, - doublecomplex *a, integer *lda, doublereal *vl, - doublereal *vu, integer *il, integer *iu, doublereal *abstol, - integer *m, doublereal *w, doublecomplex *z__, integer *ldz, - integer *isuppz, doublecomplex *work, integer *lwork, - doublereal *rwork, integer *lrwork, integer *iwork, - integer *liwork, integer *info, ftnlen jobz_len, - ftnlen range_len, ftnlen uplo_len); -extern int zheevx_(char *jobz, char *range, char *uplo, integer *n, - doublecomplex *a, integer *lda, doublereal *vl, - doublereal *vu, integer *il, integer *iu, doublereal *abstol, - integer *m, doublereal *w, doublecomplex *z__, integer *ldz, - doublecomplex *work, integer *lwork, doublereal *rwork, - integer *iwork, integer *ifail, integer *info, - ftnlen jobz_len, ftnlen range_len, ftnlen uplo_len); -extern int zhegs2_(integer *itype, char *uplo, integer *n, doublecomplex *a, - integer *lda, doublecomplex *b, integer *ldb, integer *info, - ftnlen uplo_len); -extern int zhegst_(integer *itype, char *uplo, integer *n, doublecomplex *a, - integer *lda, doublecomplex *b, integer *ldb, integer *info, - ftnlen uplo_len); -extern int zhegv_(integer *itype, char *jobz, char *uplo, integer *n, - doublecomplex *a, integer *lda, doublecomplex *b, - integer *ldb, doublereal *w, doublecomplex *work, - integer *lwork, doublereal *rwork, integer *info, - ftnlen jobz_len, ftnlen uplo_len); -extern int zhegvd_(integer *itype, char *jobz, char *uplo, integer *n, - doublecomplex *a, integer *lda, doublecomplex *b, - integer *ldb, doublereal *w, doublecomplex *work, - integer *lwork, doublereal *rwork, integer *lrwork, - integer *iwork, integer *liwork, integer *info, - ftnlen jobz_len, ftnlen uplo_len); -extern int zhegvx_(integer *itype, char *jobz, char *range, char *uplo, - integer *n, doublecomplex *a, integer *lda, doublecomplex *b, - integer *ldb, doublereal *vl, doublereal *vu, integer *il, - integer *iu, doublereal *abstol, integer *m, doublereal *w, - doublecomplex *z__, integer *ldz, doublecomplex *work, - integer *lwork, doublereal *rwork, integer *iwork, - integer *ifail, integer *info, ftnlen jobz_len, - ftnlen range_len, ftnlen uplo_len); -extern int zherfs_(char *uplo, integer *n, integer *nrhs, doublecomplex *a, - integer *lda, doublecomplex *af, integer *ldaf, - integer *ipiv, doublecomplex *b, integer *ldb, - doublecomplex *x, integer *ldx, doublereal *ferr, - doublereal *berr, doublecomplex *work, doublereal *rwork, - integer *info, ftnlen uplo_len); -extern int zhesv_(char *uplo, integer *n, integer *nrhs, doublecomplex *a, - integer *lda, integer *ipiv, doublecomplex *b, integer *ldb, - doublecomplex *work, integer *lwork, integer *info, - ftnlen uplo_len); -extern int zhesvx_(char *fact, char *uplo, integer *n, integer *nrhs, - doublecomplex *a, integer *lda, doublecomplex *af, - integer *ldaf, integer *ipiv, doublecomplex *b, integer *ldb, - doublecomplex *x, integer *ldx, doublereal *rcond, - doublereal *ferr, doublereal *berr, doublecomplex *work, - integer *lwork, doublereal *rwork, integer *info, - ftnlen fact_len, ftnlen uplo_len); -extern int zhetd2_(char *uplo, integer *n, doublecomplex *a, integer *lda, - doublereal *d__, doublereal *e, doublecomplex *tau, - integer *info, ftnlen uplo_len); -extern int zhetf2_(char *uplo, integer *n, doublecomplex *a, integer *lda, - integer *ipiv, integer *info, ftnlen uplo_len); -extern int zhetrd_(char *uplo, integer *n, doublecomplex *a, integer *lda, - doublereal *d__, doublereal *e, doublecomplex *tau, - doublecomplex *work, integer *lwork, integer *info, - ftnlen uplo_len); -extern int zhetrf_(char *uplo, integer *n, doublecomplex *a, integer *lda, - integer *ipiv, doublecomplex *work, integer *lwork, - integer *info, ftnlen uplo_len); -extern int zhetri_(char *uplo, integer *n, doublecomplex *a, integer *lda, - integer *ipiv, doublecomplex *work, integer *info, - ftnlen uplo_len); -extern int zhetrs_(char *uplo, integer *n, integer *nrhs, doublecomplex *a, - integer *lda, integer *ipiv, doublecomplex *b, integer *ldb, - integer *info, ftnlen uplo_len); -extern int zhgeqz_(char *job, char *compq, char *compz, integer *n, - integer *ilo, integer *ihi, doublecomplex *a, integer *lda, - doublecomplex *b, integer *ldb, doublecomplex *alpha, - doublecomplex *beta, doublecomplex *q, integer *ldq, - doublecomplex *z__, integer *ldz, doublecomplex *work, - integer *lwork, doublereal *rwork, integer *info, - ftnlen job_len, ftnlen compq_len, ftnlen compz_len); -extern int zhpcon_(char *uplo, integer *n, doublecomplex *ap, integer *ipiv, - doublereal *anorm, doublereal *rcond, doublecomplex *work, - integer *info, ftnlen uplo_len); -extern int zhpev_(char *jobz, char *uplo, integer *n, doublecomplex *ap, - doublereal *w, doublecomplex *z__, integer *ldz, - doublecomplex *work, doublereal *rwork, integer *info, - ftnlen jobz_len, ftnlen uplo_len); -extern int zhpevd_(char *jobz, char *uplo, integer *n, doublecomplex *ap, - doublereal *w, doublecomplex *z__, integer *ldz, - doublecomplex *work, integer *lwork, doublereal *rwork, - integer *lrwork, integer *iwork, integer *liwork, - integer *info, ftnlen jobz_len, ftnlen uplo_len); -extern int zhpevx_(char *jobz, char *range, char *uplo, integer *n, - doublecomplex *ap, doublereal *vl, doublereal *vu, - integer *il, integer *iu, doublereal *abstol, integer *m, - doublereal *w, doublecomplex *z__, integer *ldz, - doublecomplex *work, doublereal *rwork, integer *iwork, - integer *ifail, integer *info, ftnlen jobz_len, - ftnlen range_len, ftnlen uplo_len); -extern int zhpgst_(integer *itype, char *uplo, integer *n, doublecomplex *ap, - doublecomplex *bp, integer *info, ftnlen uplo_len); -extern int zhpgv_(integer *itype, char *jobz, char *uplo, integer *n, - doublecomplex *ap, doublecomplex *bp, doublereal *w, - doublecomplex *z__, integer *ldz, doublecomplex *work, - doublereal *rwork, integer *info, ftnlen jobz_len, - ftnlen uplo_len); -extern int zhpgvd_(integer *itype, char *jobz, char *uplo, integer *n, - doublecomplex *ap, doublecomplex *bp, doublereal *w, - doublecomplex *z__, integer *ldz, doublecomplex *work, - integer *lwork, doublereal *rwork, integer *lrwork, - integer *iwork, integer *liwork, integer *info, - ftnlen jobz_len, ftnlen uplo_len); -extern int zhpgvx_(integer *itype, char *jobz, char *range, char *uplo, - integer *n, doublecomplex *ap, doublecomplex *bp, - doublereal *vl, doublereal *vu, integer *il, integer *iu, - doublereal *abstol, integer *m, doublereal *w, - doublecomplex *z__, integer *ldz, doublecomplex *work, - doublereal *rwork, integer *iwork, integer *ifail, - integer *info, ftnlen jobz_len, ftnlen range_len, - ftnlen uplo_len); -extern int zhprfs_(char *uplo, integer *n, integer *nrhs, doublecomplex *ap, - doublecomplex *afp, integer *ipiv, doublecomplex *b, - integer *ldb, doublecomplex *x, integer *ldx, - doublereal *ferr, doublereal *berr, doublecomplex *work, - doublereal *rwork, integer *info, ftnlen uplo_len); -extern int zhpsv_(char *uplo, integer *n, integer *nrhs, doublecomplex *ap, - integer *ipiv, doublecomplex *b, integer *ldb, integer *info, - ftnlen uplo_len); -extern int zhpsvx_(char *fact, char *uplo, integer *n, integer *nrhs, - doublecomplex *ap, doublecomplex *afp, integer *ipiv, - doublecomplex *b, integer *ldb, doublecomplex *x, - integer *ldx, doublereal *rcond, doublereal *ferr, - doublereal *berr, doublecomplex *work, doublereal *rwork, - integer *info, ftnlen fact_len, ftnlen uplo_len); -extern int zhptrd_(char *uplo, integer *n, doublecomplex *ap, doublereal *d__, - doublereal *e, doublecomplex *tau, integer *info, - ftnlen uplo_len); -extern int zhptrf_(char *uplo, integer *n, doublecomplex *ap, integer *ipiv, - integer *info, ftnlen uplo_len); -extern int zhptri_(char *uplo, integer *n, doublecomplex *ap, integer *ipiv, - doublecomplex *work, integer *info, ftnlen uplo_len); -extern int zhptrs_(char *uplo, integer *n, integer *nrhs, doublecomplex *ap, - integer *ipiv, doublecomplex *b, integer *ldb, integer *info, - ftnlen uplo_len); -extern int zhsein_(char *side, char *eigsrc, char *initv, logical *select, - integer *n, doublecomplex *h__, integer *ldh, - doublecomplex *w, doublecomplex *vl, integer *ldvl, - doublecomplex *vr, integer *ldvr, integer *mm, integer *m, - doublecomplex *work, doublereal *rwork, integer *ifaill, - integer *ifailr, integer *info, ftnlen side_len, - ftnlen eigsrc_len, ftnlen initv_len); -extern int zhseqr_(char *job, char *compz, integer *n, integer *ilo, - integer *ihi, doublecomplex *h__, integer *ldh, - doublecomplex *w, doublecomplex *z__, integer *ldz, - doublecomplex *work, integer *lwork, integer *info, - ftnlen job_len, ftnlen compz_len); -extern int zlabrd_(integer *m, integer *n, integer *nb, doublecomplex *a, - integer *lda, doublereal *d__, doublereal *e, - doublecomplex *tauq, doublecomplex *taup, doublecomplex *x, - integer *ldx, doublecomplex *y, integer *ldy); -extern int zlacgv_(integer *n, doublecomplex *x, integer *incx); -extern int zlacon_(integer *n, doublecomplex *v, doublecomplex *x, - doublereal *est, integer *kase); -extern int zlacp2_(char *uplo, integer *m, integer *n, doublereal *a, - integer *lda, doublecomplex *b, integer *ldb, - ftnlen uplo_len); -extern int zlacpy_(char *uplo, integer *m, integer *n, doublecomplex *a, - integer *lda, doublecomplex *b, integer *ldb, - ftnlen uplo_len); -extern int zlacrm_(integer *m, integer *n, doublecomplex *a, integer *lda, - doublereal *b, integer *ldb, doublecomplex *c__, - integer *ldc, doublereal *rwork); -extern int zlacrt_(integer *n, doublecomplex *cx, integer *incx, - doublecomplex *cy, integer *incy, doublecomplex *c__, - doublecomplex *s); -extern Z_f zladiv_(doublecomplex *ret_val, doublecomplex *x, doublecomplex *y); -extern int zlaed0_(integer *qsiz, integer *n, doublereal *d__, doublereal *e, - doublecomplex *q, integer *ldq, doublecomplex *qstore, - integer *ldqs, doublereal *rwork, integer *iwork, - integer *info); -extern int zlaed7_(integer *n, integer *cutpnt, integer *qsiz, integer *tlvls, - integer *curlvl, integer *curpbm, doublereal *d__, - doublecomplex *q, integer *ldq, doublereal *rho, - integer *indxq, doublereal *qstore, integer *qptr, - integer *prmptr, integer *perm, integer *givptr, - integer *givcol, doublereal *givnum, doublecomplex *work, - doublereal *rwork, integer *iwork, integer *info); -extern int zlaed8_(integer *k, integer *n, integer *qsiz, doublecomplex *q, - integer *ldq, doublereal *d__, doublereal *rho, - integer *cutpnt, doublereal *z__, doublereal *dlamda, - doublecomplex *q2, integer *ldq2, doublereal *w, - integer *indxp, integer *indx, integer *indxq, integer *perm, - integer *givptr, integer *givcol, doublereal *givnum, - integer *info); -extern int zlaein_(logical *rightv, logical *noinit, integer *n, - doublecomplex *h__, integer *ldh, doublecomplex *w, - doublecomplex *v, doublecomplex *b, integer *ldb, - doublereal *rwork, doublereal *eps3, doublereal *smlnum, - integer *info); -extern int zlaesy_(doublecomplex *a, doublecomplex *b, doublecomplex *c__, - doublecomplex *rt1, doublecomplex *rt2, - doublecomplex *evscal, doublecomplex *cs1, - doublecomplex *sn1); -extern int zlaev2_(doublecomplex *a, doublecomplex *b, doublecomplex *c__, - doublereal *rt1, doublereal *rt2, doublereal *cs1, - doublecomplex *sn1); -extern int zlags2_(logical *upper, doublereal *a1, doublecomplex *a2, - doublereal *a3, doublereal *b1, doublecomplex *b2, - doublereal *b3, doublereal *csu, doublecomplex *snu, - doublereal *csv, doublecomplex *snv, doublereal *csq, - doublecomplex *snq); -extern int zlagtm_(char *trans, integer *n, integer *nrhs, doublereal *alpha, - doublecomplex *dl, doublecomplex *d__, doublecomplex *du, - doublecomplex *x, integer *ldx, doublereal *beta, - doublecomplex *b, integer *ldb, ftnlen trans_len); -extern int zlahef_(char *uplo, integer *n, integer *nb, integer *kb, - doublecomplex *a, integer *lda, integer *ipiv, - doublecomplex *w, integer *ldw, integer *info, - ftnlen uplo_len); -extern int zlahqr_(logical *wantt, logical *wantz, integer *n, integer *ilo, - integer *ihi, doublecomplex *h__, integer *ldh, - doublecomplex *w, integer *iloz, integer *ihiz, - doublecomplex *z__, integer *ldz, integer *info); -extern int zlahrd_(integer *n, integer *k, integer *nb, doublecomplex *a, - integer *lda, doublecomplex *tau, doublecomplex *t, - integer *ldt, doublecomplex *y, integer *ldy); -extern int zlaic1_(integer *job, integer *j, doublecomplex *x, doublereal *sest, - doublecomplex *w, doublecomplex *gamma, doublereal *sestpr, - doublecomplex *s, doublecomplex *c__); -extern int zlals0_(integer *icompq, integer *nl, integer *nr, integer *sqre, - integer *nrhs, doublecomplex *b, integer *ldb, - doublecomplex *bx, integer *ldbx, integer *perm, - integer *givptr, integer *givcol, integer *ldgcol, - doublereal *givnum, integer *ldgnum, doublereal *poles, - doublereal *difl, doublereal *difr, doublereal *z__, - integer *k, doublereal *c__, doublereal *s, - doublereal *rwork, integer *info); -extern int zlalsa_(integer *icompq, integer *smlsiz, integer *n, integer *nrhs, - doublecomplex *b, integer *ldb, doublecomplex *bx, - integer *ldbx, doublereal *u, integer *ldu, doublereal *vt, - integer *k, doublereal *difl, doublereal *difr, - doublereal *z__, doublereal *poles, integer *givptr, - integer *givcol, integer *ldgcol, integer *perm, - doublereal *givnum, doublereal *c__, doublereal *s, - doublereal *rwork, integer *iwork, integer *info); -extern int zlalsd_(char *uplo, integer *smlsiz, integer *n, integer *nrhs, - doublereal *d__, doublereal *e, doublecomplex *b, - integer *ldb, doublereal *rcond, integer *rank, - doublecomplex *work, doublereal *rwork, integer *iwork, - integer *info, ftnlen uplo_len); -extern doublereal zlangb_(char *norm, integer *n, integer *kl, integer *ku, - doublecomplex *ab, integer *ldab, doublereal *work, - ftnlen norm_len); -extern doublereal zlange_(char *norm, integer *m, integer *n, doublecomplex *a, - integer *lda, doublereal *work, ftnlen norm_len); -extern doublereal zlangt_(char *norm, integer *n, doublecomplex *dl, - doublecomplex *d__, doublecomplex *du, - ftnlen norm_len); -extern doublereal zlanhb_(char *norm, char *uplo, integer *n, integer *k, - doublecomplex *ab, integer *ldab, doublereal *work, - ftnlen norm_len, ftnlen uplo_len); -extern doublereal zlanhe_(char *norm, char *uplo, integer *n, doublecomplex *a, - integer *lda, doublereal *work, ftnlen norm_len, - ftnlen uplo_len); -extern doublereal zlanhp_(char *norm, char *uplo, integer *n, doublecomplex *ap, - doublereal *work, ftnlen norm_len, ftnlen uplo_len); -extern doublereal zlanhs_(char *norm, integer *n, doublecomplex *a, - integer *lda, doublereal *work, ftnlen norm_len); -extern doublereal zlanht_(char *norm, integer *n, doublereal *d__, - doublecomplex *e, ftnlen norm_len); -extern doublereal zlansb_(char *norm, char *uplo, integer *n, integer *k, - doublecomplex *ab, integer *ldab, doublereal *work, - ftnlen norm_len, ftnlen uplo_len); -extern doublereal zlansp_(char *norm, char *uplo, integer *n, doublecomplex *ap, - doublereal *work, ftnlen norm_len, ftnlen uplo_len); -extern doublereal zlansy_(char *norm, char *uplo, integer *n, doublecomplex *a, - integer *lda, doublereal *work, ftnlen norm_len, - ftnlen uplo_len); -extern doublereal zlantb_(char *norm, char *uplo, char *diag, integer *n, - integer *k, doublecomplex *ab, integer *ldab, - doublereal *work, ftnlen norm_len, ftnlen uplo_len, - ftnlen diag_len); -extern doublereal zlantp_(char *norm, char *uplo, char *diag, integer *n, - doublecomplex *ap, doublereal *work, ftnlen norm_len, - ftnlen uplo_len, ftnlen diag_len); -extern doublereal zlantr_(char *norm, char *uplo, char *diag, integer *m, - integer *n, doublecomplex *a, integer *lda, - doublereal *work, ftnlen norm_len, ftnlen uplo_len, - ftnlen diag_len); -extern int zlapll_(integer *n, doublecomplex *x, integer *incx, - doublecomplex *y, integer *incy, doublereal *ssmin); -extern int zlapmt_(logical *forwrd, integer *m, integer *n, doublecomplex *x, - integer *ldx, integer *k); -extern int zlaqgb_(integer *m, integer *n, integer *kl, integer *ku, - doublecomplex *ab, integer *ldab, doublereal *r__, - doublereal *c__, doublereal *rowcnd, doublereal *colcnd, - doublereal *amax, char *equed, ftnlen equed_len); -extern int zlaqge_(integer *m, integer *n, doublecomplex *a, integer *lda, - doublereal *r__, doublereal *c__, doublereal *rowcnd, - doublereal *colcnd, doublereal *amax, char *equed, - ftnlen equed_len); -extern int zlaqhb_(char *uplo, integer *n, integer *kd, doublecomplex *ab, - integer *ldab, doublereal *s, doublereal *scond, - doublereal *amax, char *equed, ftnlen uplo_len, - ftnlen equed_len); -extern int zlaqhe_(char *uplo, integer *n, doublecomplex *a, integer *lda, - doublereal *s, doublereal *scond, doublereal *amax, - char *equed, ftnlen uplo_len, ftnlen equed_len); -extern int zlaqhp_(char *uplo, integer *n, doublecomplex *ap, doublereal *s, - doublereal *scond, doublereal *amax, char *equed, - ftnlen uplo_len, ftnlen equed_len); -extern int zlaqp2_(integer *m, integer *n, integer *offset, doublecomplex *a, - integer *lda, integer *jpvt, doublecomplex *tau, - doublereal *vn1, doublereal *vn2, doublecomplex *work); -extern int zlaqps_(integer *m, integer *n, integer *offset, integer *nb, - integer *kb, doublecomplex *a, integer *lda, integer *jpvt, - doublecomplex *tau, doublereal *vn1, doublereal *vn2, - doublecomplex *auxv, doublecomplex *f, integer *ldf); -extern int zlaqsb_(char *uplo, integer *n, integer *kd, doublecomplex *ab, - integer *ldab, doublereal *s, doublereal *scond, - doublereal *amax, char *equed, ftnlen uplo_len, - ftnlen equed_len); -extern int zlaqsp_(char *uplo, integer *n, doublecomplex *ap, doublereal *s, - doublereal *scond, doublereal *amax, char *equed, - ftnlen uplo_len, ftnlen equed_len); -extern int zlaqsy_(char *uplo, integer *n, doublecomplex *a, integer *lda, - doublereal *s, doublereal *scond, doublereal *amax, - char *equed, ftnlen uplo_len, ftnlen equed_len); -extern int zlar1v_(integer *n, integer *b1, integer *bn, doublereal *sigma, - doublereal *d__, doublereal *l, doublereal *ld, - doublereal *lld, doublereal *gersch, doublecomplex *z__, - doublereal *ztz, doublereal *mingma, integer *r__, - integer *isuppz, doublereal *work); -extern int zlar2v_(integer *n, doublecomplex *x, doublecomplex *y, - doublecomplex *z__, integer *incx, doublereal *c__, - doublecomplex *s, integer *incc); -extern int zlarcm_(integer *m, integer *n, doublereal *a, integer *lda, - doublecomplex *b, integer *ldb, doublecomplex *c__, - integer *ldc, doublereal *rwork); -extern int zlarf_(char *side, integer *m, integer *n, doublecomplex *v, - integer *incv, doublecomplex *tau, doublecomplex *c__, - integer *ldc, doublecomplex *work, ftnlen side_len); -extern int zlarfb_(char *side, char *trans, char *direct, char *storev, - integer *m, integer *n, integer *k, doublecomplex *v, - integer *ldv, doublecomplex *t, integer *ldt, - doublecomplex *c__, integer *ldc, doublecomplex *work, - integer *ldwork, ftnlen side_len, ftnlen trans_len, - ftnlen direct_len, ftnlen storev_len); -extern int zlarfg_(integer *n, doublecomplex *alpha, doublecomplex *x, - integer *incx, doublecomplex *tau); -extern int zlarft_(char *direct, char *storev, integer *n, integer *k, - doublecomplex *v, integer *ldv, doublecomplex *tau, - doublecomplex *t, integer *ldt, ftnlen direct_len, - ftnlen storev_len); -extern int zlarfx_(char *side, integer *m, integer *n, doublecomplex *v, - doublecomplex *tau, doublecomplex *c__, integer *ldc, - doublecomplex *work, ftnlen side_len); -extern int zlargv_(integer *n, doublecomplex *x, integer *incx, - doublecomplex *y, integer *incy, doublereal *c__, - integer *incc); -extern int zlarnv_(integer *idist, integer *iseed, integer *n, - doublecomplex *x); -extern int zlarrv_(integer *n, doublereal *d__, doublereal *l, integer *isplit, - integer *m, doublereal *w, integer *iblock, - doublereal *gersch, doublereal *tol, doublecomplex *z__, - integer *ldz, integer *isuppz, doublereal *work, - integer *iwork, integer *info); -extern int zlartg_(doublecomplex *f, doublecomplex *g, doublereal *cs, - doublecomplex *sn, doublecomplex *r__); -extern int zlartv_(integer *n, doublecomplex *x, integer *incx, - doublecomplex *y, integer *incy, doublereal *c__, - doublecomplex *s, integer *incc); -extern int zlarz_(char *side, integer *m, integer *n, integer *l, - doublecomplex *v, integer *incv, doublecomplex *tau, - doublecomplex *c__, integer *ldc, doublecomplex *work, - ftnlen side_len); -extern int zlarzb_(char *side, char *trans, char *direct, char *storev, - integer *m, integer *n, integer *k, integer *l, - doublecomplex *v, integer *ldv, doublecomplex *t, - integer *ldt, doublecomplex *c__, integer *ldc, - doublecomplex *work, integer *ldwork, ftnlen side_len, - ftnlen trans_len, ftnlen direct_len, ftnlen storev_len); -extern int zlarzt_(char *direct, char *storev, integer *n, integer *k, - doublecomplex *v, integer *ldv, doublecomplex *tau, - doublecomplex *t, integer *ldt, ftnlen direct_len, - ftnlen storev_len); -extern int zlascl_(char *type__, integer *kl, integer *ku, doublereal *cfrom, - doublereal *cto, integer *m, integer *n, doublecomplex *a, - integer *lda, integer *info, ftnlen type_len); -extern int zlaset_(char *uplo, integer *m, integer *n, doublecomplex *alpha, - doublecomplex *beta, doublecomplex *a, integer *lda, - ftnlen uplo_len); -extern int zlasr_(char *side, char *pivot, char *direct, integer *m, integer *n, - doublereal *c__, doublereal *s, doublecomplex *a, - integer *lda, ftnlen side_len, ftnlen pivot_len, - ftnlen direct_len); -extern int zlassq_(integer *n, doublecomplex *x, integer *incx, - doublereal *scale, doublereal *sumsq); -extern int zlaswp_(integer *n, doublecomplex *a, integer *lda, integer *k1, - integer *k2, integer *ipiv, integer *incx); -extern int zlasyf_(char *uplo, integer *n, integer *nb, integer *kb, - doublecomplex *a, integer *lda, integer *ipiv, - doublecomplex *w, integer *ldw, integer *info, - ftnlen uplo_len); -extern int zlatbs_(char *uplo, char *trans, char *diag, char *normin, - integer *n, integer *kd, doublecomplex *ab, integer *ldab, - doublecomplex *x, doublereal *scale, doublereal *cnorm, - integer *info, ftnlen uplo_len, ftnlen trans_len, - ftnlen diag_len, ftnlen normin_len); -extern int zlatdf_(integer *ijob, integer *n, doublecomplex *z__, integer *ldz, - doublecomplex *rhs, doublereal *rdsum, doublereal *rdscal, - integer *ipiv, integer *jpiv); -extern int zlatps_(char *uplo, char *trans, char *diag, char *normin, - integer *n, doublecomplex *ap, doublecomplex *x, - doublereal *scale, doublereal *cnorm, integer *info, - ftnlen uplo_len, ftnlen trans_len, ftnlen diag_len, - ftnlen normin_len); -extern int zlatrd_(char *uplo, integer *n, integer *nb, doublecomplex *a, - integer *lda, doublereal *e, doublecomplex *tau, - doublecomplex *w, integer *ldw, ftnlen uplo_len); -extern int zlatrs_(char *uplo, char *trans, char *diag, char *normin, - integer *n, doublecomplex *a, integer *lda, doublecomplex *x, - doublereal *scale, doublereal *cnorm, integer *info, - ftnlen uplo_len, ftnlen trans_len, ftnlen diag_len, - ftnlen normin_len); -extern int zlatrz_(integer *m, integer *n, integer *l, doublecomplex *a, - integer *lda, doublecomplex *tau, doublecomplex *work); -extern int zlatzm_(char *side, integer *m, integer *n, doublecomplex *v, - integer *incv, doublecomplex *tau, doublecomplex *c1, - doublecomplex *c2, integer *ldc, doublecomplex *work, - ftnlen side_len); -extern int zlauu2_(char *uplo, integer *n, doublecomplex *a, integer *lda, - integer *info, ftnlen uplo_len); -extern int zlauum_(char *uplo, integer *n, doublecomplex *a, integer *lda, - integer *info, ftnlen uplo_len); -extern int zpbcon_(char *uplo, integer *n, integer *kd, doublecomplex *ab, - integer *ldab, doublereal *anorm, doublereal *rcond, - doublecomplex *work, doublereal *rwork, integer *info, - ftnlen uplo_len); -extern int zpbequ_(char *uplo, integer *n, integer *kd, doublecomplex *ab, - integer *ldab, doublereal *s, doublereal *scond, - doublereal *amax, integer *info, ftnlen uplo_len); -extern int zpbrfs_(char *uplo, integer *n, integer *kd, integer *nrhs, - doublecomplex *ab, integer *ldab, doublecomplex *afb, - integer *ldafb, doublecomplex *b, integer *ldb, - doublecomplex *x, integer *ldx, doublereal *ferr, - doublereal *berr, doublecomplex *work, doublereal *rwork, - integer *info, ftnlen uplo_len); -extern int zpbstf_(char *uplo, integer *n, integer *kd, doublecomplex *ab, - integer *ldab, integer *info, ftnlen uplo_len); -extern int zpbsv_(char *uplo, integer *n, integer *kd, integer *nrhs, - doublecomplex *ab, integer *ldab, doublecomplex *b, - integer *ldb, integer *info, ftnlen uplo_len); -extern int zpbsvx_(char *fact, char *uplo, integer *n, integer *kd, - integer *nrhs, doublecomplex *ab, integer *ldab, - doublecomplex *afb, integer *ldafb, char *equed, - doublereal *s, doublecomplex *b, integer *ldb, - doublecomplex *x, integer *ldx, doublereal *rcond, - doublereal *ferr, doublereal *berr, doublecomplex *work, - doublereal *rwork, integer *info, ftnlen fact_len, - ftnlen uplo_len, ftnlen equed_len); -extern int zpbtf2_(char *uplo, integer *n, integer *kd, doublecomplex *ab, - integer *ldab, integer *info, ftnlen uplo_len); -extern int zpbtrf_(char *uplo, integer *n, integer *kd, doublecomplex *ab, - integer *ldab, integer *info, ftnlen uplo_len); -extern int zpbtrs_(char *uplo, integer *n, integer *kd, integer *nrhs, - doublecomplex *ab, integer *ldab, doublecomplex *b, - integer *ldb, integer *info, ftnlen uplo_len); -extern int zpocon_(char *uplo, integer *n, doublecomplex *a, integer *lda, - doublereal *anorm, doublereal *rcond, doublecomplex *work, - doublereal *rwork, integer *info, ftnlen uplo_len); -extern int zpoequ_(integer *n, doublecomplex *a, integer *lda, doublereal *s, - doublereal *scond, doublereal *amax, integer *info); -extern int zporfs_(char *uplo, integer *n, integer *nrhs, doublecomplex *a, - integer *lda, doublecomplex *af, integer *ldaf, - doublecomplex *b, integer *ldb, doublecomplex *x, - integer *ldx, doublereal *ferr, doublereal *berr, - doublecomplex *work, doublereal *rwork, integer *info, - ftnlen uplo_len); -extern int zposv_(char *uplo, integer *n, integer *nrhs, doublecomplex *a, - integer *lda, doublecomplex *b, integer *ldb, integer *info, - ftnlen uplo_len); -extern int zposvx_(char *fact, char *uplo, integer *n, integer *nrhs, - doublecomplex *a, integer *lda, doublecomplex *af, - integer *ldaf, char *equed, doublereal *s, doublecomplex *b, - integer *ldb, doublecomplex *x, integer *ldx, - doublereal *rcond, doublereal *ferr, doublereal *berr, - doublecomplex *work, doublereal *rwork, integer *info, - ftnlen fact_len, ftnlen uplo_len, ftnlen equed_len); -extern int zpotf2_(char *uplo, integer *n, doublecomplex *a, integer *lda, - integer *info, ftnlen uplo_len); -extern int zpotrf_(char *uplo, integer *n, doublecomplex *a, integer *lda, - integer *info, ftnlen uplo_len); -extern int zpotri_(char *uplo, integer *n, doublecomplex *a, integer *lda, - integer *info, ftnlen uplo_len); -extern int zpotrs_(char *uplo, integer *n, integer *nrhs, doublecomplex *a, - integer *lda, doublecomplex *b, integer *ldb, integer *info, - ftnlen uplo_len); -extern int zppcon_(char *uplo, integer *n, doublecomplex *ap, doublereal *anorm, - doublereal *rcond, doublecomplex *work, doublereal *rwork, - integer *info, ftnlen uplo_len); -extern int zppequ_(char *uplo, integer *n, doublecomplex *ap, doublereal *s, - doublereal *scond, doublereal *amax, integer *info, - ftnlen uplo_len); -extern int zpprfs_(char *uplo, integer *n, integer *nrhs, doublecomplex *ap, - doublecomplex *afp, doublecomplex *b, integer *ldb, - doublecomplex *x, integer *ldx, doublereal *ferr, - doublereal *berr, doublecomplex *work, doublereal *rwork, - integer *info, ftnlen uplo_len); -extern int zppsv_(char *uplo, integer *n, integer *nrhs, doublecomplex *ap, - doublecomplex *b, integer *ldb, integer *info, - ftnlen uplo_len); -extern int zppsvx_(char *fact, char *uplo, integer *n, integer *nrhs, - doublecomplex *ap, doublecomplex *afp, char *equed, - doublereal *s, doublecomplex *b, integer *ldb, - doublecomplex *x, integer *ldx, doublereal *rcond, - doublereal *ferr, doublereal *berr, doublecomplex *work, - doublereal *rwork, integer *info, ftnlen fact_len, - ftnlen uplo_len, ftnlen equed_len); -extern int zpptrf_(char *uplo, integer *n, doublecomplex *ap, integer *info, - ftnlen uplo_len); -extern int zpptri_(char *uplo, integer *n, doublecomplex *ap, integer *info, - ftnlen uplo_len); -extern int zpptrs_(char *uplo, integer *n, integer *nrhs, doublecomplex *ap, - doublecomplex *b, integer *ldb, integer *info, - ftnlen uplo_len); -extern int zptcon_(integer *n, doublereal *d__, doublecomplex *e, - doublereal *anorm, doublereal *rcond, doublereal *rwork, - integer *info); -extern int zpteqr_(char *compz, integer *n, doublereal *d__, doublereal *e, - doublecomplex *z__, integer *ldz, doublereal *work, - integer *info, ftnlen compz_len); -extern int zptrfs_(char *uplo, integer *n, integer *nrhs, doublereal *d__, - doublecomplex *e, doublereal *df, doublecomplex *ef, - doublecomplex *b, integer *ldb, doublecomplex *x, - integer *ldx, doublereal *ferr, doublereal *berr, - doublecomplex *work, doublereal *rwork, integer *info, - ftnlen uplo_len); -extern int zptsv_(integer *n, integer *nrhs, doublereal *d__, doublecomplex *e, - doublecomplex *b, integer *ldb, integer *info); -extern int zptsvx_(char *fact, integer *n, integer *nrhs, doublereal *d__, - doublecomplex *e, doublereal *df, doublecomplex *ef, - doublecomplex *b, integer *ldb, doublecomplex *x, - integer *ldx, doublereal *rcond, doublereal *ferr, - doublereal *berr, doublecomplex *work, doublereal *rwork, - integer *info, ftnlen fact_len); -extern int zpttrf_(integer *n, doublereal *d__, doublecomplex *e, - integer *info); -extern int zpttrs_(char *uplo, integer *n, integer *nrhs, doublereal *d__, - doublecomplex *e, doublecomplex *b, integer *ldb, - integer *info, ftnlen uplo_len); -extern int zptts2_(integer *iuplo, integer *n, integer *nrhs, doublereal *d__, - doublecomplex *e, doublecomplex *b, integer *ldb); -extern int zrot_(integer *n, doublecomplex *cx, integer *incx, - doublecomplex *cy, integer *incy, doublereal *c__, - doublecomplex *s); -extern int zspcon_(char *uplo, integer *n, doublecomplex *ap, integer *ipiv, - doublereal *anorm, doublereal *rcond, doublecomplex *work, - integer *info, ftnlen uplo_len); -extern int zspmv_(char *uplo, integer *n, doublecomplex *alpha, - doublecomplex *ap, doublecomplex *x, integer *incx, - doublecomplex *beta, doublecomplex *y, integer *incy, - ftnlen uplo_len); -extern int zspr_(char *uplo, integer *n, doublecomplex *alpha, doublecomplex *x, - integer *incx, doublecomplex *ap, ftnlen uplo_len); -extern int zsprfs_(char *uplo, integer *n, integer *nrhs, doublecomplex *ap, - doublecomplex *afp, integer *ipiv, doublecomplex *b, - integer *ldb, doublecomplex *x, integer *ldx, - doublereal *ferr, doublereal *berr, doublecomplex *work, - doublereal *rwork, integer *info, ftnlen uplo_len); -extern int zspsv_(char *uplo, integer *n, integer *nrhs, doublecomplex *ap, - integer *ipiv, doublecomplex *b, integer *ldb, integer *info, - ftnlen uplo_len); -extern int zspsvx_(char *fact, char *uplo, integer *n, integer *nrhs, - doublecomplex *ap, doublecomplex *afp, integer *ipiv, - doublecomplex *b, integer *ldb, doublecomplex *x, - integer *ldx, doublereal *rcond, doublereal *ferr, - doublereal *berr, doublecomplex *work, doublereal *rwork, - integer *info, ftnlen fact_len, ftnlen uplo_len); -extern int zsptrf_(char *uplo, integer *n, doublecomplex *ap, integer *ipiv, - integer *info, ftnlen uplo_len); -extern int zsptri_(char *uplo, integer *n, doublecomplex *ap, integer *ipiv, - doublecomplex *work, integer *info, ftnlen uplo_len); -extern int zsptrs_(char *uplo, integer *n, integer *nrhs, doublecomplex *ap, - integer *ipiv, doublecomplex *b, integer *ldb, integer *info, - ftnlen uplo_len); -extern int zstedc_(char *compz, integer *n, doublereal *d__, doublereal *e, - doublecomplex *z__, integer *ldz, doublecomplex *work, - integer *lwork, doublereal *rwork, integer *lrwork, - integer *iwork, integer *liwork, integer *info, - ftnlen compz_len); -extern int zstegr_(char *jobz, char *range, integer *n, doublereal *d__, - doublereal *e, doublereal *vl, doublereal *vu, integer *il, - integer *iu, doublereal *abstol, integer *m, doublereal *w, - doublecomplex *z__, integer *ldz, integer *isuppz, - doublereal *work, integer *lwork, integer *iwork, - integer *liwork, integer *info, ftnlen jobz_len, - ftnlen range_len); -extern int zstein_(integer *n, doublereal *d__, doublereal *e, integer *m, - doublereal *w, integer *iblock, integer *isplit, - doublecomplex *z__, integer *ldz, doublereal *work, - integer *iwork, integer *ifail, integer *info); -extern int zsteqr_(char *compz, integer *n, doublereal *d__, doublereal *e, - doublecomplex *z__, integer *ldz, doublereal *work, - integer *info, ftnlen compz_len); -extern int zsycon_(char *uplo, integer *n, doublecomplex *a, integer *lda, - integer *ipiv, doublereal *anorm, doublereal *rcond, - doublecomplex *work, integer *info, ftnlen uplo_len); -extern int zsymv_(char *uplo, integer *n, doublecomplex *alpha, - doublecomplex *a, integer *lda, doublecomplex *x, - integer *incx, doublecomplex *beta, doublecomplex *y, - integer *incy, ftnlen uplo_len); -extern int zsyr_(char *uplo, integer *n, doublecomplex *alpha, doublecomplex *x, - integer *incx, doublecomplex *a, integer *lda, - ftnlen uplo_len); -extern int zsyrfs_(char *uplo, integer *n, integer *nrhs, doublecomplex *a, - integer *lda, doublecomplex *af, integer *ldaf, - integer *ipiv, doublecomplex *b, integer *ldb, - doublecomplex *x, integer *ldx, doublereal *ferr, - doublereal *berr, doublecomplex *work, doublereal *rwork, - integer *info, ftnlen uplo_len); -extern int zsysv_(char *uplo, integer *n, integer *nrhs, doublecomplex *a, - integer *lda, integer *ipiv, doublecomplex *b, integer *ldb, - doublecomplex *work, integer *lwork, integer *info, - ftnlen uplo_len); -extern int zsysvx_(char *fact, char *uplo, integer *n, integer *nrhs, - doublecomplex *a, integer *lda, doublecomplex *af, - integer *ldaf, integer *ipiv, doublecomplex *b, integer *ldb, - doublecomplex *x, integer *ldx, doublereal *rcond, - doublereal *ferr, doublereal *berr, doublecomplex *work, - integer *lwork, doublereal *rwork, integer *info, - ftnlen fact_len, ftnlen uplo_len); -extern int zsytf2_(char *uplo, integer *n, doublecomplex *a, integer *lda, - integer *ipiv, integer *info, ftnlen uplo_len); -extern int zsytrf_(char *uplo, integer *n, doublecomplex *a, integer *lda, - integer *ipiv, doublecomplex *work, integer *lwork, - integer *info, ftnlen uplo_len); -extern int zsytri_(char *uplo, integer *n, doublecomplex *a, integer *lda, - integer *ipiv, doublecomplex *work, integer *info, - ftnlen uplo_len); -extern int zsytrs_(char *uplo, integer *n, integer *nrhs, doublecomplex *a, - integer *lda, integer *ipiv, doublecomplex *b, integer *ldb, - integer *info, ftnlen uplo_len); -extern int ztbcon_(char *norm, char *uplo, char *diag, integer *n, integer *kd, - doublecomplex *ab, integer *ldab, doublereal *rcond, - doublecomplex *work, doublereal *rwork, integer *info, - ftnlen norm_len, ftnlen uplo_len, ftnlen diag_len); -extern int ztbrfs_(char *uplo, char *trans, char *diag, integer *n, integer *kd, - integer *nrhs, doublecomplex *ab, integer *ldab, - doublecomplex *b, integer *ldb, doublecomplex *x, - integer *ldx, doublereal *ferr, doublereal *berr, - doublecomplex *work, doublereal *rwork, integer *info, - ftnlen uplo_len, ftnlen trans_len, ftnlen diag_len); -extern int ztbtrs_(char *uplo, char *trans, char *diag, integer *n, integer *kd, - integer *nrhs, doublecomplex *ab, integer *ldab, - doublecomplex *b, integer *ldb, integer *info, - ftnlen uplo_len, ftnlen trans_len, ftnlen diag_len); -extern int ztgevc_(char *side, char *howmny, logical *select, integer *n, - doublecomplex *a, integer *lda, doublecomplex *b, - integer *ldb, doublecomplex *vl, integer *ldvl, - doublecomplex *vr, integer *ldvr, integer *mm, integer *m, - doublecomplex *work, doublereal *rwork, integer *info, - ftnlen side_len, ftnlen howmny_len); -extern int ztgex2_(logical *wantq, logical *wantz, integer *n, doublecomplex *a, - integer *lda, doublecomplex *b, integer *ldb, - doublecomplex *q, integer *ldq, doublecomplex *z__, - integer *ldz, integer *j1, integer *info); -extern int ztgexc_(logical *wantq, logical *wantz, integer *n, doublecomplex *a, - integer *lda, doublecomplex *b, integer *ldb, - doublecomplex *q, integer *ldq, doublecomplex *z__, - integer *ldz, integer *ifst, integer *ilst, integer *info); -extern int ztgsen_(integer *ijob, logical *wantq, logical *wantz, - logical *select, integer *n, doublecomplex *a, integer *lda, - doublecomplex *b, integer *ldb, doublecomplex *alpha, - doublecomplex *beta, doublecomplex *q, integer *ldq, - doublecomplex *z__, integer *ldz, integer *m, doublereal *pl, - doublereal *pr, doublereal *dif, doublecomplex *work, - integer *lwork, integer *iwork, integer *liwork, - integer *info); -extern int ztgsja_(char *jobu, char *jobv, char *jobq, integer *m, integer *p, - integer *n, integer *k, integer *l, doublecomplex *a, - integer *lda, doublecomplex *b, integer *ldb, - doublereal *tola, doublereal *tolb, doublereal *alpha, - doublereal *beta, doublecomplex *u, integer *ldu, - doublecomplex *v, integer *ldv, doublecomplex *q, - integer *ldq, doublecomplex *work, integer *ncycle, - integer *info, ftnlen jobu_len, ftnlen jobv_len, - ftnlen jobq_len); -extern int ztgsna_(char *job, char *howmny, logical *select, integer *n, - doublecomplex *a, integer *lda, doublecomplex *b, - integer *ldb, doublecomplex *vl, integer *ldvl, - doublecomplex *vr, integer *ldvr, doublereal *s, - doublereal *dif, integer *mm, integer *m, - doublecomplex *work, integer *lwork, integer *iwork, - integer *info, ftnlen job_len, ftnlen howmny_len); -extern int ztgsy2_(char *trans, integer *ijob, integer *m, integer *n, - doublecomplex *a, integer *lda, doublecomplex *b, - integer *ldb, doublecomplex *c__, integer *ldc, - doublecomplex *d__, integer *ldd, doublecomplex *e, - integer *lde, doublecomplex *f, integer *ldf, - doublereal *scale, doublereal *rdsum, doublereal *rdscal, - integer *info, ftnlen trans_len); -extern int ztgsyl_(char *trans, integer *ijob, integer *m, integer *n, - doublecomplex *a, integer *lda, doublecomplex *b, - integer *ldb, doublecomplex *c__, integer *ldc, - doublecomplex *d__, integer *ldd, doublecomplex *e, - integer *lde, doublecomplex *f, integer *ldf, - doublereal *scale, doublereal *dif, doublecomplex *work, - integer *lwork, integer *iwork, integer *info, - ftnlen trans_len); -extern int ztpcon_(char *norm, char *uplo, char *diag, integer *n, - doublecomplex *ap, doublereal *rcond, doublecomplex *work, - doublereal *rwork, integer *info, ftnlen norm_len, - ftnlen uplo_len, ftnlen diag_len); -extern int ztprfs_(char *uplo, char *trans, char *diag, integer *n, - integer *nrhs, doublecomplex *ap, doublecomplex *b, - integer *ldb, doublecomplex *x, integer *ldx, - doublereal *ferr, doublereal *berr, doublecomplex *work, - doublereal *rwork, integer *info, ftnlen uplo_len, - ftnlen trans_len, ftnlen diag_len); -extern int ztptri_(char *uplo, char *diag, integer *n, doublecomplex *ap, - integer *info, ftnlen uplo_len, ftnlen diag_len); -extern int ztptrs_(char *uplo, char *trans, char *diag, integer *n, - integer *nrhs, doublecomplex *ap, doublecomplex *b, - integer *ldb, integer *info, ftnlen uplo_len, - ftnlen trans_len, ftnlen diag_len); -extern int ztrcon_(char *norm, char *uplo, char *diag, integer *n, - doublecomplex *a, integer *lda, doublereal *rcond, - doublecomplex *work, doublereal *rwork, integer *info, - ftnlen norm_len, ftnlen uplo_len, ftnlen diag_len); -extern int ztrevc_(char *side, char *howmny, logical *select, integer *n, - doublecomplex *t, integer *ldt, doublecomplex *vl, - integer *ldvl, doublecomplex *vr, integer *ldvr, integer *mm, - integer *m, doublecomplex *work, doublereal *rwork, - integer *info, ftnlen side_len, ftnlen howmny_len); -extern int ztrexc_(char *compq, integer *n, doublecomplex *t, integer *ldt, - doublecomplex *q, integer *ldq, integer *ifst, integer *ilst, - integer *info, ftnlen compq_len); -extern int ztrrfs_(char *uplo, char *trans, char *diag, integer *n, - integer *nrhs, doublecomplex *a, integer *lda, - doublecomplex *b, integer *ldb, doublecomplex *x, - integer *ldx, doublereal *ferr, doublereal *berr, - doublecomplex *work, doublereal *rwork, integer *info, - ftnlen uplo_len, ftnlen trans_len, ftnlen diag_len); -extern int ztrsen_(char *job, char *compq, logical *select, integer *n, - doublecomplex *t, integer *ldt, doublecomplex *q, - integer *ldq, doublecomplex *w, integer *m, doublereal *s, - doublereal *sep, doublecomplex *work, integer *lwork, - integer *info, ftnlen job_len, ftnlen compq_len); -extern int ztrsna_(char *job, char *howmny, logical *select, integer *n, - doublecomplex *t, integer *ldt, doublecomplex *vl, - integer *ldvl, doublecomplex *vr, integer *ldvr, - doublereal *s, doublereal *sep, integer *mm, integer *m, - doublecomplex *work, integer *ldwork, doublereal *rwork, - integer *info, ftnlen job_len, ftnlen howmny_len); -extern int ztrsyl_(char *trana, char *tranb, integer *isgn, integer *m, - integer *n, doublecomplex *a, integer *lda, doublecomplex *b, - integer *ldb, doublecomplex *c__, integer *ldc, - doublereal *scale, integer *info, ftnlen trana_len, - ftnlen tranb_len); -extern int ztrti2_(char *uplo, char *diag, integer *n, doublecomplex *a, - integer *lda, integer *info, ftnlen uplo_len, - ftnlen diag_len); -extern int ztrtri_(char *uplo, char *diag, integer *n, doublecomplex *a, - integer *lda, integer *info, ftnlen uplo_len, - ftnlen diag_len); -extern int ztrtrs_(char *uplo, char *trans, char *diag, integer *n, - integer *nrhs, doublecomplex *a, integer *lda, - doublecomplex *b, integer *ldb, integer *info, - ftnlen uplo_len, ftnlen trans_len, ftnlen diag_len); -extern int ztzrqf_(integer *m, integer *n, doublecomplex *a, integer *lda, - doublecomplex *tau, integer *info); -extern int ztzrzf_(integer *m, integer *n, doublecomplex *a, integer *lda, - doublecomplex *tau, doublecomplex *work, integer *lwork, - integer *info); -extern int zung2l_(integer *m, integer *n, integer *k, doublecomplex *a, - integer *lda, doublecomplex *tau, doublecomplex *work, - integer *info); -extern int zung2r_(integer *m, integer *n, integer *k, doublecomplex *a, - integer *lda, doublecomplex *tau, doublecomplex *work, - integer *info); -extern int zungbr_(char *vect, integer *m, integer *n, integer *k, - doublecomplex *a, integer *lda, doublecomplex *tau, - doublecomplex *work, integer *lwork, integer *info, - ftnlen vect_len); -extern int zunghr_(integer *n, integer *ilo, integer *ihi, doublecomplex *a, - integer *lda, doublecomplex *tau, doublecomplex *work, - integer *lwork, integer *info); -extern int zungl2_(integer *m, integer *n, integer *k, doublecomplex *a, - integer *lda, doublecomplex *tau, doublecomplex *work, - integer *info); -extern int zunglq_(integer *m, integer *n, integer *k, doublecomplex *a, - integer *lda, doublecomplex *tau, doublecomplex *work, - integer *lwork, integer *info); -extern int zungql_(integer *m, integer *n, integer *k, doublecomplex *a, - integer *lda, doublecomplex *tau, doublecomplex *work, - integer *lwork, integer *info); -extern int zungqr_(integer *m, integer *n, integer *k, doublecomplex *a, - integer *lda, doublecomplex *tau, doublecomplex *work, - integer *lwork, integer *info); -extern int zungr2_(integer *m, integer *n, integer *k, doublecomplex *a, - integer *lda, doublecomplex *tau, doublecomplex *work, - integer *info); -extern int zungrq_(integer *m, integer *n, integer *k, doublecomplex *a, - integer *lda, doublecomplex *tau, doublecomplex *work, - integer *lwork, integer *info); -extern int zungtr_(char *uplo, integer *n, doublecomplex *a, integer *lda, - doublecomplex *tau, doublecomplex *work, integer *lwork, - integer *info, ftnlen uplo_len); -extern int zunm2l_(char *side, char *trans, integer *m, integer *n, integer *k, - doublecomplex *a, integer *lda, doublecomplex *tau, - doublecomplex *c__, integer *ldc, doublecomplex *work, - integer *info, ftnlen side_len, ftnlen trans_len); -extern int zunm2r_(char *side, char *trans, integer *m, integer *n, integer *k, - doublecomplex *a, integer *lda, doublecomplex *tau, - doublecomplex *c__, integer *ldc, doublecomplex *work, - integer *info, ftnlen side_len, ftnlen trans_len); -extern int zunmbr_(char *vect, char *side, char *trans, integer *m, integer *n, - integer *k, doublecomplex *a, integer *lda, - doublecomplex *tau, doublecomplex *c__, integer *ldc, - doublecomplex *work, integer *lwork, integer *info, - ftnlen vect_len, ftnlen side_len, ftnlen trans_len); -extern int zunmhr_(char *side, char *trans, integer *m, integer *n, - integer *ilo, integer *ihi, doublecomplex *a, integer *lda, - doublecomplex *tau, doublecomplex *c__, integer *ldc, - doublecomplex *work, integer *lwork, integer *info, - ftnlen side_len, ftnlen trans_len); -extern int zunml2_(char *side, char *trans, integer *m, integer *n, integer *k, - doublecomplex *a, integer *lda, doublecomplex *tau, - doublecomplex *c__, integer *ldc, doublecomplex *work, - integer *info, ftnlen side_len, ftnlen trans_len); -extern int zunmlq_(char *side, char *trans, integer *m, integer *n, integer *k, - doublecomplex *a, integer *lda, doublecomplex *tau, - doublecomplex *c__, integer *ldc, doublecomplex *work, - integer *lwork, integer *info, ftnlen side_len, - ftnlen trans_len); -extern int zunmql_(char *side, char *trans, integer *m, integer *n, integer *k, - doublecomplex *a, integer *lda, doublecomplex *tau, - doublecomplex *c__, integer *ldc, doublecomplex *work, - integer *lwork, integer *info, ftnlen side_len, - ftnlen trans_len); -extern int zunmqr_(char *side, char *trans, integer *m, integer *n, integer *k, - doublecomplex *a, integer *lda, doublecomplex *tau, - doublecomplex *c__, integer *ldc, doublecomplex *work, - integer *lwork, integer *info, ftnlen side_len, - ftnlen trans_len); -extern int zunmr2_(char *side, char *trans, integer *m, integer *n, integer *k, - doublecomplex *a, integer *lda, doublecomplex *tau, - doublecomplex *c__, integer *ldc, doublecomplex *work, - integer *info, ftnlen side_len, ftnlen trans_len); -extern int zunmr3_(char *side, char *trans, integer *m, integer *n, integer *k, - integer *l, doublecomplex *a, integer *lda, - doublecomplex *tau, doublecomplex *c__, integer *ldc, - doublecomplex *work, integer *info, ftnlen side_len, - ftnlen trans_len); -extern int zunmrq_(char *side, char *trans, integer *m, integer *n, integer *k, - doublecomplex *a, integer *lda, doublecomplex *tau, - doublecomplex *c__, integer *ldc, doublecomplex *work, - integer *lwork, integer *info, ftnlen side_len, - ftnlen trans_len); -extern int zunmrz_(char *side, char *trans, integer *m, integer *n, integer *k, - integer *l, doublecomplex *a, integer *lda, - doublecomplex *tau, doublecomplex *c__, integer *ldc, - doublecomplex *work, integer *lwork, integer *info, - ftnlen side_len, ftnlen trans_len); -extern int zunmtr_(char *side, char *uplo, char *trans, integer *m, integer *n, - doublecomplex *a, integer *lda, doublecomplex *tau, - doublecomplex *c__, integer *ldc, doublecomplex *work, - integer *lwork, integer *info, ftnlen side_len, - ftnlen uplo_len, ftnlen trans_len); -extern int zupgtr_(char *uplo, integer *n, doublecomplex *ap, - doublecomplex *tau, doublecomplex *q, integer *ldq, - doublecomplex *work, integer *info, ftnlen uplo_len); -extern int zupmtr_(char *side, char *uplo, char *trans, integer *m, integer *n, - doublecomplex *ap, doublecomplex *tau, doublecomplex *c__, - integer *ldc, doublecomplex *work, integer *info, - ftnlen side_len, ftnlen uplo_len, ftnlen trans_len); - -#endif diff --git a/lib/gmath/ATLAS_wrapper_blas_level_1.c b/lib/gmath/ATLAS_wrapper_blas_level_1.c index 24390a61dfb..556f1654b46 100644 --- a/lib/gmath/ATLAS_wrapper_blas_level_1.c +++ b/lib/gmath/ATLAS_wrapper_blas_level_1.c @@ -21,15 +21,19 @@ #include #include -#if defined(HAVE_ATLAS) +#if defined(HAVE_LIBBLAS) +#if defined(HAVE_CBLAS_ATLAS_H) +#include +#else #include #endif +#endif /* HAVE_LIBBLAS */ /*! * \brief Compute the dot product of vector x and y - * using the ATLAS routine cblas_ddot + * using the CBLAS routine cblas_ddot * - * If grass was not compiled with ATLAS support + * If grass was not compiled with CBLAS support * it will call #G_math_f_x_dot_y, the OpenMP multi threaded * grass implementatiom * @@ -41,7 +45,7 @@ * */ double G_math_ddot(double *x, double *y, int rows) { -#if defined(HAVE_ATLAS) +#if defined(HAVE_LIBBLAS) return cblas_ddot(rows, x, 1, y, 1); #else double val; @@ -53,9 +57,9 @@ double G_math_ddot(double *x, double *y, int rows) /*! * \brief Compute the dot product of vector x and y - * using the ATLAS routine cblas_sdsdot + * using the CBLAS routine cblas_sdsdot * - * If grass was not compiled with ATLAS support + * If grass was not compiled with CBLAS support * it will call #G_math_f_x_dot_y, the OpenMP multi threaded * grass implementatiom * @@ -68,7 +72,7 @@ double G_math_ddot(double *x, double *y, int rows) * */ float G_math_sdsdot(float *x, float *y, float a, int rows) { -#if defined(HAVE_ATLAS) +#if defined(HAVE_LIBBLAS) return cblas_sdsdot(rows, a, x, 1, y, 1); #else float val; @@ -80,9 +84,9 @@ float G_math_sdsdot(float *x, float *y, float a, int rows) /*! * \brief Compute the euclidean norm of vector x - * using the ATLAS routine cblas_dnrm2 + * using the CBLAS routine cblas_dnrm2 * - * If grass was not compiled with ATLAS support + * If grass was not compiled with CBLAS support * it will call #G_math_d_euclid_norm, the OpenMP multi threaded * grass implementatiom * @@ -93,7 +97,7 @@ float G_math_sdsdot(float *x, float *y, float a, int rows) * */ double G_math_dnrm2(double *x, int rows) { -#if defined(HAVE_ATLAS) +#if defined(HAVE_LIBBLAS) return cblas_dnrm2(rows, x, 1); #else double val; @@ -105,9 +109,9 @@ double G_math_dnrm2(double *x, int rows) /*! * \brief Compute the absolute sum norm of vector x - * using the ATLAS routine cblas_dasum + * using the CBLAS routine cblas_dasum * - * If grass was not compiled with ATLAS support + * If grass was not compiled with CBLAS support * it will call #G_math_d_asum_norm, the OpenMP multi threaded * grass implementatiom * @@ -118,7 +122,7 @@ double G_math_dnrm2(double *x, int rows) * */ double G_math_dasum(double *x, int rows) { -#if defined(HAVE_ATLAS) +#if defined(HAVE_LIBBLAS) return cblas_dasum(rows, x, 1); #else double val; @@ -130,9 +134,9 @@ double G_math_dasum(double *x, int rows) /*! * \brief Compute the maximum norm of vector x - * using the ATLAS routine cblas_idamax + * using the CBLAS routine cblas_idamax * - * If grass was not compiled with ATLAS support + * If grass was not compiled with CBLAS support * it will call #G_math_d_max_norm, the OpenMP multi threaded * grass implementatiom * @@ -143,7 +147,7 @@ double G_math_dasum(double *x, int rows) * */ double G_math_idamax(double *x, int rows) { -#if defined(HAVE_ATLAS) +#if defined(HAVE_LIBBLAS) return cblas_idamax(rows, x, 1); #else double val; @@ -155,9 +159,9 @@ double G_math_idamax(double *x, int rows) /*! * \brief Scale vector x with scalar a - * using the ATLAS routine cblas_dscal + * using the CBLAS routine cblas_dscal * - * If grass was not compiled with ATLAS support + * If grass was not compiled with CBLAS support * it will call #G_math_d_ax_by, the OpenMP multi threaded * grass implementatiom * @@ -169,7 +173,7 @@ double G_math_idamax(double *x, int rows) * */ void G_math_dscal(double *x, double a, int rows) { -#if defined(HAVE_ATLAS) +#if defined(HAVE_LIBBLAS) cblas_dscal(rows, a, x, 1); #else G_math_d_ax_by(x, x, x, a, 0.0, rows); @@ -181,7 +185,7 @@ void G_math_dscal(double *x, double a, int rows) /*! * \brief Copy vector x to vector y * - * If grass was not compiled with ATLAS support + * If grass was not compiled with CBLAS support * it will call #G_math_d_copy * * \param x (double *) @@ -192,7 +196,7 @@ void G_math_dscal(double *x, double a, int rows) * */ void G_math_dcopy(double *x, double *y, int rows) { -#if defined(HAVE_ATLAS) +#if defined(HAVE_LIBBLAS) cblas_dcopy(rows, x, 1, y, 1); #else G_math_d_copy(x, y, rows); @@ -206,7 +210,7 @@ void G_math_dcopy(double *x, double *y, int rows) * * \f[ {\bf z} = a{\bf x} + {\bf y} \f] * - * If grass was not compiled with ATLAS support + * If grass was not compiled with CBLAS support * it will call #G_math_d_ax_by, the * grass implementatiom @@ -220,7 +224,7 @@ void G_math_dcopy(double *x, double *y, int rows) * */ void G_math_daxpy(double *x, double *y, double a, int rows) { -#if defined(HAVE_ATLAS) +#if defined(HAVE_LIBBLAS) cblas_daxpy(rows, a, x, 1, y, 1); #else G_math_d_ax_by(x, y, y, a, 1.0, rows); @@ -231,15 +235,15 @@ void G_math_daxpy(double *x, double *y, double a, int rows) /****************************************************************** */ -/********* F L O A T / S I N G L E P E P R E C I S I O N ******** */ +/********* F L O A T / S I N G L E P R E C I S I O N ********* */ /****************************************************************** */ /*! * \brief Compute the dot product of vector x and y - * using the ATLAS routine cblas_sdot + * using the CBLAS routine cblas_sdot * - * If grass was not compiled with ATLAS support + * If grass was not compiled with CBLAS support * it will call #G_math_f_x_dot_y, the OpenMP multi threaded * grass implementatiom * @@ -251,7 +255,7 @@ void G_math_daxpy(double *x, double *y, double a, int rows) * */ float G_math_sdot(float *x, float *y, int rows) { -#if defined(HAVE_ATLAS) +#if defined(HAVE_LIBBLAS) return cblas_sdot(rows, x, 1, y, 1); #else float val; @@ -263,9 +267,9 @@ float G_math_sdot(float *x, float *y, int rows) /*! * \brief Compute the euclidean norm of vector x - * using the ATLAS routine cblas_dnrm2 + * using the CBLAS routine cblas_dnrm2 * - * If grass was not compiled with ATLAS support + * If grass was not compiled with CBLAS support * it will call #G_math_f_euclid_norm, the OpenMP multi threaded * grass implementatiom * @@ -276,7 +280,7 @@ float G_math_sdot(float *x, float *y, int rows) * */ float G_math_snrm2(float *x, int rows) { -#if defined(HAVE_ATLAS) +#if defined(HAVE_LIBBLAS) return cblas_snrm2(rows, x, 1); #else float val; @@ -288,9 +292,9 @@ float G_math_snrm2(float *x, int rows) /*! * \brief Compute the absolute sum norm of vector x - * using the ATLAS routine cblas_dasum + * using the CBLAS routine cblas_dasum * - * If grass was not compiled with ATLAS support + * If grass was not compiled with CBLAS support * it will call #G_math_f_asum_norm, the OpenMP multi threaded * grass implementatiom * @@ -301,7 +305,7 @@ float G_math_snrm2(float *x, int rows) * */ float G_math_sasum(float *x, int rows) { -#if defined(HAVE_ATLAS) +#if defined(HAVE_LIBBLAS) return cblas_sasum(rows, x, 1); #else float val; @@ -313,9 +317,9 @@ float G_math_sasum(float *x, int rows) /*! * \brief Compute the maximum norm of vector x - * using the ATLAS routine cblas_idamax + * using the CBLAS routine cblas_idamax * - * If grass was not compiled with ATLAS support + * If grass was not compiled with CBLAS support * it will call #G_math_f_max_norm, the OpenMP multi threaded * grass implementatiom * @@ -326,7 +330,7 @@ float G_math_sasum(float *x, int rows) * */ float G_math_isamax(float *x, int rows) { -#if defined(HAVE_ATLAS) +#if defined(HAVE_LIBBLAS) return cblas_isamax(rows, x, 1); #else float val; @@ -338,9 +342,9 @@ float G_math_isamax(float *x, int rows) /*! * \brief Scale vector x with scalar a - * using the ATLAS routine cblas_dscal + * using the CBLAS routine cblas_dscal * - * If grass was not compiled with ATLAS support + * If grass was not compiled with CBLAS support * it will call #G_math_f_ax_by, the OpenMP multi threaded * grass implementatiom * @@ -352,7 +356,7 @@ float G_math_isamax(float *x, int rows) * */ void G_math_sscal(float *x, float a, int rows) { -#if defined(HAVE_ATLAS) +#if defined(HAVE_LIBBLAS) cblas_sscal(rows, a, x, 1); #else G_math_f_ax_by(x, x, x, a, 0.0, rows); @@ -364,7 +368,7 @@ void G_math_sscal(float *x, float a, int rows) /*! * \brief Copy vector x to vector y * - * If grass was not compiled with ATLAS support + * If grass was not compiled with CBLAS support * it will call #G_math_f_copy, the * grass implementatiom * @@ -376,7 +380,7 @@ void G_math_sscal(float *x, float a, int rows) * */ void G_math_scopy(float *x, float *y, int rows) { -#if defined(HAVE_ATLAS) +#if defined(HAVE_LIBBLAS) cblas_scopy(rows, x, 1, y, 1); #else G_math_f_copy(x, y, rows); @@ -390,7 +394,7 @@ void G_math_scopy(float *x, float *y, int rows) * * \f[ {\bf z} = a{\bf x} + {\bf y} \f] * - * If grass was not compiled with ATLAS support + * If grass was not compiled with CBLAS support * it will call #G_math_f_ax_by, the * grass implementatiom @@ -404,7 +408,7 @@ void G_math_scopy(float *x, float *y, int rows) * */ void G_math_saxpy(float *x, float *y, float a, int rows) { -#if defined(HAVE_ATLAS) +#if defined(HAVE_LIBBLAS) cblas_saxpy(rows, a, x, 1, y, 1); #else G_math_f_ax_by(x, y, y, a, 1.0, rows); diff --git a/lib/gmath/Makefile b/lib/gmath/Makefile index 0fd6e14f55d..2927d32dec6 100644 --- a/lib/gmath/Makefile +++ b/lib/gmath/Makefile @@ -1,7 +1,7 @@ MODULE_TOPDIR = ../.. EXTRA_LIBS = $(OPENMP_LIBPATH) $(OPENMP_LIB) -EXTRA_INC = $(OPENMP_INCPATH) +EXTRA_INC = $(OPENMP_INCPATH) $(BLASINC) $(LAPACKINC) EXTRA_CFLAGS = $(FFTWINC) $(OPENMP_CFLAGS) LIB = GMATH diff --git a/lib/gmath/la.c b/lib/gmath/la.c index 5c0e59cb862..48a4cbc7817 100644 --- a/lib/gmath/la.c +++ b/lib/gmath/la.c @@ -22,19 +22,25 @@ ******************************************************************************/ -#include /* needed here for ifdef/else */ -#include -#include -#include - #include - -#if defined(HAVE_LIBLAPACK) && defined(HAVE_LIBBLAS) - #include #include #include +#if defined(HAVE_LIBLAPACK) && defined(HAVE_LIBBLAS) +#include +#if defined(HAVE_CBLAS_ATLAS_H) +#include +#else +#include +#endif +#endif + +#include +#include +#include +#include + static int egcmp(const void *pa, const void *pb); /*! @@ -66,7 +72,7 @@ mat_struct *G_matrix_init(int rows, int cols, int ldim) tmp_arry->type = MATRIX_; tmp_arry->v_indx = -1; - tmp_arry->vals = (doublereal *)G_calloc(ldim * cols, sizeof(doublereal)); + tmp_arry->vals = (double *)G_calloc(ldim * cols, sizeof(double)); tmp_arry->is_init = 1; return tmp_arry; @@ -86,7 +92,7 @@ int G_matrix_zero(mat_struct *A) if (!A->vals) return 0; - memset(A->vals, 0, (A->ldim * A->cols) * sizeof(doublereal)); + memset(A->vals, 0, (A->ldim * A->cols) * sizeof(double)); return 1; } @@ -119,7 +125,7 @@ int G_matrix_set(mat_struct *A, int rows, int cols, int ldim) A->type = MATRIX_; A->v_indx = -1; - A->vals = (doublereal *)G_calloc(ldim * cols, sizeof(doublereal)); + A->vals = (double *)G_calloc(ldim * cols, sizeof(double)); A->is_init = 1; return 0; @@ -150,7 +156,8 @@ mat_struct *G_matrix_copy(const mat_struct *A) return NULL; } - memcpy(&B->vals[0], &A->vals[0], A->cols * A->ldim * sizeof(doublereal)); + memcpy(&B->vals[0], &A->vals[0], + (size_t)A->cols * A->ldim * sizeof(double)); return B; } @@ -225,7 +232,7 @@ mat_struct *G_matrix_scalar_mul(double scalar, mat_struct *matrix, for (i = 0; i < m; i++) { for (j = 0; j < n; j++) { - doublereal value = scalar * G_matrix_get_element(matrix, i, j); + double value = scalar * G_matrix_get_element(matrix, i, j); G_matrix_set_element(out, i, j, value); } @@ -346,9 +353,8 @@ mat_struct *G__matrix_add(mat_struct *mt1, mat_struct *mt2, const double c1, mat_struct *G_matrix_product(mat_struct *mt1, mat_struct *mt2) { mat_struct *mt3; - doublereal unity = 1, zero = 0; - integer rows, cols, interdim, lda, ldb; - integer1 no_trans = 'n'; + double unity = 1., zero = 0.; + int rows, cols, interdim, lda, ldb; if (!((mt1->is_init) || (mt2->is_init))) { G_warning(_("One or both input matrices uninitialised")); @@ -367,21 +373,19 @@ mat_struct *G_matrix_product(mat_struct *mt1, mat_struct *mt2) /* Call the driver */ - rows = (integer)mt1->rows; - interdim = (integer)mt1->cols; - cols = (integer)mt2->cols; + rows = (int)mt1->rows; + interdim = (int)mt1->cols; + cols = (int)mt2->cols; - lda = (integer)mt1->ldim; - ldb = (integer)mt2->ldim; + lda = (int)mt1->ldim; + ldb = (int)mt2->ldim; - f77_dgemm(&no_trans, &no_trans, &rows, &cols, &interdim, &unity, mt1->vals, - &lda, mt2->vals, &ldb, &zero, mt3->vals, &lda); + cblas_dgemm(CblasColMajor, CblasNoTrans, CblasNoTrans, rows, cols, interdim, + unity, mt1->vals, lda, mt2->vals, ldb, zero, mt3->vals, lda); return mt3; } -#else /* defined(HAVE_LIBBLAS) */ -#warning G_matrix_product() not compiled; requires BLAS library #endif /* defined(HAVE_LIBBLAS) */ /*! @@ -401,7 +405,7 @@ mat_struct *G_matrix_transpose(mat_struct *mt) { mat_struct *mt1; int ldim, ldo; - doublereal *dbo, *dbt, *dbx, *dby; + double *dbo, *dbt, *dbx, *dby; int cnt, cnt2; /* Word align the workspace blocks */ @@ -509,20 +513,20 @@ int G_matrix_LU_solve(const mat_struct *mt1, mat_struct **xmat0, switch (mtype) { case NONSYM: { - integer *perm, res_info; - integer num_eqns, nrhs, lda, ldb; + int *perm, res_info; + int num_eqns, nrhs, lda, ldb; - perm = (integer *)G_malloc(wmat->rows * sizeof(integer)); + perm = (int *)G_malloc(wmat->rows * sizeof(int)); /* Set fields to pass to fortran routine */ - num_eqns = (integer)mt1->rows; - nrhs = (integer)wmat->cols; - lda = (integer)mt1->ldim; - ldb = (integer)wmat->ldim; + num_eqns = (int)mt1->rows; + nrhs = (int)wmat->cols; + lda = (int)mt1->ldim; + ldb = (int)wmat->ldim; /* Call LA driver */ - f77_dgesv(&num_eqns, &nrhs, mtx->vals, &lda, perm, wmat->vals, &ldb, - &res_info); + res_info = LAPACKE_dgesv(LAPACK_COL_MAJOR, num_eqns, nrhs, mtx->vals, + lda, perm, wmat->vals, ldb); /* Copy the results from the modified data matrix, taking account of pivot permutations ??? @@ -545,7 +549,7 @@ int G_matrix_LU_solve(const mat_struct *mt1, mat_struct **xmat0, */ memcpy(xmat->vals, wmat->vals, - wmat->cols * wmat->ldim * sizeof(doublereal)); + (size_t)wmat->cols * wmat->ldim * sizeof(double)); /* Free temp arrays */ G_free(perm); @@ -574,8 +578,6 @@ int G_matrix_LU_solve(const mat_struct *mt1, mat_struct **xmat0, return 0; } -#else /* defined(HAVE_LIBBLAS) && defined(HAVE_LIBLAPACK) */ -#warning G_matrix_LU_solve() not compiled; requires BLAS and LAPACK libraries #endif /* defined(HAVE_LIBBLAS) && defined(HAVE_LIBLAPACK) */ #if defined(HAVE_LIBBLAS) && defined(HAVE_LIBLAPACK) @@ -635,8 +637,6 @@ mat_struct *G_matrix_inverse(mat_struct *mt) } } -#else /* defined(HAVE_LIBBLAS) && defined(HAVE_LIBLAPACK) */ -#warning G_matrix_inverse() not compiled; requires BLAS and LAPACK libraries #endif /* defined(HAVE_LIBBLAS) && defined(HAVE_LIBLAPACK) */ /*! @@ -672,14 +672,14 @@ void G_matrix_free(mat_struct *mt) void G_matrix_print(mat_struct *mt) { int i, j; - char buf[64], numbuf[64]; + char buf[2048], numbuf[64]; for (i = 0; i < mt->rows; i++) { - strcpy(buf, ""); + G_strlcpy(buf, "", sizeof(buf)); for (j = 0; j < mt->cols; j++) { - - sprintf(numbuf, "%14.6f", G_matrix_get_element(mt, i, j)); + snprintf(numbuf, sizeof(numbuf), "%14.6f", + G_matrix_get_element(mt, i, j)); strcat(buf, numbuf); if (j < mt->cols - 1) strcat(buf, ", "); @@ -720,7 +720,7 @@ int G_matrix_set_element(mat_struct *mt, int rowval, int colval, double val) return -1; } - mt->vals[rowval + colval * mt->ldim] = (doublereal)val; + mt->vals[rowval + colval * mt->ldim] = (double)val; return 0; } @@ -918,7 +918,7 @@ int G_matvect_retrieve_matrix(vec_struct *vc) vec_struct *G_matvect_product(mat_struct *A, vec_struct *b, vec_struct *out) { unsigned int i, m, n, j; - register doublereal sum; + register double sum; /* G_message("A=%d,%d,%d", A->cols, A->rows, A->ldim); */ /* G_message("B=%d,%d,%d", b->cols, b->rows, b->ldim); */ @@ -997,8 +997,7 @@ vec_struct *G_vector_init(int cells, int ldim, vtype vt) tmp_arry->v_indx = 0; - tmp_arry->vals = - (doublereal *)G_calloc(ldim * tmp_arry->cols, sizeof(doublereal)); + tmp_arry->vals = (double *)G_calloc(ldim * tmp_arry->cols, sizeof(double)); tmp_arry->is_init = 1; return tmp_arry; @@ -1144,7 +1143,7 @@ int G_vector_set(vec_struct *A, int cells, int ldim, vtype vt, int vindx) else A->v_indx = vindx; - A->vals = (doublereal *)G_calloc(ldim * A->cols, sizeof(doublereal)); + A->vals = (double *)G_calloc(ldim * A->cols, sizeof(double)); A->is_init = 1; return 0; @@ -1166,22 +1165,22 @@ int G_vector_set(vec_struct *A, int cells, int ldim, vtype vt, int vindx) double G_vector_norm_euclid(vec_struct *vc) { - integer incr, Nval; - doublereal *startpt; + int incr, Nval; + double *startpt; if (!vc->is_init) G_fatal_error(_("Matrix is not initialised")); if (vc->type == ROWVEC_) { - Nval = (integer)vc->cols; - incr = (integer)vc->ldim; + Nval = (int)vc->cols; + incr = (int)vc->ldim; if (vc->v_indx < 0) startpt = vc->vals; else startpt = vc->vals + vc->v_indx; } else { - Nval = (integer)vc->rows; + Nval = (int)vc->rows; incr = 1; if (vc->v_indx < 0) startpt = vc->vals; @@ -1190,11 +1189,9 @@ double G_vector_norm_euclid(vec_struct *vc) } /* Call the BLAS routine dnrm2_() */ - return (double)f77_dnrm2(&Nval, startpt, &incr); + return cblas_dnrm2(Nval, startpt, incr); } -#else /* defined(HAVE_LIBBLAS) */ -#warning G_vector_norm_euclid() not compiled; requires BLAS library #endif /* defined(HAVE_LIBBLAS) */ /*! @@ -1216,7 +1213,7 @@ double G_vector_norm_euclid(vec_struct *vc) double G_vector_norm_maxval(vec_struct *vc, int vflag) { - doublereal xval, *startpt, *curpt; + double xval, *startpt, *curpt; double cellval; int ncells, incr; @@ -1224,15 +1221,15 @@ double G_vector_norm_maxval(vec_struct *vc, int vflag) G_fatal_error(_("Matrix is not initialised")); if (vc->type == ROWVEC_) { - ncells = (integer)vc->cols; - incr = (integer)vc->ldim; + ncells = (int)vc->cols; + incr = (int)vc->ldim; if (vc->v_indx < 0) startpt = vc->vals; else startpt = vc->vals + vc->v_indx; } else { - ncells = (integer)vc->rows; + ncells = (int)vc->rows; incr = 1; if (vc->v_indx < 0) startpt = vc->vals; @@ -1360,9 +1357,6 @@ vec_struct *G_vector_product(vec_struct *v1, vec_struct *v2, vec_struct *out) return NULL; } -#if defined(HAVE_LAPACK) && defined(HAVE_LIBBLAS) - f77_dhad(v1->cols, 1.0, v1->vals, 1, v2->vals, 1, 0.0, out->vals, 1.0); -#else idx1 = (v1->v_indx > 0) ? v1->v_indx : 0; idx2 = (v2->v_indx > 0) ? v2->v_indx : 0; idx0 = (out->v_indx > 0) ? out->v_indx : 0; @@ -1379,7 +1373,6 @@ vec_struct *G_vector_product(vec_struct *v1, vec_struct *v2, vec_struct *out) G_matrix_get_element(v1, i, idx1) * G_matrix_get_element(v2, i, idx2)); } -#endif return out; } @@ -1399,7 +1392,7 @@ vec_struct *G_vector_copy(const vec_struct *vc1, int comp_flag) { vec_struct *tmp_arry; int incr1, incr2; - doublereal *startpt1, *startpt2, *curpt1, *curpt2; + double *startpt1, *startpt2, *curpt1, *curpt2; int cnt; if (!vc1->is_init) { @@ -1441,8 +1434,8 @@ vec_struct *G_vector_copy(const vec_struct *vc1, int comp_flag) return NULL; } - tmp_arry->vals = (doublereal *)G_calloc(tmp_arry->ldim * tmp_arry->cols, - sizeof(doublereal)); + tmp_arry->vals = + (double *)G_calloc(tmp_arry->ldim * tmp_arry->cols, sizeof(double)); if (comp_flag == DO_COMPACT) { if (tmp_arry->type == ROWVEC_) { startpt1 = tmp_arry->vals; @@ -1482,7 +1475,7 @@ vec_struct *G_vector_copy(const vec_struct *vc1, int comp_flag) } while (cnt > 0) { - memcpy(curpt1, curpt2, sizeof(doublereal)); + memcpy(curpt1, curpt2, sizeof(double)); curpt1 += incr1; curpt2 += incr2; cnt--; @@ -1633,7 +1626,7 @@ int G_matrix_eigen_sort(vec_struct *d, mat_struct *m) } /* sort the combined matrix */ - qsort(tmp.vals, tmp.cols, tmp.ldim * sizeof(doublereal), egcmp); + qsort(tmp.vals, tmp.cols, tmp.ldim * sizeof(double), egcmp); /* split tmp into m and d */ for (i = 0; i < m->cols; i++) { @@ -1652,8 +1645,8 @@ int G_matrix_eigen_sort(vec_struct *d, mat_struct *m) static int egcmp(const void *pa, const void *pb) { - double a = *(doublereal *const)pa; - double b = *(doublereal *const)pb; + double a = *(double *const)pa; + double b = *(double *const)pb; if (a > b) return 1; @@ -1662,5 +1655,3 @@ static int egcmp(const void *pa, const void *pb) return 0; } - -#endif /* HAVE_BLAS && HAVE_LAPACK && HAVE_G2C */ diff --git a/mswindows/osgeo4w/build_osgeo4w.sh b/mswindows/osgeo4w/build_osgeo4w.sh index 6885bfe02f8..d38354d4b78 100755 --- a/mswindows/osgeo4w/build_osgeo4w.sh +++ b/mswindows/osgeo4w/build_osgeo4w.sh @@ -38,7 +38,7 @@ export ARCH=x86_64-w64-mingw32 --with-nls \ --with-readline \ --with-blas \ - --with-lapack-includes=/mingw64/include/lapack \ + --with-lapack \ --with-freetype \ --with-freetype-includes=${OSGEO4W_ROOT_MSYS}/include/freetype2 \ --with-proj-share=${OSGEO4W_ROOT_MSYS}/share/proj \ From 89cfc5230d7ac460da62692132f8da3d2d59538f Mon Sep 17 00:00:00 2001 From: Markus Neteler Date: Thu, 19 Sep 2024 09:37:25 +0200 Subject: [PATCH 251/471] docs: move Programmer's manual creation to INSTALL.md (#4336) - move Programmer's manual creation from `doc/development/README.md` to `INSTALL.md`. update `doc/development/README.md` - add Python API; add subsections - update URLs and wording - list guides - remove stray control chars (visible in vim) in `doc/development/style_guide.md` --- INSTALL.md | 32 ++++++++++++++++++++--- doc/development/README.md | 46 ++++++++-------------------------- doc/development/style_guide.md | 8 +++--- 3 files changed, 44 insertions(+), 42 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index 41249a04143..7687266d7c8 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -321,9 +321,35 @@ developers mailing list. See ## (L) GRASS PROGRAMMER'S MANUAL -The Programmer's manual is generated with doxygen from the source code. -Please see the [README](doc/development/README.md) file and the files at: - +The Programmer's manual is +generated from the source code. This requires the installation of +`doxygen` () and optionally Graphviz `dot` +(). + +The main file is: `./grasslib.dox` where all sub-documents have +to be linked to. + +To locally generate the 'Programmer's Manual', run + +```sh +make htmldocs +``` + +To generate documentation as a single html file +(recommended for simple reading) + +```sh +make htmldocs-single +``` + +This process takes some time. The result will be found in +the file `lib/html/index.html`. + +To generate the 'Programmer's Manual' in PDF format, run + +```sh +make pdfdocs +``` ## (M) CONTRIBUTING CODE AND PATCHES diff --git a/doc/development/README.md b/doc/development/README.md index 9845f20649e..db5244a5a72 100644 --- a/doc/development/README.md +++ b/doc/development/README.md @@ -1,43 +1,19 @@ # Development and Maintenance Documentation -Here is development and maitanance documentation. The API documentation -is at appropriate places, but here is the information relevant to -contributing to GRASS GIS and its maintanance. +Find below the development and maintenance documentation. +The API documentation is available in the appropriate places, +but here is the information relevant to contributing to and +maintaining GRASS GIS. -## How to generate the 'Programmer's Manual' +## Style and GitHub guide -You can locally generate the [GRASS GIS Programmer's Manual](https://grass.osgeo.org/programming8/). +- [GRASS Programming Style Guide](style_guide.md) +- [Guide to contributing on GitHub](github_guide.md) -This needs doxygen () and optionally -Graphviz dot (). +## Python API -To build the GRASS programmer's documentation, run +- ["grass" Python package documentation](https://grass.osgeo.org/grass-devel/manuals/libpython/) -```sh -make htmldocs -``` +## C API -Or to generate documentation as a single html file -(recommended for simple reading) - -```sh -make htmldocs-single -``` - -This takes quite some time. The result is in `lib/html/index.html` -which refers to further document repositories in - -```text -lib/vector/html/index.html -lib/db/html/index.html -lib/gis/html/index.html -``` - -The master file is: `./grasslib.dox` where all sub-documents have to -be linked to. - -To generate the documents in PDF format, run - -```sh -make pdfdocs -``` +- [GRASS Programmer's manual](https://grass.osgeo.org/programming8/) diff --git a/doc/development/style_guide.md b/doc/development/style_guide.md index 8f1d5ab5245..3b065d9f838 100644 --- a/doc/development/style_guide.md +++ b/doc/development/style_guide.md @@ -122,7 +122,7 @@ sorted and separated by a newline. Use function names which fulfill the official [GNU naming convention](https://www.gnu.org/prep/standards/html_node/Names.html). Instead of -naming a function like: MyNewFunction() use snake case: my_new_function()`. +naming a function like: `MyNewFunction()` use snake case: `my_new_function()`. ### Using pre-commit @@ -192,7 +192,7 @@ There are three types of documentation: C API, Python API and tool documentation #### C API documentation We -[​use doxygen and document the functions](https://grass.osgeo.org/programming8/) +[use doxygen and document the functions](https://grass.osgeo.org/programming8/) directly in the source code. See `lib/gis/open.c` and `lib/gis/gislib.dox` for examples. @@ -947,7 +947,7 @@ gs.message(_("Running through {}").format(shellname)) ### Developing C tools Refer to the [online GRASS Programmer's -Manual](​https://grass.osgeo.org/programming8/) or generate it with `make +Manual](https://grass.osgeo.org/programming8/) or generate it with `make htmldocs` or `make pdfdocs`. #### Use GRASS library functions @@ -962,7 +962,7 @@ improves portability. - File seek: `G_fseek()`, `G_ftell()` - Printing: `G_asprintf()`, `G_vsaprintf()`, `G_vfaprintf()`, ... -Please refer to [the programmers manual](https://grass.osgeo.org/programming8/) +Please refer to the [programmers manual](https://grass.osgeo.org/programming8/) for the proper use (e.g., determining if any casts are needed for arguments or return values) of these library functions. They may perform a task slightly different from their corresponding C library function, and thus, their use may From a94c3b0bb42968c98d513558f458990c91e799a7 Mon Sep 17 00:00:00 2001 From: Nicklas Larsson Date: Thu, 19 Sep 2024 13:08:06 +0200 Subject: [PATCH 252/471] configure: fix default variable name for blas and lapack (#4343) In addition: improve handling of configuration with LAPACK without BLAS --- configure | 18 ++++++++++++------ configure.ac | 17 +++++++++++------ 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/configure b/configure index 17182bc4b7a..56b203e0c13 100755 --- a/configure +++ b/configure @@ -5611,7 +5611,7 @@ if test ${with_blas+y} then : withval=$with_blas; else $as_nop - with-blas="no" + with_blas=no fi @@ -5621,7 +5621,7 @@ if test ${with_lapack+y} then : withval=$with_lapack; else $as_nop - with-lapack="no" + with_lapack=no fi @@ -13719,10 +13719,10 @@ case "$with_blas" in no) ;; yes) - USE_BLAS="$with_blas" + USE_BLAS=1 ;; *) - USE_BLAS=yes + USE_BLAS=1 BLAS_PKGS="$with_blas" ;; esac @@ -13832,15 +13832,21 @@ case "$with_lapack" in no) ;; yes) - USE_LAPACK="$with_lapack" + USE_LAPACK=1 ;; *) - USE_LAPACK=yes + USE_LAPACK=1 LAPACK_PKG="$with_lapack" ;; esac # LAPACK is useless without BLAS +if test -z "$USE_BLAS" && test -n "$USE_LAPACK"; then + USE_LAPACK= + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: LAPACK is disabled, BLAS configuration is missing" >&5 +printf "%s\n" "$as_me: WARNING: LAPACK is disabled, BLAS configuration is missing" >&2;} +fi + if test -n "$USE_BLAS" && test -n "$USE_LAPACK"; then lapack_ok=no diff --git a/configure.ac b/configure.ac index 8b7413c7cea..52a985960c3 100644 --- a/configure.ac +++ b/configure.ac @@ -323,13 +323,13 @@ AC_ARG_WITH([blas], [AS_HELP_STRING([--with-blas=pkg-config-package], [enable BLAS support with by adding name of pkg-config package name for CBLAS library, e.g. '--with-lapack-package=openblas', - default: cblas])],, [with-blas="no"]) + default: cblas])],, [with_blas=no]) AC_ARG_WITH([lapack], [AS_HELP_STRING([--with-lapack=pkg-config-package], [enable LAPACK support with by adding name of pkg-config package name for LAPACKE library, e.g. '--with-lapack-package=openblas', - default: lapacke])],, [with-lapack="no"]) + default: lapacke])],, [with_lapack=no]) AC_ARG_WITH(libpng, [ --with-libpng[=path/libpng-config] @@ -1594,10 +1594,10 @@ case "$with_blas" in no) ;; yes) - USE_BLAS="$with_blas" + USE_BLAS=1 ;; *) - USE_BLAS=yes + USE_BLAS=1 BLAS_PKGS="$with_blas" ;; esac @@ -1665,15 +1665,20 @@ case "$with_lapack" in no) ;; yes) - USE_LAPACK="$with_lapack" + USE_LAPACK=1 ;; *) - USE_LAPACK=yes + USE_LAPACK=1 LAPACK_PKG="$with_lapack" ;; esac # LAPACK is useless without BLAS +if test -z "$USE_BLAS" && test -n "$USE_LAPACK"; then + USE_LAPACK= + AC_MSG_WARN([LAPACK is disabled, BLAS configuration is missing]) +fi + if test -n "$USE_BLAS" && test -n "$USE_LAPACK"; then lapack_ok=no From 5fd45e48affcc387fb4f225e24c02017125f1cb1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 19 Sep 2024 12:18:21 +0000 Subject: [PATCH 253/471] CI(deps): Update github/codeql-action action to v3.26.8 (#4344) --- .github/workflows/codeql-analysis.yml | 4 ++-- .github/workflows/python-code-quality.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index b4562dd75c8..b4fb254d11d 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -56,7 +56,7 @@ jobs: if: ${{ matrix.language == 'c-cpp' }} - name: Initialize CodeQL - uses: github/codeql-action/init@8214744c546c1e5c8f03dde8fab3a7353211988d # v3.26.7 + uses: github/codeql-action/init@294a9d92911152fe08befb9ec03e240add280cb3 # v3.26.8 with: languages: ${{ matrix.language }} config-file: ./.github/codeql/codeql-config.yml @@ -81,6 +81,6 @@ jobs: run: .github/workflows/build_ubuntu-22.04.sh "${HOME}/install" - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@8214744c546c1e5c8f03dde8fab3a7353211988d # v3.26.7 + uses: github/codeql-action/analyze@294a9d92911152fe08befb9ec03e240add280cb3 # v3.26.8 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index cd2624051a5..2ca2822d897 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -135,7 +135,7 @@ jobs: path: bandit.sarif - name: Upload SARIF File into Security Tab - uses: github/codeql-action/upload-sarif@8214744c546c1e5c8f03dde8fab3a7353211988d # v3.26.7 + uses: github/codeql-action/upload-sarif@294a9d92911152fe08befb9ec03e240add280cb3 # v3.26.8 with: sarif_file: bandit.sarif From 333d8e6989982854ee4490e2d6ffbd336390e88d Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Thu, 19 Sep 2024 09:57:06 -0400 Subject: [PATCH 254/471] v.cluster: Fix unused value assignment (#4094) Removes the lines with unused assignments with 90%. The variable eps 90% value got overwritten. The used 99% confidence interval is mentioned in the documentation. Fixes the issue identified by Coverity Scan (CID: 1270389). --- vector/v.cluster/main.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/vector/v.cluster/main.c b/vector/v.cluster/main.c index c3f95e78778..49c7535beca 100644 --- a/vector/v.cluster/main.c +++ b/vector/v.cluster/main.c @@ -286,7 +286,6 @@ int main(int argc, char *argv[]) mean = sum / n; sd = sqrt(sumsq / n - mean * mean); - eps = mean + 1.644854 * sd; /* 90% CI */ eps = mean + 2.575829 * sd; /* 99% CI */ if (eps > max) @@ -493,7 +492,6 @@ int main(int argc, char *argv[]) mean = sum / n; sd = sqrt(sumsq / n - mean * mean); - eps = mean + 1.644854 * sd; /* 90% CI */ eps = mean + 2.575829 * sd; /* 99% CI */ if (eps > max) From 6607e1cebe8b82728d0cb8ff3f69325a98fc0cbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Thu, 19 Sep 2024 17:05:04 -0400 Subject: [PATCH 255/471] CI(macOS): Cache micromamba environment on same week (#4342) * CI(macOS): Cache micromamba environment on same day * Use year and week of year for cache key date segment * Update macos.yml Co-authored-by: Vaclav Petras --------- Co-authored-by: Vaclav Petras --- .github/workflows/macos.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index b74b2b042c9..4fad9d315a7 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -45,12 +45,19 @@ jobs: # Rehash to forget about the deleted files hash -r - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - name: Get current date cache key segment + id: date + # Year and week of year so cache key changes weekly + run: echo "date=$(date +%Y-%U)" >> "${GITHUB_OUTPUT}" - name: Setup Mamba uses: mamba-org/setup-micromamba@f8b8a1e23a26f60a44c853292711bacfd3eac822 # v1.9.0 with: init-shell: bash environment-file: .github/workflows/macos_dependencies.txt environment-name: grass-env + # Persist on the same period (date). + cache-environment-key: environment-${{ steps.date.outputs.date }} + - name: Environment info shell: bash -el {0} run: | From 51f865b774a6f00e7776fae97886c59c394b796a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Thu, 19 Sep 2024 17:05:23 -0400 Subject: [PATCH 256/471] style: Bump Black's target Python versions for Python 3.9 minimum (#4347) * style: Bump Black's target Python versions * style: Format with new Black settings --- pyproject.toml | 2 +- scripts/g.extension/g.extension.py | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 992e48391ad..6689d6375eb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ requires-python = ">=3.9" [tool.black] required-version = '24' line-length = 88 -target-version = ['py38', 'py39', 'py310', 'py311', 'py312'] +target-version = ['py39', 'py310', 'py311', 'py312', 'py313'] # 'extend-exclude' excludes files or directories in addition to the defaults extend-exclude = ''' ( diff --git a/scripts/g.extension/g.extension.py b/scripts/g.extension/g.extension.py index bf8cd183135..ed0544ee2c4 100644 --- a/scripts/g.extension/g.extension.py +++ b/scripts/g.extension/g.extension.py @@ -470,9 +470,10 @@ def replace_shebang_win(python_file): cur_dir = os.path.dirname(python_file) tmp_name = os.path.join(cur_dir, gs.tempname(12)) - with codecs.open(python_file, "r", encoding="utf8") as in_file, codecs.open( - tmp_name, "w", encoding="utf8" - ) as out_file: + with ( + codecs.open(python_file, "r", encoding="utf8") as in_file, + codecs.open(tmp_name, "w", encoding="utf8") as out_file, + ): for line in in_file: new_line = line.replace( "#!/usr/bin/env python\n", "#!/usr/bin/env python3\n" From 75458f8e642a42b3ffbfbe9e69c6d50b3be76629 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Thu, 19 Sep 2024 17:05:49 -0400 Subject: [PATCH 257/471] style: Call `startswith` or `endswith` once with a `tuple` (PIE810) (#4348) style: Call `startswith` once with a `tuple` (PIE810) Ruff rule: https://docs.astral.sh/ruff/rules/multiple-starts-ends-with/ --- gui/wxpython/core/toolboxes.py | 2 +- gui/wxpython/psmap/instructions.py | 6 +----- lib/init/grass.py | 4 +--- man/build_class_graphical.py | 4 +--- pyproject.toml | 1 - utils/generate_release_notes.py | 2 +- utils/gitlog2changelog.py | 2 +- 7 files changed, 6 insertions(+), 15 deletions(-) diff --git a/gui/wxpython/core/toolboxes.py b/gui/wxpython/core/toolboxes.py index 441898fa8f6..ae6b1b6a0a6 100644 --- a/gui/wxpython/core/toolboxes.py +++ b/gui/wxpython/core/toolboxes.py @@ -855,7 +855,7 @@ def module_test(): someDiff = False for line in result: - if line.startswith("+") or line.startswith("-"): + if line.startswith(("+", "-")): sys.stdout.write(line) someDiff = True if someDiff: diff --git a/gui/wxpython/psmap/instructions.py b/gui/wxpython/psmap/instructions.py index 2ae68a91069..8a535b96704 100644 --- a/gui/wxpython/psmap/instructions.py +++ b/gui/wxpython/psmap/instructions.py @@ -1936,11 +1936,7 @@ def Read(self, instruction, text, **kwargs): instr = {} for line in text: - if ( - line.startswith("vpoints") - or line.startswith("vlines") - or line.startswith("vareas") - ): + if line.startswith(("vpoints", "vlines", "vareas")): # subtype if line.startswith("vpoints"): subType = "points" diff --git a/lib/init/grass.py b/lib/init/grass.py index 28537f957b1..30c0a1a68ba 100755 --- a/lib/init/grass.py +++ b/lib/init/grass.py @@ -1057,9 +1057,7 @@ def load_env(grass_env_file): v = v.strip('"') # we'll keep expand=True to expand $var's inside "value" because # they are within double quotes - elif ( - v.startswith("'") or v.endswith("'") or v.startswith('"') or v.endswith('"') - ): + elif v.startswith(("'", '"')) or v.endswith(("'", '"')): # here, let's try to ignore unmatching single/double quotes, which # might be a multi-line variable or just a user error debug("Ignoring multi-line environmental variable {0}".format(k)) diff --git a/man/build_class_graphical.py b/man/build_class_graphical.py index 955f5c2a63e..35b2a731cfe 100644 --- a/man/build_class_graphical.py +++ b/man/build_class_graphical.py @@ -102,9 +102,7 @@ def starts_with_module(string, module) -> bool: # module = module.replace('wxGUI.', 'g.gui.') # TODO: matches g.mapsets images for g.mapset and d.rast.num for d.rast return bool( - string.startswith(module.replace(".", "_")) - or string.startswith(module.replace(".", "")) - or string.startswith(module) + string.startswith((module.replace(".", "_"), module.replace(".", ""), module)) ) diff --git a/pyproject.toml b/pyproject.toml index 6689d6375eb..649e309a73f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -154,7 +154,6 @@ ignore = [ "PERF403", # manual-dict-comprehension "PIE794", # duplicate-class-field-definition "PIE808", # unnecessary-range-start - "PIE810", # multiple-starts-ends-with "PLC0415", # import-outside-top-level "PLC1901", # compare-to-empty-string "PLC2701", # import-private-name diff --git a/utils/generate_release_notes.py b/utils/generate_release_notes.py index 66aa4ced08c..c8a22b01d86 100755 --- a/utils/generate_release_notes.py +++ b/utils/generate_release_notes.py @@ -227,7 +227,7 @@ def notes_from_gh_api(start_tag, end_tag, branch, categories, exclude): raw_changes = lines[start_whats_changed + 1 : end_whats_changed] changes = [] for change in raw_changes: - if change.startswith("* ") or change.startswith("- "): + if change.startswith(("* ", "- ")): changes.append(change[2:]) else: changes.append(change) diff --git a/utils/gitlog2changelog.py b/utils/gitlog2changelog.py index 758b142196b..43aedcfb491 100755 --- a/utils/gitlog2changelog.py +++ b/utils/gitlog2changelog.py @@ -79,7 +79,7 @@ except Exception as e: print(f"Could not parse dateList = '{line}'. Error: {e!s}") # The Fossil-IDs are ignored: - elif line.startswith(" Fossil-ID:") or line.startswith(" [[SVN:"): + elif line.startswith((" Fossil-ID:", " [[SVN:")): continue # The svn-id lines are ignored elif " git-svn-id:" in line: From 4bb31562d99c312f402c9d7b67cfb36f8b59fa7c Mon Sep 17 00:00:00 2001 From: Tomas Zigo <50632337+tmszi@users.noreply.github.com> Date: Fri, 20 Sep 2024 10:30:51 +0200 Subject: [PATCH 258/471] wxGUI/history: fix the run of a special cmds only once (#4322) After left double mouse click on the wxGUI history tab tree cmd node. Special cmds are: ``` r"^d\..*|^r[3]?\.mapcalc$|^i.group$|^r.import$|^r.external$|^r.external.out$|" r"^v.import$|^v.external$|^v.external.out$" ``` --- gui/wxpython/history/tree.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/gui/wxpython/history/tree.py b/gui/wxpython/history/tree.py index 929f2d9f53b..74c321aeb67 100644 --- a/gui/wxpython/history/tree.py +++ b/gui/wxpython/history/tree.py @@ -557,14 +557,12 @@ def Run(self, node=None): selected_command = self.selected_command[0] command = selected_command.data["name"] - lst = re.split(r"\s+", command) if ( globalvar.ignoredCmdPattern and re.compile(globalvar.ignoredCmdPattern).search(command) and "--help" not in command and "--ui" not in command ): - self.runIgnoredCmdPattern.emit(cmd=lst) self.runIgnoredCmdPattern.emit(cmd=split(command)) return if re.compile(r"^r[3]?\.mapcalc").search(command): From 64deef26fe5fff239c178fb586b93b62cae1d75b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 20 Sep 2024 07:24:07 -0400 Subject: [PATCH 259/471] CI(deps): Update ruff to v0.6.6 (#4350) --- .github/workflows/python-code-quality.yml | 2 +- .pre-commit-config.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index 2ca2822d897..55e6cb02e53 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -36,7 +36,7 @@ jobs: # renovate: datasource=pypi depName=bandit BANDIT_VERSION: "1.7.9" # renovate: datasource=pypi depName=ruff - RUFF_VERSION: "0.6.5" + RUFF_VERSION: "0.6.6" runs-on: ${{ matrix.os }} permissions: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3fdb0741681..425205e24cf 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -37,7 +37,7 @@ repos: ) - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.6.5 + rev: v0.6.6 hooks: # Run the linter. - id: ruff From 6bc69e3d75b0b095c902e9fec9891afbeffbc59f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Fri, 20 Sep 2024 07:46:21 -0400 Subject: [PATCH 260/471] CI(OSGeo4W): Use setup-OSGeo4W action to parametrize install options (#4290) * CI(OSGeo4W): Use setup-OSGeo4W action to parametrize instal options * Remon extra double quote * Remove unnecessary space in OSGeo4W workflow file * CI: Multiline sorted OSGeo4W package list --- .github/workflows/osgeo4w.yml | 41 ++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/.github/workflows/osgeo4w.yml b/.github/workflows/osgeo4w.yml index 10acd64dc37..79a44937755 100644 --- a/.github/workflows/osgeo4w.yml +++ b/.github/workflows/osgeo4w.yml @@ -45,19 +45,34 @@ jobs: mingw-w64-x86_64-libtre-git mingw-w64-x86_64-libwinpthread-git mingw-w64-x86_64-libpng mingw-w64-x86_64-pcre - - name: Install OSGeo4W - run: | - $exe = 'osgeo4w-setup.exe' - $url = 'http://download.osgeo.org/osgeo4w/v2/' + $exe - (New-Object System.Net.WebClient).DownloadFile($url, $exe) - Start-Process ('.\'+$exe) -ArgumentList '-A -g -k -q \ - -s http://download.osgeo.org/osgeo4w/v2/ -P ${{ env.Deps }}' -Wait - env: - Deps: "proj-devel,gdal-devel,geos-devel,libtiff-devel,libpng-devel,\ - pdal-devel,netcdf-devel,cairo-devel,fftw,freetype-devel,gdal-ecw,\ - gdal-mrsid,liblas-devel,libxdr,libpq-devel,pdcurses,\ - python3-matplotlib,python3-numpy,python3-ply,python3-pywin32,\ - python3-wxpython,regex-devel,zstd-devel" + - name: Setup OSGeo4W environment + uses: echoix/setup-OSGeo4W@17deecd39e077a80bf1081443998ea8edd6f15bf # v0.1.0 + with: + package-dir: "D:/OSGeo4W_pkg" + packages: | + cairo-devel + fftw + freetype-devel + gdal-devel + gdal-ecw + gdal-mrsid + geos-devel + liblas-devel + libpng-devel + libpq-devel + libtiff-devel + libxdr + netcdf-devel + pdal-devel + pdcurses + proj-devel + python3-matplotlib + python3-numpy + python3-ply + python3-pywin32 + python3-wxpython + regex-devel + zstd-devel - name: Set number of cores for compilation run: | From bf41fc770d27e4784b971d2dd6ccf6cd31c25b0b Mon Sep 17 00:00:00 2001 From: Nicklas Larsson Date: Fri, 20 Sep 2024 16:50:02 +0200 Subject: [PATCH 261/471] lib/gmath: rename ATLAS_wrapper to CBLAS_wrapper (#4351) Rename file to reflect changes made with https://github.com/OSGeo/grass/commit/d5bb442d7b121861928a37842e05e5265a980427 --- ...{ATLAS_wrapper_blas_level_1.c => CBLAS_wrapper_blas_level_1.c} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename lib/gmath/{ATLAS_wrapper_blas_level_1.c => CBLAS_wrapper_blas_level_1.c} (100%) diff --git a/lib/gmath/ATLAS_wrapper_blas_level_1.c b/lib/gmath/CBLAS_wrapper_blas_level_1.c similarity index 100% rename from lib/gmath/ATLAS_wrapper_blas_level_1.c rename to lib/gmath/CBLAS_wrapper_blas_level_1.c From c4424ab14a4b3679b2001e376892ec711d06f856 Mon Sep 17 00:00:00 2001 From: Nicklas Larsson Date: Fri, 20 Sep 2024 16:53:16 +0200 Subject: [PATCH 262/471] lib/gmath: properly guard BLAS and LAPACK dependent code (#4346) --- lib/gmath/la.c | 41 +++++++++++++++-------------------------- 1 file changed, 15 insertions(+), 26 deletions(-) diff --git a/lib/gmath/la.c b/lib/gmath/la.c index 48a4cbc7817..5696a174bda 100644 --- a/lib/gmath/la.c +++ b/lib/gmath/la.c @@ -23,23 +23,24 @@ ******************************************************************************/ #include -#include -#include -#include -#if defined(HAVE_LIBLAPACK) && defined(HAVE_LIBBLAS) +#if defined(HAVE_LIBBLAS) && defined(HAVE_LIBLAPACK) + +#include +#include +#include +#include + #include #if defined(HAVE_CBLAS_ATLAS_H) #include #else #include -#endif -#endif +#endif // HAVE_CBLAS_ATLAS_H -#include -#include -#include -#include +#include +#include +#include static int egcmp(const void *pa, const void *pb); @@ -335,8 +336,6 @@ mat_struct *G__matrix_add(mat_struct *mt1, mat_struct *mt2, const double c1, return mt3; } -#if defined(HAVE_LIBBLAS) - /*! * \fn mat_struct *G_matrix_product (mat_struct *mt1, mat_struct *mt2) * @@ -386,8 +385,6 @@ mat_struct *G_matrix_product(mat_struct *mt1, mat_struct *mt2) return mt3; } -#endif /* defined(HAVE_LIBBLAS) */ - /*! * \fn mat_struct *G_matrix_transpose (mat_struct *mt) * @@ -442,8 +439,6 @@ mat_struct *G_matrix_transpose(mat_struct *mt) return mt1; } -#if defined(HAVE_LIBBLAS) && defined(HAVE_LIBLAPACK) - /*! * \fn int G_matrix_LU_solve (const mat_struct *mt1, mat_struct **xmat0, * const mat_struct *bmat, mat_type mtype) @@ -578,10 +573,6 @@ int G_matrix_LU_solve(const mat_struct *mt1, mat_struct **xmat0, return 0; } -#endif /* defined(HAVE_LIBBLAS) && defined(HAVE_LIBLAPACK) */ - -#if defined(HAVE_LIBBLAS) && defined(HAVE_LIBLAPACK) - /*! * \fn mat_struct *G_matrix_inverse (mat_struct *mt) * @@ -637,8 +628,6 @@ mat_struct *G_matrix_inverse(mat_struct *mt) } } -#endif /* defined(HAVE_LIBBLAS) && defined(HAVE_LIBLAPACK) */ - /*! * \fn void G_matrix_free (mat_struct *mt) * @@ -1149,8 +1138,6 @@ int G_vector_set(vec_struct *A, int cells, int ldim, vtype vt, int vindx) return 0; } -#if defined(HAVE_LIBBLAS) - /*! * \fn double G_vector_norm_euclid (vec_struct *vc) * @@ -1192,8 +1179,6 @@ double G_vector_norm_euclid(vec_struct *vc) return cblas_dnrm2(Nval, startpt, incr); } -#endif /* defined(HAVE_LIBBLAS) */ - /*! * \fn double G_vector_norm_maxval (vec_struct *vc, int vflag) * @@ -1655,3 +1640,7 @@ static int egcmp(const void *pa, const void *pb) return 0; } + +#endif // HAVE_LIBLAPACK HAVE_LIBBLAS + +typedef int suppress_empty_translation_unit_compiler_warning; From 2356520814d2ab272c308af9e89c3af466c13a13 Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Fri, 20 Sep 2024 23:05:38 -0400 Subject: [PATCH 263/471] doc: Fix most Flake8 issues in example files (#4353) This addresses F403 and F405. These are mostly related to wild card imports. This does not touch max line length issue with long tool description documentation line (current format limitation). --- .flake8 | 4 +--- doc/python/m.distance.py | 15 +++++++++++++-- doc/python/raster_example_ctypes.py | 20 +++++++++++++++++--- doc/python/vector_example_ctypes.py | 23 +++++++++++++++++++++-- 4 files changed, 52 insertions(+), 10 deletions(-) diff --git a/.flake8 b/.flake8 index e72eaa73333..bd33e493bfc 100644 --- a/.flake8 +++ b/.flake8 @@ -22,9 +22,7 @@ per-file-ignores = __init__.py: F401, F403 man/build_html.py: E501 imagery/i.atcorr/create_iwave.py: F632, F821, W293 - doc/python/raster_example_ctypes.py: F403, F405 - doc/python/vector_example_ctypes.py: F403, F405 - doc/python/m.distance.py: F403, F405, E501 + doc/python/m.distance.py: E501 doc/gui/wxpython/example/dialogs.py: F401 locale/grass_po_stats.py: E122, E128, E231, E401, E722 gui/scripts/d.wms.py: E501 diff --git a/doc/python/m.distance.py b/doc/python/m.distance.py index 5eddb1dcc56..576760c3b40 100755 --- a/doc/python/m.distance.py +++ b/doc/python/m.distance.py @@ -45,10 +45,21 @@ # %end import sys +from ctypes import byref, c_double import grass.script as gs - -from grass.lib.gis import * +from grass.lib.gis import ( + G_gisinit, + G_begin_distance_calculations, + G_scan_easting, + G_scan_northing, + G_distance, + G_begin_polygon_area_calculations, + G_area_of_polygon, + G_database_units_to_meters_factor, + G_database_unit_name, + PROJECTION_LL, +) def main(): diff --git a/doc/python/raster_example_ctypes.py b/doc/python/raster_example_ctypes.py index f62ce4a9492..67f7a2cf4cb 100644 --- a/doc/python/raster_example_ctypes.py +++ b/doc/python/raster_example_ctypes.py @@ -17,9 +17,23 @@ import os import sys - -from grass.lib.gis import * -from grass.lib.raster import * +import math + +from ctypes import POINTER, c_int, c_float, c_double, c_void_p, cast + +from grass.lib.gis import G_gisinit, G_find_raster2, G_free +from grass.lib.raster import ( + Rast_map_type, + CELL_TYPE, + FCELL_TYPE, + DCELL_TYPE, + Rast_open_old, + Rast_allocate_buf, + Rast_window_rows, + Rast_window_cols, + Rast_get_row, + Rast_close, +) # check if GRASS is running or not if "GISBASE" not in os.environ: diff --git a/doc/python/vector_example_ctypes.py b/doc/python/vector_example_ctypes.py index 9f8a90d6459..ae3877224d0 100644 --- a/doc/python/vector_example_ctypes.py +++ b/doc/python/vector_example_ctypes.py @@ -7,9 +7,28 @@ import os import sys +from ctypes import pointer, byref -from grass.lib.gis import * -from grass.lib.vector import * +# Import specific functions and classes instead of using wildcard imports +from grass.lib.gis import G_gisinit, G_find_vector2 +from grass.lib.vector import ( + Map_info, + Vect_set_open_level, + Vect_open_old, + Vect_get_full_name, + Vect_is_3d, + Vect_get_num_dblinks, + Vect_get_scale, + Vect_get_map_box, + Vect_point_in_box, + Vect_get_num_lines, + Vect_get_num_primitives, + Vect_get_num_areas, + Vect_close, + bound_box, + GV_POINT, + GV_LINE, +) if "GISBASE" not in os.environ: sys.exit("You must be in GRASS GIS to run this program.") From d89ad9e3a186a6791190f25d6cbc1e093390bb37 Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Sat, 21 Sep 2024 14:36:34 -0400 Subject: [PATCH 264/471] g.proj: Fix copy into fixed size buffer issue in input.c file (#4359) Co-authored-by: Shubham Vasudeo Desai --- general/g.proj/input.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/general/g.proj/input.c b/general/g.proj/input.c index 7f07b9bdcbf..c4232bdb9dd 100644 --- a/general/g.proj/input.c +++ b/general/g.proj/input.c @@ -230,8 +230,12 @@ int input_proj4(char *proj4params) if (fgets(buff, sizeof(buff), infd) == NULL) G_warning(_("Failed to read PROJ.4 parameter from stdin")); } - else - strcpy(buff, proj4params); + else { + if (G_strlcpy(buff, proj4params, sizeof(buff)) >= sizeof(buff)) { + G_fatal_error(_("PROJ.4 parameter string is too long: %s"), + proj4params); + } + } #if PROJ_VERSION_MAJOR >= 6 if (!strstr(buff, "+type=crs")) From 93781e51e659174b4203ce5690a6404d6af5cfbc Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 21 Sep 2024 19:21:32 +0000 Subject: [PATCH 265/471] CI(deps): Update ruff to v0.6.7 (#4360) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/python-code-quality.yml | 2 +- .pre-commit-config.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index 55e6cb02e53..92ef7d788b2 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -36,7 +36,7 @@ jobs: # renovate: datasource=pypi depName=bandit BANDIT_VERSION: "1.7.9" # renovate: datasource=pypi depName=ruff - RUFF_VERSION: "0.6.6" + RUFF_VERSION: "0.6.7" runs-on: ${{ matrix.os }} permissions: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 425205e24cf..e1f98c7a2a5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -37,7 +37,7 @@ repos: ) - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.6.6 + rev: v0.6.7 hooks: # Run the linter. - id: ruff From 5aed1a6aa939bbe6ad8a82a6313eeb6cdc8b2dc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Sat, 21 Sep 2024 18:30:35 -0400 Subject: [PATCH 266/471] tests: Re-enable passing tests from .gunittest.cfg exclusion list (#4358) * tests: Re-enable passing tests from .gunittest.cfg exclusion list * tests: Re-enable all macOS excluded tests that are passing * tests: Exclude back tests that are failing on macOS --- .github/workflows/macos_gunittest.cfg | 9 +-------- .gunittest.cfg | 7 ------- 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/.github/workflows/macos_gunittest.cfg b/.github/workflows/macos_gunittest.cfg index b6806ff5823..923ae2cbe5d 100644 --- a/.github/workflows/macos_gunittest.cfg +++ b/.github/workflows/macos_gunittest.cfg @@ -6,25 +6,18 @@ exclude = gui/wxpython/core/testsuite/test_gcmd.py gui/wxpython/core/testsuite/toolboxes.sh - lib/init/testsuite/test_grass_tmp_mapset.py - python/grass/gunittest/testsuite/test_assertions_rast3d.py python/grass/gunittest/testsuite/test_assertions_vect.py - python/grass/gunittest/testsuite/test_gmodules.py - python/grass/gunittest/testsuite/test_gunitest_doctests.py python/grass/pygrass/raster/testsuite/test_pygrass_raster_doctests.py python/grass/pygrass/rpc/testsuite/test_pygrass_rpc_doctests.py - python/grass/script/testsuite/test_script_doctests.py python/grass/temporal/testsuite/unittests_temporal_raster_algebra_equal_ts.py python/grass/temporal/testsuite/unittests_temporal_raster_conditionals_complement_else.py raster/r.in.lidar/testsuite/test_base_resolution.sh raster/r.in.pdal/testsuite/test_r_in_pdal_binning.py raster/r.in.pdal/testsuite/test_r_in_pdal_selection.py - raster/r.terraflow/testsuite/test_r_terraflow.py raster/r.sun/testsuite/test_rsun.py raster3d/r3.flow/testsuite/r3flow_test.py - scripts/g.search.modules/testsuite/test_g_search_modules.py - temporal/t.connect/testsuite/test_distr_tgis_db_raster3d.py temporal/t.connect/testsuite/test_distr_tgis_db_raster.py + temporal/t.connect/testsuite/test_distr_tgis_db_raster3d.py temporal/t.connect/testsuite/test_distr_tgis_db_vector.py temporal/t.info/testsuite/test.t.info.sh temporal/t.rast.aggregate/testsuite/test_aggregation_relative.py diff --git a/.gunittest.cfg b/.gunittest.cfg index 01b06c78913..2117fde9d7a 100644 --- a/.gunittest.cfg +++ b/.gunittest.cfg @@ -4,21 +4,14 @@ # space. This would be ideally empty or it would include just special cases, # but it includes mainly tests which can (and should) be fixed. exclude = - gui/wxpython/core/testsuite/test_gcmd.py gui/wxpython/core/testsuite/toolboxes.sh - lib/init/testsuite/test_grass_tmp_mapset.py - python/grass/gunittest/testsuite/test_assertions_rast3d.py python/grass/gunittest/testsuite/test_assertions_vect.py - python/grass/gunittest/testsuite/test_gmodules.py python/grass/gunittest/testsuite/test_gunitest_doctests.py python/grass/pygrass/raster/testsuite/test_pygrass_raster_doctests.py python/grass/pygrass/rpc/testsuite/test_pygrass_rpc_doctests.py - python/grass/script/testsuite/test_script_doctests.py python/grass/temporal/testsuite/unittests_temporal_raster_algebra_equal_ts.py python/grass/temporal/testsuite/unittests_temporal_raster_conditionals_complement_else.py raster/r.in.lidar/testsuite/test_base_resolution.sh - raster/r.in.lidar/testsuite/test_base_resolution.sh - scripts/g.search.modules/testsuite/test_g_search_modules.py temporal/t.connect/testsuite/test_distr_tgis_db_raster3d.py temporal/t.connect/testsuite/test_distr_tgis_db_raster.py temporal/t.connect/testsuite/test_distr_tgis_db_vector.py From 0c1e79bdf80b5c3f3e584c7cc5432d65e962399d Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Sat, 21 Sep 2024 19:28:51 -0400 Subject: [PATCH 267/471] checks: Move imports to the top in wxGUI/core (#4357) Moves imports to the top to fix Flake8 E402 warning for wxGUI/core. Also orders ignores alphabetically. --- .flake8 | 6 ++---- gui/wxpython/core/gcmd.py | 12 ++++++------ gui/wxpython/core/watchdog.py | 9 ++++----- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/.flake8 b/.flake8 index bd33e493bfc..ecea7b82a0b 100644 --- a/.flake8 +++ b/.flake8 @@ -26,14 +26,12 @@ per-file-ignores = doc/gui/wxpython/example/dialogs.py: F401 locale/grass_po_stats.py: E122, E128, E231, E401, E722 gui/scripts/d.wms.py: E501 - gui/wxpython/core/gcmd.py: E402 gui/wxpython/core/gconsole.py: E722 + gui/wxpython/core/render.py: E722 + gui/wxpython/core/settings.py: E722 gui/wxpython/core/toolboxes.py: E722 gui/wxpython/core/utils.py: E722 gui/wxpython/core/workspace.py: E722 - gui/wxpython/core/render.py: E722 - gui/wxpython/core/settings.py: E722 - gui/wxpython/core/watchdog.py: E402 gui/wxpython/datacatalog/tree.py: E731, E402 gui/wxpython/dbmgr/base.py: E722 gui/wxpython/dbmgr/dialogs.py: E722 diff --git a/gui/wxpython/core/gcmd.py b/gui/wxpython/core/gcmd.py index 7d4ed678d79..b93789f22f7 100644 --- a/gui/wxpython/core/gcmd.py +++ b/gui/wxpython/core/gcmd.py @@ -36,6 +36,12 @@ from threading import Thread import wx +from core.debug import Debug +from core.globalvar import SCT_EXT + +from grass.script import core as grass +from grass.script.utils import decode, encode + is_mswindows = sys.platform == "win32" if is_mswindows: from win32file import ReadFile, WriteFile @@ -45,12 +51,6 @@ import select import fcntl -from core.debug import Debug -from core.globalvar import SCT_EXT - -from grass.script import core as grass -from grass.script.utils import decode, encode - def DecodeString(string): """Decode string using system encoding diff --git a/gui/wxpython/core/watchdog.py b/gui/wxpython/core/watchdog.py index 9315a1010d6..652bc1d755d 100644 --- a/gui/wxpython/core/watchdog.py +++ b/gui/wxpython/core/watchdog.py @@ -19,6 +19,10 @@ import os import time +import wx +from wx.lib.newevent import NewEvent + +from grass.script import core as grass watchdog_used = True try: @@ -32,11 +36,6 @@ PatternMatchingEventHandler = object FileSystemEventHandler = object -import wx -from wx.lib.newevent import NewEvent - -from grass.script import core as grass - updateMapset, EVT_UPDATE_MAPSET = NewEvent() currentMapsetChanged, EVT_CURRENT_MAPSET_CHANGED = NewEvent() From 7b4a2404a4fe9fe7cda1807f8ab144b02928d661 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Sun, 22 Sep 2024 08:28:07 -0400 Subject: [PATCH 268/471] tests: Add `xfail_windows` decorator for failing tests on Windows (#4362) * grass.gunittest: Add xfail_windows decorator for Windows expected failures * lib/imagery: Add xfail_windows decorator to test functions * Fix spacing issue in xfail_windows warning message * Add stacklevel=2 to xfail_windows decorator warning message * Update utils.py Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Format end of file * Comment out warning in decorator * i.maxlik: Add xfail_windows decorator to SuccessTest class * lib/gis: Add xfail_windows decorator to TestNewlinesWithGetlFunctions class * Update warning stacklevel to 3 * Update utils.py Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update utils.py Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update utils.py * tests: Add expected failures to failing tests on windows * tests: Apply xfail_windows decorator to setUpClass in lib/gis getl tests * tests: Apply xfail_windows decorator to parser json tests in lib/gis * tests: Apply xfail_windows decorator to failing test in lib/imagery * Remove wrong import of xfail_windows * tests: Temporarily remove all gunittest exclusions * tests: Apply xfail_windows decorator to failing test in test_gcmd.py * tests: Apply xfail_windows decorator to failing test in lib/init in test_grass_tmp_mapset.py * CI(OSGeo4W): Use an OSGeo4W-specific gunittest.cfg config file * Revert "tests: Temporarily remove all gunittest exclusions" This reverts commit 20eb947d4cc0f1b2f79ffb9e16541fcb94d59a9a. * v.out.lidar: Apply xfail_windows decorator to one failing test * t.rast.series: Apply xfail_windows decorator to failing tests * t.connect: Apply xfail_windows decorator to three failing tests * g.search.modules: Apply xfail_windows decorator to a failing test * grass.temporal: Apply xfail_windows decorator to failing tests in unittests_temporal_raster_algebra_equal_ts * grass.gunittest: Apply xfail_windows decorator to failing test test_assertVectorEqualsAscii_by_import * tests: Re-enable excluded tests that are not failing on Windows * tests: Re-enable passing tests from .gunittest.cfg exclusion list * Update test_gis_lib_getl.py * Update test_raster_img.py * tests: Try re-enabling python/grass/script/testsuite/test_script_doctests.py * i.maxlik: Add xfail_windows decorator to all tests in class * tests: Exclude more tests that are failing on Windows * v.univar: Apply xfail_windows decorator to failing tests * t.rast.univar: Apply xfail_windows decorator to a failing test * r.in.gdal: Try avoiding error in calling gdal-config in skipif decorator on Windows * CI(OSGeo4W): Increase min-success to 96% for gunittest * Add line in test_grass_tmp_mapset.py * Update test_r_tileset.py --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .github/workflows/osgeo4w_gunittest.cfg | 32 +++++++++++++++++++ .github/workflows/test_thorough.bat | 2 +- gui/wxpython/core/testsuite/test_gcmd.py | 3 ++ imagery/i.maxlik/testsuite/test_i_maxlik.py | 3 ++ lib/gis/testsuite/test_parser_json.py | 5 +++ .../test_imagery_signature_management.py | 4 +++ lib/init/testsuite/test_grass_tmp_mapset.py | 5 +++ python/grass/grassdb/testsuite/test_manage.py | 5 +++ .../gunittest/testsuite/test_assertions.py | 3 ++ .../testsuite/test_assertions_vect.py | 2 ++ .../gunittest/testsuite/test_checkers.py | 2 ++ python/grass/gunittest/utils.py | 16 ++++++++++ python/grass/jupyter/testsuite/map3d_test.py | 5 +++ .../pygrass/raster/testsuite/test_category.py | 2 ++ .../pygrass/raster/testsuite/test_numpy.py | 2 ++ .../raster/testsuite/test_raster_img.py | 4 +++ .../testsuite/test_start_command_functions.py | 3 ++ python/grass/script/testsuite/test_utils.py | 2 ++ ...ttests_temporal_raster_algebra_equal_ts.py | 3 ++ raster/r.in.gdal/testsuite/test_r_in_gdal.py | 4 ++- raster/r.kappa/testsuite/test_r_kappa.py | 2 ++ raster/r.mfilter/testsuite/test_r_mfilter.py | 7 ++++ raster/r.report/testsuite/test_r_report.py | 2 ++ .../testsuite/test_addons_modules.py | 7 +++- .../testsuite/test_addons_toolboxes.py | 3 ++ .../testsuite/test_g_search_modules.py | 2 ++ scripts/r.tileset/testsuite/test_r_tileset.py | 2 ++ .../testsuite/test_v_rast_stats.py | 6 ++++ .../testsuite/test_distr_tgis_db_raster.py | 3 +- .../testsuite/test_distr_tgis_db_raster3d.py | 3 +- .../testsuite/test_distr_tgis_db_vector.py | 3 +- .../t.rast.gapfill/testsuite/test_gapfill.py | 6 ++++ .../t.rast.series/testsuite/test_series.py | 3 ++ .../testsuite/test_t_rast_univar.py | 10 ++++++ temporal/t.rast.what/testsuite/test_what.py | 5 +++ .../testsuite/test_t_rast3d_univar.py | 5 +++ .../v.out.lidar/testsuite/test_v_out_lidar.py | 2 ++ vector/v.univar/testsuite/test_v_univar.py | 5 +++ 38 files changed, 177 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/osgeo4w_gunittest.cfg diff --git a/.github/workflows/osgeo4w_gunittest.cfg b/.github/workflows/osgeo4w_gunittest.cfg new file mode 100644 index 00000000000..b9252cf49ef --- /dev/null +++ b/.github/workflows/osgeo4w_gunittest.cfg @@ -0,0 +1,32 @@ +[gunittest] + +# Files (or wildcard patterns) to exclude from testing separated by newline or +# space. This would be ideally empty or it would include just special cases, +# but it includes mainly tests which can (and should) be fixed. +exclude = + gui/wxpython/core/testsuite/toolboxes.sh + lib/init/testsuite/test_grass_tmp_mapset.py + python/grass/gunittest/testsuite/test_gunitest_doctests.py + python/grass/pygrass/raster/testsuite/test_pygrass_raster_doctests.py + python/grass/pygrass/rpc/testsuite/test_pygrass_rpc_doctests.py + python/grass/script/testsuite/test_script_doctests.py + python/grass/temporal/testsuite/unittests_temporal_raster_conditionals_complement_else.py + temporal/t.info/testsuite/test.t.info.sh + temporal/t.rast.accdetect/testsuite/test.t.rast.accdetect.reverse.sh + temporal/t.rast.accdetect/testsuite/test.t.rast.accdetect.sh + temporal/t.rast.aggregate/testsuite/test_aggregation_relative.py + vector/v.edit/testsuite/select_all_flag.sh + vector/v.in.lidar/testsuite/decimation_test.py + vector/v.in.lidar/testsuite/mask_test.py + vector/v.in.lidar/testsuite/test_v_in_lidar_basic.py + vector/v.in.lidar/testsuite/test_v_in_lidar_filter.py + vector/v.what.rast3/testsuite/test.v.what.rast3.sh + vector/v.what/testsuite/test_vwhat_layers.py + vector/v.what/testsuite/test_vwhat_ncspm.py + +# Maximum time for execution of one test file (not a test function) +# after which test is terminated (which may not terminate child processes +# from that test). +# Using 5 minutes as maximum (average test time should be much shorter, +# couple seconds per file, median should be around one second). +timeout = 300 diff --git a/.github/workflows/test_thorough.bat b/.github/workflows/test_thorough.bat index 40df9534a20..843adb4c4f0 100644 --- a/.github/workflows/test_thorough.bat +++ b/.github/workflows/test_thorough.bat @@ -2,4 +2,4 @@ set grass=%1 set python=%2 call %grass% --tmp-project XY --exec g.download.project url=https://grass.osgeo.org/sampledata/north_carolina/nc_spm_full_v2alpha2.tar.gz path=%USERPROFILE% -call %grass% --tmp-project XY --exec %python% -m grass.gunittest.main --grassdata %USERPROFILE% --location nc_spm_full_v2alpha2 --location-type nc --min-success 86 +call %grass% --tmp-project XY --exec %python% -m grass.gunittest.main --grassdata %USERPROFILE% --location nc_spm_full_v2alpha2 --location-type nc --min-success 96 --config .github\workflows\osgeo4w_gunittest.cfg diff --git a/gui/wxpython/core/testsuite/test_gcmd.py b/gui/wxpython/core/testsuite/test_gcmd.py index 121ede99b77..75d3bf5932b 100644 --- a/gui/wxpython/core/testsuite/test_gcmd.py +++ b/gui/wxpython/core/testsuite/test_gcmd.py @@ -1,5 +1,6 @@ from grass.gunittest.case import TestCase from grass.gunittest.main import test +from grass.gunittest.utils import xfail_windows class Rcv: @@ -16,6 +17,8 @@ def recv(self): class Recv_SomeTest(TestCase): + + @xfail_windows def test_decode(self): """ Multibyte chars should not be split diff --git a/imagery/i.maxlik/testsuite/test_i_maxlik.py b/imagery/i.maxlik/testsuite/test_i_maxlik.py index 1f6829e92b4..e7fbf6c1a79 100644 --- a/imagery/i.maxlik/testsuite/test_i_maxlik.py +++ b/imagery/i.maxlik/testsuite/test_i_maxlik.py @@ -19,6 +19,7 @@ from grass.gunittest.case import TestCase from grass.gunittest.main import test +from grass.gunittest.utils import xfail_windows from grass.lib.gis import G_mapset_path from grass.lib.raster import Rast_write_semantic_label @@ -172,6 +173,7 @@ def tearDownClass(cls): ) cls.runModule("g.remove", flags="f", type="group", name=cls.group, quiet=True) + @xfail_windows def test_v1(self): """Test v1 signature""" self.assertModule( @@ -193,6 +195,7 @@ def test_v1(self): self.assertEqual(res.get_cat(0)[1], 1) res.close() + @xfail_windows def test_v2(self): """Test v2 signature""" self.assertModule( diff --git a/lib/gis/testsuite/test_parser_json.py b/lib/gis/testsuite/test_parser_json.py index 918fb453d04..74ff9673c7f 100644 --- a/lib/gis/testsuite/test_parser_json.py +++ b/lib/gis/testsuite/test_parser_json.py @@ -10,11 +10,14 @@ import subprocess from grass.gunittest.case import TestCase +from grass.gunittest.utils import xfail_windows from grass.script import decode import json class TestParserJson(TestCase): + + @xfail_windows def test_r_slope_aspect_json(self): args = [ "r.slope.aspect", @@ -58,6 +61,7 @@ def test_r_slope_aspect_json(self): self.assertEqual(json_code["inputs"], inputs) self.assertEqual(json_code["outputs"], outputs) + @xfail_windows def test_v_out_ascii(self): args = [ "v.out.ascii", @@ -91,6 +95,7 @@ def test_v_out_ascii(self): self.assertEqual(json_code["inputs"], inputs) self.assertEqual(json_code["outputs"], outputs) + @xfail_windows def test_v_info(self): args = ["v.info", "map=hospitals@PERMANENT", "-c", "--json"] diff --git a/lib/imagery/testsuite/test_imagery_signature_management.py b/lib/imagery/testsuite/test_imagery_signature_management.py index 7b1dc8bdc57..42c55c45123 100644 --- a/lib/imagery/testsuite/test_imagery_signature_management.py +++ b/lib/imagery/testsuite/test_imagery_signature_management.py @@ -15,6 +15,7 @@ from grass.gunittest.case import TestCase from grass.gunittest.main import test +from grass.gunittest.utils import xfail_windows from grass.script.core import tempname import grass.script as gs @@ -44,16 +45,19 @@ class GetSignaturesDirTestCase(TestCase): + @xfail_windows def test_get_sig(self): cdir = ctypes.create_string_buffer(GNAME_MAX) I_get_signatures_dir(cdir, I_SIGFILE_TYPE_SIG) self.assertEqual(utils.decode(cdir.value), f"signatures{HOST_DIRSEP}sig") + @xfail_windows def test_get_sigset(self): cdir = ctypes.create_string_buffer(GNAME_MAX) I_get_signatures_dir(cdir, I_SIGFILE_TYPE_SIGSET) self.assertEqual(utils.decode(cdir.value), f"signatures{HOST_DIRSEP}sigset") + @xfail_windows def test_get_libsvm(self): elem = ctypes.create_string_buffer(GNAME_MAX) I_get_signatures_dir(elem, I_SIGFILE_TYPE_LIBSVM) diff --git a/lib/init/testsuite/test_grass_tmp_mapset.py b/lib/init/testsuite/test_grass_tmp_mapset.py index 7a488dadb5f..9379957cd3f 100644 --- a/lib/init/testsuite/test_grass_tmp_mapset.py +++ b/lib/init/testsuite/test_grass_tmp_mapset.py @@ -18,6 +18,7 @@ import os import shutil import subprocess +from grass.gunittest.utils import xfail_windows # Note that unlike rest of GRASS GIS, here we are using unittest package @@ -43,6 +44,7 @@ def tearDown(self): """Deletes the location""" shutil.rmtree(self.location, ignore_errors=True) + @xfail_windows def test_command_runs(self): """Check that correct parameters are accepted""" return_code = subprocess.call( @@ -57,6 +59,7 @@ def test_command_runs(self): ), ) + @xfail_windows def test_command_fails_without_location(self): """Check that the command fails with a nonexistent location""" return_code = subprocess.call( @@ -78,6 +81,7 @@ def test_command_fails_without_location(self): ), ) + @xfail_windows def test_mapset_metadata_correct(self): """Check that metadata is readable and have expected value (XY CRS)""" output = subprocess.check_output( @@ -91,6 +95,7 @@ def test_mapset_metadata_correct(self): ), ) + @xfail_windows def test_mapset_deleted(self): """Check that mapset is deleted at the end of execution""" subprocess.check_call( diff --git a/python/grass/grassdb/testsuite/test_manage.py b/python/grass/grassdb/testsuite/test_manage.py index a2788d4f1a8..43b8945bd31 100644 --- a/python/grass/grassdb/testsuite/test_manage.py +++ b/python/grass/grassdb/testsuite/test_manage.py @@ -18,6 +18,7 @@ from grass.gunittest.case import TestCase from grass.gunittest.gmodules import call_module from grass.gunittest.main import test +from grass.gunittest.utils import xfail_windows class TestMapsetPath(TestCase): @@ -38,6 +39,7 @@ def test_mapset_from_path_object(self): self.assertEqual(mapset_path.mapset, mapset_name) self.assertEqual(mapset_path.path, Path(path) / location_name / mapset_name) + @xfail_windows def test_mapset_from_str(self): """Check with path from str and database directory as Path""" path = "does/not/exist" @@ -60,6 +62,7 @@ def test_mapset_from_str(self): class TestSplitMapsetPath(TestCase): """Check that split works with different parameters""" + @xfail_windows def test_split_path(self): """Check that pathlib.Path is correctly split""" ref_db = "does/not/exist" @@ -71,6 +74,7 @@ def test_split_path(self): self.assertEqual(new_location, ref_location) self.assertEqual(new_mapset, ref_mapset) + @xfail_windows def test_split_str(self): """Check that path as str is correctly split""" ref_db = "does/not/exist" @@ -82,6 +86,7 @@ def test_split_str(self): self.assertEqual(new_location, ref_location) self.assertEqual(new_mapset, ref_mapset) + @xfail_windows def test_split_str_trailing_slash(self): """Check that path as str with a trailing slash is correctly split""" ref_db = "does/not/exist" diff --git a/python/grass/gunittest/testsuite/test_assertions.py b/python/grass/gunittest/testsuite/test_assertions.py index 3af538132f3..ed27c0e54fb 100644 --- a/python/grass/gunittest/testsuite/test_assertions.py +++ b/python/grass/gunittest/testsuite/test_assertions.py @@ -11,6 +11,7 @@ from grass.gunittest.case import TestCase from grass.gunittest.main import test from grass.gunittest.gmodules import SimpleModule +from grass.gunittest.utils import xfail_windows class TestTextAssertions(TestCase): @@ -34,6 +35,7 @@ def test_assertLooksLike(self): def test_assertLooksLike_multiline(self): self.assertLooksLike("a=123\nb=456\nc=789", "a=...\nb=...\nc=...") + @xfail_windows def test_assertLooksLike_multiline_platform_dependent(self): self.assertLooksLike( "a=123\nb=456\nc=789", "a=...{nl}b=...{nl}c=...".format(nl=os.linesep) @@ -384,6 +386,7 @@ def test_assertFileExists_empty_file(self): self.failureException, self.assertFileExists, filename=self.emtpy_file ) + @xfail_windows def test_assertFileMd5(self): self.assertFileMd5(filename=self.file_with_md5, md5=self.file_md5) self.assertRaises( diff --git a/python/grass/gunittest/testsuite/test_assertions_vect.py b/python/grass/gunittest/testsuite/test_assertions_vect.py index 8b3288ad2ca..0a73580ba58 100644 --- a/python/grass/gunittest/testsuite/test_assertions_vect.py +++ b/python/grass/gunittest/testsuite/test_assertions_vect.py @@ -5,6 +5,7 @@ from grass.exceptions import CalledModuleError from grass.gunittest.case import TestCase from grass.gunittest.main import test +from grass.gunittest.utils import xfail_windows V_UNIVAR_SCHOOLS_WIDTH_SUBSET = """n=144 @@ -281,6 +282,7 @@ def test_assertVectorAsciiEqualsVectorAscii_diff_content(self): self.assertFileExists(self.simple_base_file) self.assertFileExists(self.simple_modified_file) + @xfail_windows def test_assertVectorEqualsAscii_by_import(self): amap = "simple_vector_map_imported_base" self.runModule( diff --git a/python/grass/gunittest/testsuite/test_checkers.py b/python/grass/gunittest/testsuite/test_checkers.py index d35ffcb19d8..b11b25778a3 100644 --- a/python/grass/gunittest/testsuite/test_checkers.py +++ b/python/grass/gunittest/testsuite/test_checkers.py @@ -24,6 +24,7 @@ file_md5, text_file_md5, ) +from grass.gunittest.utils import xfail_windows class TestValuesEqual(TestCase): @@ -386,6 +387,7 @@ def tearDownClass(cls): try_remove(cls.correct_file_name_unix_nl) try_remove(cls.wrong_file_name) + @xfail_windows def test_text_file_binary(self): r"""File with ``\n`` (LF) newlines as binary (MD5 has ``\n``).""" self.assertEqual( diff --git a/python/grass/gunittest/utils.py b/python/grass/gunittest/utils.py index c1afea3d5ad..5313dbb9bc8 100644 --- a/python/grass/gunittest/utils.py +++ b/python/grass/gunittest/utils.py @@ -14,6 +14,8 @@ from pathlib import Path import shutil import sys +from unittest import expectedFailure +import warnings def ensure_dir(directory): @@ -80,3 +82,17 @@ def safe_repr(obj, short=False): if not short or len(result) < _MAX_LENGTH: return result return result[:_MAX_LENGTH] + " [truncated]..." + + +def xfail_windows(test_item): + """Marks a test as an expected failure or error only on Windows + Equivalent to applying @unittest.expectedFailure only when running + on Windows. + """ + if not sys.platform.startswith("win"): + return lambda func: func + warnings.warn( + "Once the test is fixed and passing, remove the @xfail_windows decorator", + stacklevel=2, + ) + return expectedFailure(test_item) diff --git a/python/grass/jupyter/testsuite/map3d_test.py b/python/grass/jupyter/testsuite/map3d_test.py index b28b6a03a5b..5f88fad24e7 100644 --- a/python/grass/jupyter/testsuite/map3d_test.py +++ b/python/grass/jupyter/testsuite/map3d_test.py @@ -26,6 +26,7 @@ import grass.jupyter as gj from grass.gunittest.case import TestCase from grass.gunittest.main import test +from grass.gunittest.utils import xfail_windows def can_import_ipython(): @@ -81,12 +82,14 @@ def tearDown(self): else: file.unlink(missing_ok=True) + @xfail_windows def test_defaults(self): """Check that default settings work""" renderer = gj.Map3D() renderer.render(elevation_map="elevation", color_map="elevation") self.assertFileExists(renderer.filename) + @xfail_windows def test_filename(self): """Check that custom filename works""" custom_filename = "test_filename.png" @@ -96,12 +99,14 @@ def test_filename(self): renderer.render(elevation_map="elevation", color_map="elevation") self.assertFileExists(custom_filename) + @xfail_windows def test_hw(self): """Check that custom width and height works""" renderer = gj.Map3D(width=200, height=400) renderer.render(elevation_map="elevation", color_map="elevation") self.assertFileExists(renderer.filename) + @xfail_windows def test_overlay(self): """Check that overlay works""" renderer = gj.Map3D() diff --git a/python/grass/pygrass/raster/testsuite/test_category.py b/python/grass/pygrass/raster/testsuite/test_category.py index 527c43b894f..113264c82f6 100644 --- a/python/grass/pygrass/raster/testsuite/test_category.py +++ b/python/grass/pygrass/raster/testsuite/test_category.py @@ -6,6 +6,7 @@ from grass.gunittest.case import TestCase from grass.gunittest.main import test +from grass.gunittest.utils import xfail_windows from grass.pygrass.raster import RasterRow from grass.pygrass.raster.category import Category @@ -76,6 +77,7 @@ def testFirstCat(self): self.assertEqual(cats[7], cat7) self.assertEqual(cats[15], cat15) + @xfail_windows def testWrite(self): tmpfile = tempfile(False) cats = Category(self.name) diff --git a/python/grass/pygrass/raster/testsuite/test_numpy.py b/python/grass/pygrass/raster/testsuite/test_numpy.py index 5f0b2309544..b23926d8ba7 100644 --- a/python/grass/pygrass/raster/testsuite/test_numpy.py +++ b/python/grass/pygrass/raster/testsuite/test_numpy.py @@ -6,6 +6,7 @@ from grass.gunittest.case import TestCase from grass.gunittest.main import test +from grass.gunittest.utils import xfail_windows from numpy.random import default_rng from grass.pygrass.raster import raster2numpy, numpy2raster, RasterRow @@ -48,6 +49,7 @@ def test_len(self): self.assertTrue(len(self.numpy_obj), 40) self.assertTrue(len(self.numpy_obj[0]), 60) + @xfail_windows def test_write(self): rng = default_rng() numpy2raster(rng.random([40, 60]), "FCELL", self.name, True) diff --git a/python/grass/pygrass/raster/testsuite/test_raster_img.py b/python/grass/pygrass/raster/testsuite/test_raster_img.py index bbc1cb2dabd..b873b16a86b 100644 --- a/python/grass/pygrass/raster/testsuite/test_raster_img.py +++ b/python/grass/pygrass/raster/testsuite/test_raster_img.py @@ -3,6 +3,7 @@ from grass.gunittest.case import TestCase from grass.gunittest.main import test +from grass.gunittest.utils import xfail_windows from grass.pygrass.raster import raster2numpy_img from grass.pygrass.gis.region import Region @@ -149,6 +150,7 @@ def test_resampling_to_numpy_img_1(self): self.assertEqual(len(a), region.rows * region.cols * 4) + @xfail_windows def test_resampling_to_numpy_img_2(self): region = Region() region.ewres = 1 @@ -159,6 +161,7 @@ def test_resampling_to_numpy_img_2(self): self.assertEqual(len(a), region.rows * region.cols * 4) + @xfail_windows def test_resampling_to_numpy_img_3(self): region = Region() region.ewres = 0.4 @@ -169,6 +172,7 @@ def test_resampling_to_numpy_img_3(self): self.assertEqual(len(a), region.rows * region.cols * 1) + @xfail_windows def test_resampling_to_numpy_img_4(self): region = Region() region.ewres = 0.1 diff --git a/python/grass/script/testsuite/test_start_command_functions.py b/python/grass/script/testsuite/test_start_command_functions.py index 414a68cd270..368669263a8 100644 --- a/python/grass/script/testsuite/test_start_command_functions.py +++ b/python/grass/script/testsuite/test_start_command_functions.py @@ -4,6 +4,7 @@ from grass.gunittest.case import TestCase from grass.gunittest.main import test +from grass.gunittest.utils import xfail_windows from grass.script.core import start_command, PIPE, run_command, write_command from grass.script.core import read_command, find_program @@ -85,6 +86,7 @@ def setUpClass(cls): def tearDownClass(cls): cls.runModule("g.remove", type="raster", name=cls.raster, flags="f") + @xfail_windows def test_write_labels_unicode(self): """This tests if Python module works""" find_program("ls", "--version") @@ -99,6 +101,7 @@ def test_write_labels_unicode(self): self.assertEqual(res, "1:kůň\n2:kráva\n3:ovečka\n4:býk") self.assertIsInstance(res, str) + @xfail_windows def test_write_labels_bytes(self): """This tests if Python module works""" write_command( diff --git a/python/grass/script/testsuite/test_utils.py b/python/grass/script/testsuite/test_utils.py index db12041d66d..67d2c59dd69 100644 --- a/python/grass/script/testsuite/test_utils.py +++ b/python/grass/script/testsuite/test_utils.py @@ -2,6 +2,7 @@ from grass.gunittest.case import TestCase from grass.gunittest.main import test +from grass.gunittest.utils import xfail_windows from grass.script import utils @@ -39,6 +40,7 @@ def test_bytes(self): def test_unicode(self): self.assertEqual(b"text", utils.encode("text")) + @xfail_windows def test_bytes_garbage_in_out(self): """If the input is bytes we should not touch it for encoding""" self.assertEqual( diff --git a/python/grass/temporal/testsuite/unittests_temporal_raster_algebra_equal_ts.py b/python/grass/temporal/testsuite/unittests_temporal_raster_algebra_equal_ts.py index 92397cd825c..f96ed12899f 100644 --- a/python/grass/temporal/testsuite/unittests_temporal_raster_algebra_equal_ts.py +++ b/python/grass/temporal/testsuite/unittests_temporal_raster_algebra_equal_ts.py @@ -12,6 +12,7 @@ import grass.temporal as tgis from grass.gunittest.case import TestCase from grass.gunittest.main import test +from grass.gunittest.utils import xfail_windows class TestTemporalRasterAlgebraImplicitAggregation(TestCase): @@ -64,6 +65,7 @@ def tearDownClass(cls): cls.runModule("t.unregister", maps="singletmap", quiet=True) cls.del_temp_region() + @xfail_windows def test_simple_operator(self): """Test implicit aggregation @@ -149,6 +151,7 @@ def test_single_map_complex_operator(self): self.assertEqual(D.check_temporal_topology(), True) self.assertEqual(D.get_granularity(), None) + @xfail_windows def test_single_map_simple_operator(self): """Test implicit aggregation diff --git a/raster/r.in.gdal/testsuite/test_r_in_gdal.py b/raster/r.in.gdal/testsuite/test_r_in_gdal.py index 355e9b74eda..2390e9838cd 100644 --- a/raster/r.in.gdal/testsuite/test_r_in_gdal.py +++ b/raster/r.in.gdal/testsuite/test_r_in_gdal.py @@ -4,6 +4,7 @@ """ import unittest +import sys from subprocess import check_output @@ -311,7 +312,8 @@ def test_netCDF_3d_5(self): self.assertLooksLike(map_list, text_from_file) @unittest.skipIf( - tuple( + not sys.platform.startswith("win") + and tuple( map( int, check_output(["gdal-config", "--version"]) diff --git a/raster/r.kappa/testsuite/test_r_kappa.py b/raster/r.kappa/testsuite/test_r_kappa.py index 56e356ca20b..620a4f4979b 100644 --- a/raster/r.kappa/testsuite/test_r_kappa.py +++ b/raster/r.kappa/testsuite/test_r_kappa.py @@ -21,6 +21,7 @@ from grass.gunittest.case import TestCase from grass.gunittest.main import test from grass.gunittest.checkers import keyvalue_equals +from grass.gunittest.utils import xfail_windows class MatrixCorrectnessTest(TestCase): @@ -474,6 +475,7 @@ def test_stdout(self): keyvalue_equals(self.expected_outputs[i], json_out, precision=4) ) + @xfail_windows def test_file(self): for i in range(len(self.references)): f = NamedTemporaryFile() diff --git a/raster/r.mfilter/testsuite/test_r_mfilter.py b/raster/r.mfilter/testsuite/test_r_mfilter.py index 3d1764b4894..c18aa7c9273 100644 --- a/raster/r.mfilter/testsuite/test_r_mfilter.py +++ b/raster/r.mfilter/testsuite/test_r_mfilter.py @@ -1,6 +1,7 @@ from tempfile import NamedTemporaryFile from grass.gunittest.case import TestCase from grass.gunittest.main import test +from grass.gunittest.utils import xfail_windows class TestNeighbors(TestCase): @@ -202,6 +203,7 @@ def tearDownClass(cls): "g.remove", flags="f", type="raster", name=",".join(cls.to_remove) ) + @xfail_windows def test_sequential(self): """Test output with sequential filter type.""" test_case = "test_sequential" @@ -235,6 +237,7 @@ def test_sequential(self): precision=1e-5, ) + @xfail_windows def test_parallel(self): """Test output with parallel filter type.""" test_case = "test_parallel" @@ -268,6 +271,7 @@ def test_parallel(self): precision=1e-5, ) + @xfail_windows def test_sequential_null(self): """Test output with sequential filter type with null mode enabled.""" test_case = "test_sequential_null" @@ -301,6 +305,7 @@ def test_sequential_null(self): precision=1e-5, ) + @xfail_windows def test_parallel_null(self): """Test output with parallel filter type with null mode enabled.""" test_case = "test_parallel_null" @@ -361,6 +366,7 @@ def test_parallel_null(self): precision=1e-5, ) + @xfail_windows def test_multiple_filters(self): """Test output with multiple filters.""" test_case = "test_multiple_filters" @@ -394,6 +400,7 @@ def test_multiple_filters(self): precision=1e-5, ) + @xfail_windows def test_repeated_filters(self): """Test output with repeated filters.""" test_case = "test_repeated_filters" diff --git a/raster/r.report/testsuite/test_r_report.py b/raster/r.report/testsuite/test_r_report.py index 53e49fdc174..86a16c27adb 100644 --- a/raster/r.report/testsuite/test_r_report.py +++ b/raster/r.report/testsuite/test_r_report.py @@ -17,6 +17,7 @@ from grass.gunittest.case import TestCase from grass.gunittest.gmodules import SimpleModule +from grass.gunittest.utils import xfail_windows class TestRasterreport(TestCase): @@ -294,6 +295,7 @@ def test_json(self): data = json.loads(module.outputs.stdout) self._assert_report_equal(reference, data) + @xfail_windows def test_json2(self): """Test JSON format with more options""" reference = { diff --git a/scripts/g.extension/testsuite/test_addons_modules.py b/scripts/g.extension/testsuite/test_addons_modules.py index c1cab5c954a..9b99e7ac2ae 100644 --- a/scripts/g.extension/testsuite/test_addons_modules.py +++ b/scripts/g.extension/testsuite/test_addons_modules.py @@ -15,7 +15,7 @@ from grass.gunittest.case import TestCase from grass.gunittest.main import test from grass.gunittest.gmodules import SimpleModule -from grass.gunittest.utils import silent_rmtree +from grass.gunittest.utils import silent_rmtree, xfail_windows from grass.script.utils import decode import os @@ -52,6 +52,7 @@ class TestModulesMetadata(TestCase): url = "file://" + os.path.abspath("data") + @xfail_windows def test_listing(self): """List individual extensions/modules/addons""" module = SimpleModule("g.extension", flags="l", url=self.url) @@ -90,6 +91,7 @@ def tearDown(self): """Remove created files""" silent_rmtree(self.install_prefix) + @xfail_windows def test_directory_install(self): """Test installing extension from directory""" self.assertModule( @@ -102,6 +104,7 @@ def test_directory_install(self): for file in self.files: self.assertFileExists(file) + @xfail_windows def test_targz_install(self): """Test installing extension from local .tar.gz""" self.assertModule( @@ -113,6 +116,7 @@ def test_targz_install(self): for file in self.files: self.assertFileExists(file) + @xfail_windows def test_remote_targz_without_dir_install(self): """Test installing extension from (remote) .tar.gz without main dir""" self.assertModule( @@ -125,6 +129,7 @@ def test_remote_targz_without_dir_install(self): for file in self.files: self.assertFileExists(file) + @xfail_windows def test_remote_zip_install(self): """Test installing extension from .zip specified by URL (local)""" self.assertModule( diff --git a/scripts/g.extension/testsuite/test_addons_toolboxes.py b/scripts/g.extension/testsuite/test_addons_toolboxes.py index 5cbc0454a41..17d7e4b5b74 100644 --- a/scripts/g.extension/testsuite/test_addons_toolboxes.py +++ b/scripts/g.extension/testsuite/test_addons_toolboxes.py @@ -15,9 +15,11 @@ from grass.gunittest.case import TestCase from grass.gunittest.main import test from grass.gunittest.gmodules import SimpleModule +from grass.gunittest.utils import xfail_windows import os + FULL_TOOLBOXES_OUTPUT = """\ Hydrology (HY) * r.stream.basins @@ -39,6 +41,7 @@ class TestToolboxesMetadata(TestCase): url = "file://" + os.path.abspath("data") + @xfail_windows def test_listing(self): """List toolboxes and their content""" module = SimpleModule("g.extension", flags="lt", url=self.url) diff --git a/scripts/g.search.modules/testsuite/test_g_search_modules.py b/scripts/g.search.modules/testsuite/test_g_search_modules.py index d280ffc1441..75537eb8d78 100644 --- a/scripts/g.search.modules/testsuite/test_g_search_modules.py +++ b/scripts/g.search.modules/testsuite/test_g_search_modules.py @@ -15,6 +15,7 @@ from grass.gunittest.case import TestCase from grass.gunittest.main import test from grass.gunittest.gmodules import SimpleModule +from grass.gunittest.utils import xfail_windows from grass.script.utils import decode import unittest @@ -65,6 +66,7 @@ def test_colored_terminal(self): stdout = decode(module.outputs.stdout).split() self.assertEqual(stdout[0], termcolor.colored("r.basins.fill", attrs=["bold"])) + @xfail_windows def test_manual_pages(self): module = SimpleModule("g.search.modules", keyword="kapri", flags="gm") self.assertModule(module) diff --git a/scripts/r.tileset/testsuite/test_r_tileset.py b/scripts/r.tileset/testsuite/test_r_tileset.py index 7f1c62ed1f5..cba9908d900 100644 --- a/scripts/r.tileset/testsuite/test_r_tileset.py +++ b/scripts/r.tileset/testsuite/test_r_tileset.py @@ -7,6 +7,7 @@ from grass.gunittest.case import TestCase from grass.gunittest.main import test from grass.gunittest.gmodules import SimpleModule +from grass.gunittest.utils import xfail_windows from grass.script.utils import decode @@ -36,6 +37,7 @@ def tearDownClass(cls): """!Remove the temporary region""" cls.del_temp_region() + @xfail_windows def test_tiling(self): """Produce tiling test""" module = SimpleModule( diff --git a/scripts/v.rast.stats/testsuite/test_v_rast_stats.py b/scripts/v.rast.stats/testsuite/test_v_rast_stats.py index ca3111d14d8..56acbce5964 100644 --- a/scripts/v.rast.stats/testsuite/test_v_rast_stats.py +++ b/scripts/v.rast.stats/testsuite/test_v_rast_stats.py @@ -5,6 +5,7 @@ from grass.gunittest.case import TestCase from grass.gunittest.gmodules import SimpleModule +from grass.gunittest.utils import xfail_windows from grass.pygrass.vector import VectorTopo from grass.pygrass.vector.geometry import Line from grass.pygrass.vector.geometry import Boundary @@ -71,6 +72,7 @@ def setUp(self): vt.table.conn.commit() vt.close() + @xfail_windows def test_1(self): # Output of v.rast.stats univar_string = """cat|value|label|a_minimum|a_maximum|a_sum @@ -91,6 +93,7 @@ def test_1(self): self.runModule(v_db_select) self.assertLooksLike(univar_string, str(v_db_select.outputs.stdout)) + @xfail_windows def test_line_d(self): output_str = """cat|name|a_median|a_number|a_range 1|first|192|3|1 @@ -109,6 +112,7 @@ def test_line_d(self): self.runModule(v_db_select) self.assertLooksLike(output_str, str(v_db_select.outputs.stdout)) + @xfail_windows def test_line(self): output_str = """cat|name|a_median|a_number|a_range 1|first|192|5|2 @@ -128,6 +132,7 @@ def test_line(self): self.runModule(v_db_select) self.assertLooksLike(output_str, str(v_db_select.outputs.stdout)) + @xfail_windows def test_zone_all(self): # Output of v.rast.stats univar_string = """cat|value|label|a_number|a_null_cells|a_minimum|a_maximum|a_range|a_average|a_stddev|a_variance|a_coeff_var|a_sum|a_first_quartile|a_median|a_third_quartile|a_percentile_90 @@ -143,6 +148,7 @@ def test_zone_all(self): self.runModule(v_db_select) self.assertLooksLike(univar_string, str(v_db_select.outputs.stdout)) + @xfail_windows def test_small_area_with_centroid(self): # Output of v.rast.stats univar_string = """cat|name|a_number|a_null_cells|a_minimum|a_maximum|a_range|a_average|a_stddev|a_variance|a_coeff_var|a_sum|a_first_quartile|a_median|a_third_quartile|a_percentile_90 diff --git a/temporal/t.connect/testsuite/test_distr_tgis_db_raster.py b/temporal/t.connect/testsuite/test_distr_tgis_db_raster.py index 412a926f01d..6e4d76b26c5 100644 --- a/temporal/t.connect/testsuite/test_distr_tgis_db_raster.py +++ b/temporal/t.connect/testsuite/test_distr_tgis_db_raster.py @@ -13,7 +13,7 @@ from grass.gunittest.case import TestCase from grass.gunittest.gmodules import SimpleModule -from grass.gunittest.utils import silent_rmtree +from grass.gunittest.utils import silent_rmtree, xfail_windows class TestRasterExtraction(TestCase): @@ -254,6 +254,7 @@ def test_strds_info(self): module=info, reference=tinfo_string, precision=2, sep="=" ) + @xfail_windows def test_raster_info(self): self.runModule("g.mapset", mapset="test3") tinfo_string = """id=a1@test1 diff --git a/temporal/t.connect/testsuite/test_distr_tgis_db_raster3d.py b/temporal/t.connect/testsuite/test_distr_tgis_db_raster3d.py index 23bc11c271e..f9b4df76d88 100644 --- a/temporal/t.connect/testsuite/test_distr_tgis_db_raster3d.py +++ b/temporal/t.connect/testsuite/test_distr_tgis_db_raster3d.py @@ -13,7 +13,7 @@ from grass.gunittest.case import TestCase from grass.gunittest.gmodules import SimpleModule -from grass.gunittest.utils import silent_rmtree +from grass.gunittest.utils import silent_rmtree, xfail_windows class testRaster3dExtraction(TestCase): @@ -242,6 +242,7 @@ def test_strds_info(self): module=info, reference=tinfo_string, precision=2, sep="=" ) + @xfail_windows def test_raster_info(self): self.runModule("g.mapset", mapset="test3d3") tinfo_string = """id=a1@test3d1 diff --git a/temporal/t.connect/testsuite/test_distr_tgis_db_vector.py b/temporal/t.connect/testsuite/test_distr_tgis_db_vector.py index fd26cc93e26..7184fd32cb5 100644 --- a/temporal/t.connect/testsuite/test_distr_tgis_db_vector.py +++ b/temporal/t.connect/testsuite/test_distr_tgis_db_vector.py @@ -13,7 +13,7 @@ from grass.gunittest.case import TestCase from grass.gunittest.gmodules import SimpleModule -from grass.gunittest.utils import silent_rmtree +from grass.gunittest.utils import silent_rmtree, xfail_windows class TestRasterExtraction(TestCase): @@ -259,6 +259,7 @@ def test_stvds_info(self): module=info, reference=tinfo_string, precision=2, sep="=" ) + @xfail_windows def testv_vector_info(self): self.runModule("g.mapset", mapset="testvect3") tinfo_string = """id=a1@testvect1 diff --git a/temporal/t.rast.gapfill/testsuite/test_gapfill.py b/temporal/t.rast.gapfill/testsuite/test_gapfill.py index 63cd2b83c26..7b083617d08 100644 --- a/temporal/t.rast.gapfill/testsuite/test_gapfill.py +++ b/temporal/t.rast.gapfill/testsuite/test_gapfill.py @@ -12,6 +12,7 @@ from grass.gunittest.case import TestCase from grass.gunittest.gmodules import SimpleModule +from grass.gunittest.utils import xfail_windows class TestRasterToVector(TestCase): @@ -75,6 +76,7 @@ def tearDown(self): """Remove generated data""" self.runModule("t.remove", flags="df", type="strds", inputs="A") + @xfail_windows def test_simple_2procs(self): self.assertModule( "t.rast.gapfill", @@ -125,6 +127,7 @@ def test_simple_2procs(self): self.assertModule(rast_list) self.assertLooksLike(text, rast_list.outputs.stdout) + @xfail_windows def test_simple_where(self): self.assertModule( "t.rast.gapfill", @@ -173,6 +176,7 @@ def test_simple_where(self): self.assertModule(rast_list) self.assertLooksLike(text, rast_list.outputs.stdout) + @xfail_windows def test_simple_where_2(self): self.assertModule( "t.rast.gapfill", @@ -216,6 +220,7 @@ def test_simple_where_2(self): self.assertModule(rast_list) self.assertLooksLike(text, rast_list.outputs.stdout) + @xfail_windows def test_simple_empty(self): self.assertModule( "t.rast.gapfill", @@ -302,6 +307,7 @@ def test_simple_gran(self): self.assertModule(rast_list) self.assertLooksLike(text, rast_list.outputs.stdout) + @xfail_windows def test_simple_gran(self): self.assertModule( "t.rast.gapfill", diff --git a/temporal/t.rast.series/testsuite/test_series.py b/temporal/t.rast.series/testsuite/test_series.py index cfb17a338cd..8070b4de44b 100644 --- a/temporal/t.rast.series/testsuite/test_series.py +++ b/temporal/t.rast.series/testsuite/test_series.py @@ -14,6 +14,7 @@ import grass.temporal as tgis from grass.gunittest.case import TestCase from grass.gunittest.gmodules import SimpleModule +from grass.gunittest.utils import xfail_windows class TestSnapAbsoluteSTRDS(TestCase): @@ -66,6 +67,7 @@ def tearDownClass(cls): cls.runModule("g.remove", flags="f", type="raster", name="series_minimum_2") cls.runModule("g.remove", flags="f", type="raster", name="series_quantile") + @xfail_windows def test_time_stamp(self): self.assertModule( "t.rast.series", @@ -146,6 +148,7 @@ def test_minimum_where(self): map="series_minimum_2", refmin=300, refmax=300, msg="Minimum must be 300" ) + @xfail_windows def test_quantile(self): self.assertModule( "t.rast.series", diff --git a/temporal/t.rast.univar/testsuite/test_t_rast_univar.py b/temporal/t.rast.univar/testsuite/test_t_rast_univar.py index 11a91614b6c..dca870d6a98 100644 --- a/temporal/t.rast.univar/testsuite/test_t_rast_univar.py +++ b/temporal/t.rast.univar/testsuite/test_t_rast_univar.py @@ -11,6 +11,7 @@ from pathlib import Path from grass.gunittest.case import TestCase from grass.gunittest.gmodules import SimpleModule +from grass.gunittest.utils import xfail_windows class TestRasterUnivar(TestCase): @@ -152,6 +153,7 @@ def tearDownClass(cls): cls.del_temp_region() + @xfail_windows def test_with_all_maps(self): t_rast_univar = SimpleModule( "t.rast.univar", @@ -177,6 +179,7 @@ def test_with_all_maps(self): res_line = res.split("|", 1)[1] self.assertLooksLike(ref_line, res_line) + @xfail_windows def test_with_subset_of_maps(self): t_rast_univar = SimpleModule( "t.rast.univar", @@ -201,6 +204,7 @@ def test_with_subset_of_maps(self): res_line = res.split("|", 1)[1] self.assertLooksLike(ref_line, res_line) + @xfail_windows def test_coarser_resolution(self): t_rast_univar = SimpleModule( "t.rast.univar", @@ -316,6 +320,7 @@ def test_error_handling_no_input(self): # No input self.assertModuleFail("t.rast.univar", output="out.txt") + @xfail_windows def test_with_zones(self): """Test use of zones""" @@ -353,6 +358,7 @@ def test_with_zones(self): res_line = res.split("|", 1)[1] self.assertLooksLike(ref_line, res_line) + @xfail_windows def test_with_semantic_label(self): """Test semantic labels""" t_rast_univar = SimpleModule( @@ -379,6 +385,7 @@ def test_with_semantic_label(self): res_line = res.split("|", 1)[1] self.assertLooksLike(ref_line, res_line) + @xfail_windows def test_with_semantic_label_parallel(self): """Test semantic labels""" t_rast_univar = SimpleModule( @@ -406,6 +413,7 @@ def test_with_semantic_label_parallel(self): res_line = res.split("|", 1)[1] self.assertLooksLike(ref_line, res_line) + @xfail_windows def test_with_spatial_filter_intersects(self): """Test spatial filter overlaps""" t_rast_univar = SimpleModule( @@ -434,6 +442,7 @@ def test_with_spatial_filter_intersects(self): res_line = res.split("|", 1)[1] self.assertLooksLike(ref_line, res_line) + @xfail_windows def test_with_spatial_filter_contains(self): """Test spatial filter contains""" t_rast_univar = SimpleModule( @@ -460,6 +469,7 @@ def test_with_spatial_filter_contains(self): res_line = res.split("|", 1)[1] self.assertLooksLike(ref_line, res_line) + @xfail_windows def test_with_spatial_filter_is_contained(self): """Test spatial filter is_contained""" t_rast_univar = SimpleModule( diff --git a/temporal/t.rast.what/testsuite/test_what.py b/temporal/t.rast.what/testsuite/test_what.py index 96432eff4e1..18c7af63807 100644 --- a/temporal/t.rast.what/testsuite/test_what.py +++ b/temporal/t.rast.what/testsuite/test_what.py @@ -10,6 +10,7 @@ from grass.gunittest.case import TestCase from grass.gunittest.gmodules import SimpleModule +from grass.gunittest.utils import xfail_windows class TestRasterWhat(TestCase): @@ -218,6 +219,7 @@ def test_timerow_output_coords(self): "out_timerow_coords.txt", "ca4ee0e7e4aaca170d6034e0d57d292d", text=True ) + @xfail_windows def test_row_stdout_where_parallel(self): t_rast_what = SimpleModule( "t.rast.what", @@ -245,6 +247,7 @@ def test_row_stdout_where_parallel(self): """ self.assertLooksLike(text, str(t_rast_what.outputs.stdout)) + @xfail_windows def test_row_stdout_where_parallel_cat(self): t_rast_what = SimpleModule( "t.rast.what", @@ -272,6 +275,7 @@ def test_row_stdout_where_parallel_cat(self): """ self.assertLooksLike(text, str(t_rast_what.outputs.stdout)) + @xfail_windows def test_row_stdout_where_parallel2(self): """Here without output definition, the default is used then""" @@ -385,6 +389,7 @@ def tearDownClass(cls): cls.runModule("t.remove", flags="df", type="strds", inputs="A") cls.del_temp_region() + @xfail_windows def test_null_value(self): """Test setting the null value""" diff --git a/temporal/t.rast3d.univar/testsuite/test_t_rast3d_univar.py b/temporal/t.rast3d.univar/testsuite/test_t_rast3d_univar.py index 7c8ab1e4100..4ca64e88442 100644 --- a/temporal/t.rast3d.univar/testsuite/test_t_rast3d_univar.py +++ b/temporal/t.rast3d.univar/testsuite/test_t_rast3d_univar.py @@ -11,6 +11,7 @@ from pathlib import Path from grass.gunittest.case import TestCase from grass.gunittest.gmodules import SimpleModule +from grass.gunittest.utils import xfail_windows class TestRasterUnivar(TestCase): @@ -58,6 +59,7 @@ def tearDownClass(cls): cls.runModule("g.remove", flags="f", type="raster_3d", name="zones") cls.del_temp_region() + @xfail_windows def test_with_all_maps(self): t_rast3d_univar = SimpleModule( "t.rast3d.univar", @@ -82,6 +84,7 @@ def test_with_all_maps(self): res_line = res.split("|", 1)[1] self.assertLooksLike(ref_line, res_line) + @xfail_windows def test_with_subset_of_maps(self): t_rast3d_univar = SimpleModule( "t.rast3d.univar", @@ -166,6 +169,7 @@ def test_error_handling_no_input(self): # No input self.assertModuleFail("t.rast3d.univar", output="out.txt") + @xfail_windows def test_with_zones(self): """Test use of zones""" @@ -203,6 +207,7 @@ def test_with_zones(self): res_line = res.split("|", 1)[1] self.assertLooksLike(ref_line, res_line) + @xfail_windows def test_with_zones_parallel(self): """Test use of zones""" diff --git a/vector/v.out.lidar/testsuite/test_v_out_lidar.py b/vector/v.out.lidar/testsuite/test_v_out_lidar.py index 9be4f57d3b6..613cb14b61a 100644 --- a/vector/v.out.lidar/testsuite/test_v_out_lidar.py +++ b/vector/v.out.lidar/testsuite/test_v_out_lidar.py @@ -12,6 +12,7 @@ import os from grass.gunittest.case import TestCase from grass.gunittest.main import test +from grass.gunittest.utils import xfail_windows class BasicTest(TestCase): @@ -60,6 +61,7 @@ def test_module_runs_output_created(self): self.assertModule("v.out.lidar", input=self.vector_points, output=self.las_file) self.assertFileExists(self.las_file) + @xfail_windows def test_output_identical(self): """Test to see if the standard outputs are created diff --git a/vector/v.univar/testsuite/test_v_univar.py b/vector/v.univar/testsuite/test_v_univar.py index 766871d951c..e5fc2685cac 100644 --- a/vector/v.univar/testsuite/test_v_univar.py +++ b/vector/v.univar/testsuite/test_v_univar.py @@ -15,9 +15,11 @@ from grass.gunittest.case import TestCase from grass.gunittest.main import test from grass.gunittest.gmodules import SimpleModule +from grass.gunittest.utils import xfail_windows class TestProfiling(TestCase): + @xfail_windows def test_flagg(self): """Testing flag g with map lakes""" output_str = """n=15279 @@ -40,6 +42,7 @@ def test_flagg(self): v_univar.run() self.assertLooksLike(actual=v_univar.outputs.stdout, reference=output_str) + @xfail_windows def test_flage(self): """Testing flag e with map geology""" output_str = """number of features with non NULL attribute: 1832 @@ -68,6 +71,7 @@ def test_flage(self): v_univar.run() self.assertLooksLike(actual=v_univar.outputs.stdout, reference=output_str) + @xfail_windows def test_flagw(self): """Testing flag w with map lakes""" output_str = """number of features with non NULL attribute: 15279 @@ -83,6 +87,7 @@ def test_flagw(self): v_univar.run() self.assertLooksLike(actual=v_univar.outputs.stdout, reference=output_str) + @xfail_windows def test_flagd(self): """Testing flag d with map hospitals""" univar_string = """number of primitives: 160 From 6d046ece6537d02796251a9d2cc51bbd8adaf9a5 Mon Sep 17 00:00:00 2001 From: Anna Petrasova Date: Sun, 22 Sep 2024 10:42:02 -0400 Subject: [PATCH 269/471] grass.temporal: fix regression in printing metadata (#4328) * grass.temporal: fix regression in printing metadata * Update python/grass/temporal/metadata.py Co-authored-by: Stefan Blumentrath --------- Co-authored-by: Stefan Blumentrath --- python/grass/temporal/metadata.py | 186 +++++++++++++++++++----------- 1 file changed, 116 insertions(+), 70 deletions(-) diff --git a/python/grass/temporal/metadata.py b/python/grass/temporal/metadata.py index 6094bbbe146..369fadc03cf 100644 --- a/python/grass/temporal/metadata.py +++ b/python/grass/temporal/metadata.py @@ -251,6 +251,26 @@ def get_max(self): min = property(fget=get_min, fset=set_min) max = property(fget=get_max, fset=set_max) + def print_info(self): + """Print information about this class in human readable style""" + self._print_info_body(shell=False) + + def print_shell_info(self): + """Print information about this class in shell style""" + self._print_info_body(shell=True) + + def _print_info_head(self, shell=False): + """Print information about this class (head part). + + No header printed in shell style mode. + + :param bool shell: True for human readable style otherwise shell style + """ + if not shell: + print( + " +-------------------- Metadata information ----------------------------------+" # noqa: E501 + ) + def _print_info_body(self, shell=False): """Print information about this class (body part). @@ -331,6 +351,7 @@ class RasterMetadata(RasterMetadataBase): | East-west resolution:....... 0.1 | Minimum value:.............. 0.0 | Maximum value:.............. 100.0 + | Semantic label:............. None >>> meta.print_shell_info() datatype=CELL cols=100 @@ -340,6 +361,7 @@ class RasterMetadata(RasterMetadataBase): ewres=0.1 min=0.0 max=100.0 + semantic_label=None """ @@ -384,17 +406,19 @@ def get_semantic_label(self): semantic_label = property(fget=get_semantic_label, fset=set_semantic_label) - def _print_info_body(self, shell=False): - """Print information about this class (body part). + def print_info(self): + """Print information about this class.""" + self._print_info_head(shell=False) + self._print_info_body(shell=False) + # semantic label section (raster specific only) + print(" | Semantic label:............. " + str(self.get_semantic_label())) - :param bool shell: True for human readable style otherwise shell style - """ - super()._print_info_body(shell) + def print_shell_info(self): + """Print information about this class in shell style""" + self._print_info_head(shell=True) + self._print_info_body(shell=True) # semantic label section (raster specific only) - if shell: - print("semantic_label=" + str(self.get_semantic_label())) - else: - print(" | Semantic label:............. " + str(self.get_semantic_label())) + print("semantic_label=" + str(self.get_semantic_label())) ############################################################################### @@ -539,18 +563,19 @@ def get_tbres(self): depths = property(fget=get_depths, fset=set_depths) tbres = property(fget=get_tbres, fset=set_tbres) - def _print_info_body(self, shell=False): - """Print information about this class (body part). + def print_info(self): + """Print information about this class.""" + self._print_info_head(shell=False) + self._print_info_body(shell=False) + print(" | Number of depths:........... " + str(self.get_depths())) + print(" | Top-Bottom resolution:...... " + str(self.get_tbres())) - :param bool shell: True for human readable style otherwise shell style - """ - super()._print_info_body(shell) - if shell: - print("depths=" + str(self.get_depths())) - print("tbres=" + str(self.get_tbres())) - else: - print(" | Number of depths:........... " + str(self.get_depths())) - print(" | Top-Bottom resolution:...... " + str(self.get_tbres())) + def print_shell_info(self): + """Print information about this class in shell style""" + self._print_info_head(shell=True) + self._print_info_body(shell=True) + print("depths=" + str(self.get_depths())) + print("tbres=" + str(self.get_tbres())) ############################################################################### @@ -870,53 +895,40 @@ def get_number_of_volumes(self): number_of_holes = property(fget=get_number_of_holes, fset=set_number_of_holes) number_of_volumes = property(fget=get_number_of_volumes, fset=set_number_of_volumes) - def _print_info_body(self, shell=False): - """Print information about this class (body part). + def print_info(self): + """Print information about this class in human readable style""" + print( + " +-------------------- Metadata information ----------------------------------+" # noqa: E501 + ) + print(" | Is map 3d .................. " + str(self.get_3d_info())) + print(" | Number of points ........... " + str(self.get_number_of_points())) + print(" | Number of lines ............ " + str(self.get_number_of_lines())) + print(" | Number of boundaries ....... " + str(self.get_number_of_boundaries())) + print(" | Number of centroids ........ " + str(self.get_number_of_centroids())) + print(" | Number of faces ............ " + str(self.get_number_of_faces())) + print(" | Number of kernels .......... " + str(self.get_number_of_kernels())) + print(" | Number of primitives ....... " + str(self.get_number_of_primitives())) + print(" | Number of nodes ............ " + str(self.get_number_of_nodes())) + print(" | Number of areas ............ " + str(self.get_number_of_areas())) + print(" | Number of islands .......... " + str(self.get_number_of_islands())) + print(" | Number of holes ............ " + str(self.get_number_of_holes())) + print(" | Number of volumes .......... " + str(self.get_number_of_volumes())) - :param bool shell: True for human readable style otherwise shell style - """ - if shell: - print("is_3d=" + str(self.get_3d_info())) - print("points=" + str(self.get_number_of_points())) - print("lines=" + str(self.get_number_of_lines())) - print("boundaries=" + str(self.get_number_of_boundaries())) - print("centroids=" + str(self.get_number_of_centroids())) - print("faces=" + str(self.get_number_of_faces())) - print("kernels=" + str(self.get_number_of_kernels())) - print("primitives=" + str(self.get_number_of_primitives())) - print("nodes=" + str(self.get_number_of_nodes())) - print("areas=" + str(self.get_number_of_areas())) - print("islands=" + str(self.get_number_of_islands())) - print("holes=" + str(self.get_number_of_holes())) - print("volumes=" + str(self.get_number_of_volumes())) - else: - print(" | Is map 3d .................. " + str(self.get_3d_info())) - print(" | Number of points ........... " + str(self.get_number_of_points())) - print(" | Number of lines ............ " + str(self.get_number_of_lines())) - print( - " | Number of boundaries ....... " - + str(self.get_number_of_boundaries()) - ) - print( - " | Number of centroids ........ " + str(self.get_number_of_centroids()) - ) - print(" | Number of faces ............ " + str(self.get_number_of_faces())) - print( - " | Number of kernels .......... " + str(self.get_number_of_kernels()) - ) - print( - " | Number of primitives ....... " - + str(self.get_number_of_primitives()) - ) - print(" | Number of nodes ............ " + str(self.get_number_of_nodes())) - print(" | Number of areas ............ " + str(self.get_number_of_areas())) - print( - " | Number of islands .......... " + str(self.get_number_of_islands()) - ) - print(" | Number of holes ............ " + str(self.get_number_of_holes())) - print( - " | Number of volumes .......... " + str(self.get_number_of_volumes()) - ) + def print_shell_info(self): + """Print information about this class in shell style""" + print("is_3d=" + str(self.get_3d_info())) + print("points=" + str(self.get_number_of_points())) + print("lines=" + str(self.get_number_of_lines())) + print("boundaries=" + str(self.get_number_of_boundaries())) + print("centroids=" + str(self.get_number_of_centroids())) + print("faces=" + str(self.get_number_of_faces())) + print("kernels=" + str(self.get_number_of_kernels())) + print("primitives=" + str(self.get_number_of_primitives())) + print("nodes=" + str(self.get_number_of_nodes())) + print("areas=" + str(self.get_number_of_areas())) + print("islands=" + str(self.get_number_of_islands())) + print("holes=" + str(self.get_number_of_holes())) + print("volumes=" + str(self.get_number_of_volumes())) ############################################################################### @@ -1034,13 +1046,11 @@ def get_number_of_maps(self): def print_info(self): """Print information about this class in human readable style""" - self._print_info_head(shell=False) self._print_info_body(shell=False) self._print_info_tail(shell=False) def print_shell_info(self): """Print information about this class in shell style""" - self._print_info_head(shell=True) self._print_info_body(shell=True) self._print_info_tail(shell=True) @@ -1056,6 +1066,12 @@ def _print_info_head(self, shell=False): " +-------------------- Metadata information ----------------------------------+" # noqa: E501 ) + def _print_info_body(self, shell=False): + """Print information about this class (body part). + + :param bool shell: True for human readable style otherwise shell style + """ + def _print_info_tail(self, shell=False): """Print information about this class (tail part). @@ -1476,6 +1492,16 @@ def get_semantic_labels(self): number_of_semantic_labels = property(fget=get_number_of_semantic_labels) semantic_labels = property(fget=get_semantic_labels) + def print_info(self): + """Print information about this class in human readable style""" + self._print_info_head(shell=False) + super().print_info() + + def print_shell_info(self): + """Print information about this class in shell style""" + self._print_info_head(shell=True) + super().print_shell_info() + def _print_info_body(self, shell=False): """Print information about this class (body part). @@ -1562,6 +1588,8 @@ class STR3DSMetadata(STDSRasterMetadataBase): | Command history: >>> meta.print_shell_info() raster3d_register=None + tbres_min=None + tbres_max=None nsres_min=None nsres_max=None ewres_min=None @@ -1570,8 +1598,6 @@ class STR3DSMetadata(STDSRasterMetadataBase): min_max=None max_min=None max_max=None - tbres_min=None - tbres_max=None aggregation_type=None number_of_maps=None @@ -1624,6 +1650,16 @@ def get_tbres_max(self): tbres_min = property(fget=get_tbres_min) tbres_max = property(fget=get_tbres_max) + def print_info(self): + """Print information about this class in human readable style""" + self._print_info_head(shell=False) + super().print_info() + + def print_shell_info(self): + """Print information about this class in shell style""" + self._print_info_head(shell=True) + super().print_shell_info() + def _print_info_body(self, shell=False): """Print information about this class (body part). @@ -1889,6 +1925,16 @@ def get_number_of_volumes(self): number_of_holes = property(fget=get_number_of_holes) number_of_volumes = property(fget=get_number_of_volumes) + def print_info(self): + """Print information about this class in human readable style""" + self._print_info_head(shell=False) + super().print_info() + + def print_shell_info(self): + """Print information about this class in shell style""" + self._print_info_head(shell=True) + super().print_shell_info() + def _print_info_body(self, shell=False): """Print information about this class (body part). From bd39db7082f3b6d87c4066a6c1e1484974a10394 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Sun, 22 Sep 2024 18:34:00 -0400 Subject: [PATCH 270/471] grass.temporal: Remove expected failures for tests fixed by #4328 (#4364) --- temporal/t.connect/testsuite/test_distr_tgis_db_raster.py | 3 +-- temporal/t.connect/testsuite/test_distr_tgis_db_raster3d.py | 3 +-- temporal/t.connect/testsuite/test_distr_tgis_db_vector.py | 3 +-- temporal/t.rast.series/testsuite/test_series.py | 1 - 4 files changed, 3 insertions(+), 7 deletions(-) diff --git a/temporal/t.connect/testsuite/test_distr_tgis_db_raster.py b/temporal/t.connect/testsuite/test_distr_tgis_db_raster.py index 6e4d76b26c5..412a926f01d 100644 --- a/temporal/t.connect/testsuite/test_distr_tgis_db_raster.py +++ b/temporal/t.connect/testsuite/test_distr_tgis_db_raster.py @@ -13,7 +13,7 @@ from grass.gunittest.case import TestCase from grass.gunittest.gmodules import SimpleModule -from grass.gunittest.utils import silent_rmtree, xfail_windows +from grass.gunittest.utils import silent_rmtree class TestRasterExtraction(TestCase): @@ -254,7 +254,6 @@ def test_strds_info(self): module=info, reference=tinfo_string, precision=2, sep="=" ) - @xfail_windows def test_raster_info(self): self.runModule("g.mapset", mapset="test3") tinfo_string = """id=a1@test1 diff --git a/temporal/t.connect/testsuite/test_distr_tgis_db_raster3d.py b/temporal/t.connect/testsuite/test_distr_tgis_db_raster3d.py index f9b4df76d88..23bc11c271e 100644 --- a/temporal/t.connect/testsuite/test_distr_tgis_db_raster3d.py +++ b/temporal/t.connect/testsuite/test_distr_tgis_db_raster3d.py @@ -13,7 +13,7 @@ from grass.gunittest.case import TestCase from grass.gunittest.gmodules import SimpleModule -from grass.gunittest.utils import silent_rmtree, xfail_windows +from grass.gunittest.utils import silent_rmtree class testRaster3dExtraction(TestCase): @@ -242,7 +242,6 @@ def test_strds_info(self): module=info, reference=tinfo_string, precision=2, sep="=" ) - @xfail_windows def test_raster_info(self): self.runModule("g.mapset", mapset="test3d3") tinfo_string = """id=a1@test3d1 diff --git a/temporal/t.connect/testsuite/test_distr_tgis_db_vector.py b/temporal/t.connect/testsuite/test_distr_tgis_db_vector.py index 7184fd32cb5..fd26cc93e26 100644 --- a/temporal/t.connect/testsuite/test_distr_tgis_db_vector.py +++ b/temporal/t.connect/testsuite/test_distr_tgis_db_vector.py @@ -13,7 +13,7 @@ from grass.gunittest.case import TestCase from grass.gunittest.gmodules import SimpleModule -from grass.gunittest.utils import silent_rmtree, xfail_windows +from grass.gunittest.utils import silent_rmtree class TestRasterExtraction(TestCase): @@ -259,7 +259,6 @@ def test_stvds_info(self): module=info, reference=tinfo_string, precision=2, sep="=" ) - @xfail_windows def testv_vector_info(self): self.runModule("g.mapset", mapset="testvect3") tinfo_string = """id=a1@testvect1 diff --git a/temporal/t.rast.series/testsuite/test_series.py b/temporal/t.rast.series/testsuite/test_series.py index 8070b4de44b..99fe69d4352 100644 --- a/temporal/t.rast.series/testsuite/test_series.py +++ b/temporal/t.rast.series/testsuite/test_series.py @@ -67,7 +67,6 @@ def tearDownClass(cls): cls.runModule("g.remove", flags="f", type="raster", name="series_minimum_2") cls.runModule("g.remove", flags="f", type="raster", name="series_quantile") - @xfail_windows def test_time_stamp(self): self.assertModule( "t.rast.series", From 6325a18d5ffee998422fcf9d94f7b61f58281260 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Mon, 23 Sep 2024 06:45:53 -0400 Subject: [PATCH 271/471] CI(OSGeo4W): Run pytest tests on Windows (#4366) --- .github/workflows/osgeo4w.yml | 17 +++++++++++++++++ .../tests/g_mapsets_list_format_test.py | 9 +++++++++ .../jupyter/tests/grass_jupyter_session_test.py | 10 ++++++++++ .../grass/jupyter/tests/timeseriesmap_test.py | 5 +++++ 4 files changed, 41 insertions(+) diff --git a/.github/workflows/osgeo4w.yml b/.github/workflows/osgeo4w.yml index 79a44937755..027cecebf21 100644 --- a/.github/workflows/osgeo4w.yml +++ b/.github/workflows/osgeo4w.yml @@ -66,9 +66,13 @@ jobs: pdal-devel pdcurses proj-devel + python3-core + python3-jupyter python3-matplotlib python3-numpy + python3-pip python3-ply + python3-pytest python3-pywin32 python3-wxpython regex-devel @@ -98,6 +102,19 @@ jobs: shell: msys2 {0} run: .github/workflows/test_simple.sh + - name: Install pytest plugins + run: python -m pip install pytest-timeout + shell: cmd /D /E:ON /V:OFF /S /C "CALL C:/OSGeo4W/OSGeo4W.bat "{0}"" + - name: Run pytest with a single worker + run: | + call %OSGEO4W_ROOT%\opt\grass\etc\env.bat + set PYTHONPATH=%GISBASE%\etc\python;%PYTHONPATH% + path %GISBASE%\lib;%GISBASE%\bin;%PATH% + pytest --verbose --color=yes ^ + --durations=0 --durations-min=0.5 ^ + -ra . + shell: cmd /D /E:ON /V:OFF /S /C "CALL C:/OSGeo4W/OSGeo4W.bat "{0}"" + - name: Run tests run: .github/workflows/test_thorough.bat 'C:\OSGeo4W\opt\grass\grass85.bat' 'C:\OSGeo4W\bin\python3' diff --git a/general/g.mapsets/tests/g_mapsets_list_format_test.py b/general/g.mapsets/tests/g_mapsets_list_format_test.py index 79555085bb0..05fe946a6ae 100644 --- a/general/g.mapsets/tests/g_mapsets_list_format_test.py +++ b/general/g.mapsets/tests/g_mapsets_list_format_test.py @@ -14,6 +14,7 @@ """Test parsing and structure of CSV and JSON outputs from g.mapsets""" import json +import sys import pytest import grass.script as gs from grass.script import utils as gutils @@ -30,6 +31,10 @@ def _check_parsed_list(mapsets, text, sep="|"): assert text == sep.join(mapsets) + "\n" +@pytest.mark.xfail( + sys.platform == "win32", + reason="universal_newlines or text subprocess option not used", +) @pytest.mark.parametrize("separator", SEPARATORS) def test_plain_list_output(simple_dataset, separator): """Test that the separators are properly applied with list flag""" @@ -38,6 +43,10 @@ def test_plain_list_output(simple_dataset, separator): _check_parsed_list(mapsets, text, gutils.separator(separator)) +@pytest.mark.xfail( + sys.platform == "win32", + reason="universal_newlines or text subprocess option not used", +) @pytest.mark.parametrize("separator", SEPARATORS) def test_plain_print_output(simple_dataset, separator): """Test that the separators are properly applied with print flag""" diff --git a/python/grass/jupyter/tests/grass_jupyter_session_test.py b/python/grass/jupyter/tests/grass_jupyter_session_test.py index 7b6bc89e4ed..fd725303844 100644 --- a/python/grass/jupyter/tests/grass_jupyter_session_test.py +++ b/python/grass/jupyter/tests/grass_jupyter_session_test.py @@ -4,6 +4,8 @@ import os import sys +import pytest + # All init tests change the global environment, but we use a separate process # only when it is necessary. @@ -20,6 +22,10 @@ def run_in_subprocess(file): return process.stdout +@pytest.mark.xfail( + sys.platform == "win32", + reason="tmp_path string in interpolated code should be escaped or use raw strings", +) def test_init_finish(tmp_path): """Check that init function works with an explicit session finish""" location = "test" @@ -42,6 +48,10 @@ def test_init_finish(tmp_path): assert not os.path.exists(session_file), f"Session file {session_file} not deleted" +@pytest.mark.xfail( + sys.platform == "win32", + reason="tmp_path string in interpolated code should be escaped or use raw strings", +) def test_init_with_auto_finish(tmp_path): """Check that init function works with an implicit session finish""" location = "test" diff --git a/python/grass/jupyter/tests/timeseriesmap_test.py b/python/grass/jupyter/tests/timeseriesmap_test.py index d65f7fcb700..c4e20f7704e 100644 --- a/python/grass/jupyter/tests/timeseriesmap_test.py +++ b/python/grass/jupyter/tests/timeseriesmap_test.py @@ -1,6 +1,7 @@ """Test TimeSeriesMap functions""" from pathlib import Path +import sys import pytest @@ -66,6 +67,10 @@ def test_render_layers(space_time_raster_dataset, fill_gaps): assert Path(filename).is_file() +@pytest.mark.xfail( + sys.platform == "win32", + reason="DejaVuSans.ttf file isn't found and not installed with GRASS", +) @pytest.mark.needs_solo_run def test_save(space_time_raster_dataset, tmp_path): """Test returns from animate and time_slider are correct object types""" From c32dd525746f0674617cb5595d93b9e3cb6d7677 Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Mon, 23 Sep 2024 09:40:57 -0400 Subject: [PATCH 272/471] checks: Replace bare except (Flake8 E722) in po stats script (#4354) This addresses the PEP8 style error of mentioning specific exceptions (Flake8 E722) by specifying the exception type in the except clause within the writejson function in grass_po_stats.py. Now, bare except is replaced by OSError to catch only OS-related exceptions because os.remove() can raise various OS-related exceptions such as FileNotFoundError or PermissionError. Additionally, it confirms that no other common Flake8 errors (E122, E128, E231, E401) are present in the file by removing the other ignores. --- .flake8 | 1 - locale/grass_po_stats.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.flake8 b/.flake8 index ecea7b82a0b..7092b7d15e3 100644 --- a/.flake8 +++ b/.flake8 @@ -24,7 +24,6 @@ per-file-ignores = imagery/i.atcorr/create_iwave.py: F632, F821, W293 doc/python/m.distance.py: E501 doc/gui/wxpython/example/dialogs.py: F401 - locale/grass_po_stats.py: E122, E128, E231, E401, E722 gui/scripts/d.wms.py: E501 gui/wxpython/core/gconsole.py: E722 gui/wxpython/core/render.py: E722 diff --git a/locale/grass_po_stats.py b/locale/grass_po_stats.py index b5f15026691..32e3f449cb3 100644 --- a/locale/grass_po_stats.py +++ b/locale/grass_po_stats.py @@ -144,7 +144,7 @@ def writejson(stats, outfile): fout.close() try: os.remove("messages.mo") - except: + except OSError: pass From 25afbfbd0e6a80823827c3767920204f9bab123e Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Mon, 23 Sep 2024 10:56:28 -0400 Subject: [PATCH 273/471] g.version: Add tests covering different outputs (#4219) This adds tests for different outputs of the g.version tool using pytest. --- general/g.version/tests/conftest.py | 17 +++++ general/g.version/tests/g_version_test.py | 83 +++++++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 general/g.version/tests/conftest.py create mode 100644 general/g.version/tests/g_version_test.py diff --git a/general/g.version/tests/conftest.py b/general/g.version/tests/conftest.py new file mode 100644 index 00000000000..e4357b2f672 --- /dev/null +++ b/general/g.version/tests/conftest.py @@ -0,0 +1,17 @@ +import os +import pytest +import grass.script as gs + + +@pytest.fixture(scope="module") +def session(tmp_path_factory): + """Set up a GRASS session for the tests.""" + tmp_path = tmp_path_factory.mktemp("grass_session") + project = "test_project" + + # Create a test location + gs.create_project(tmp_path, project) + + # Initialize the GRASS session + with gs.setup.init(tmp_path / project, env=os.environ.copy()) as session: + yield session diff --git a/general/g.version/tests/g_version_test.py b/general/g.version/tests/g_version_test.py new file mode 100644 index 00000000000..805518382a2 --- /dev/null +++ b/general/g.version/tests/g_version_test.py @@ -0,0 +1,83 @@ +import grass.script as gs + + +def test_g_version_no_flag(session): + """Test that g.version output contains the word 'GRASS'.""" + output = gs.read_command("g.version", env=session.env).strip() + assert ( + "GRASS" in output + ), "Expected 'GRASS' in g.version output, but it was not found." + + +def test_c_flag(session): + """Test the output of g.version -c for Copyright and License Statement.""" + expected_text = "Copyright and License Statement" + output = gs.read_command("g.version", flags="c", env=session.env).strip() + assert ( + expected_text in output + ), f"Expected '{expected_text}' in g.version -c output, but got: '{output}'" + + +def test_e_flag(session): + """Test that g.version -e contains the expected keys.""" + expected_keys = ["PROJ:", "GDAL/OGR:", "SQLite:"] + output = gs.read_command("g.version", flags="e", env=session.env).strip() + for key in expected_keys: + assert ( + key in output + ), f"Expected key '{key}' in g.version -e output, but it was not found." + + +def test_b_flag(session): + """Test that g.version -b output contains the word 'GRASS'.""" + output = gs.read_command("g.version", flags="b", env=session.env).strip() + assert ( + "GRASS" in output + ), "Expected 'GRASS' in g.version -b output, but it was not found." + + +def test_g_flag(session): + """Test that g.version -g contains the expected keys.""" + expected_keys = [ + "version", + "date", + "revision", + "build_date", + "build_platform", + "build_off_t_size", + ] + output = gs.parse_command("g.version", flags="g", env=session.env) + for key in expected_keys: + assert ( + key in output + ), f"Expected key '{key}' in g.version -g output, but it was not found." + + +def test_r_flag(session): + """Test that g.version -r contains the expected keys.""" + expected_texts = ["libgis revision:", "libgis date:"] + output = gs.read_command("g.version", flags="r", env=session.env).strip() + for text in expected_texts: + assert ( + text in output + ), f"Expected key '{text}' in g.version -r output, but it was not found." + + +def test_x_flag(session): + """Test that g.version -x output has paired curly brackets.""" + output = gs.read_command("g.version", flags="x", env=session.env).strip() + + def curly_brackets_paired(text): + counter = 0 + for character in text: + if character == "{": + counter += 1 + elif character == "}": + counter -= 1 + if counter < 0: + return False + return counter == 0 + + assert curly_brackets_paired( + output + ), "Curly brackets are not properly paired in the g.version -x output." From 9e8874c5e7f8093ec9bd8d1590043e0f58c3671c Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Mon, 23 Sep 2024 16:59:17 -0400 Subject: [PATCH 274/471] ps.map: Fix memory leak issue (#4369) This addresses resource leak issue identified by Coverity Scan (CID : 1207906). Uses Vect_destroy_cats_struct() to destroy Cats and avoid the memory leak issue. --- ps/ps.map/ps_vpoints.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ps/ps.map/ps_vpoints.c b/ps/ps.map/ps_vpoints.c index 80b29a349b1..cd320b46f11 100644 --- a/ps/ps.map/ps_vpoints.c +++ b/ps/ps.map/ps_vpoints.c @@ -268,5 +268,6 @@ int PS_vpoints_plot(struct Map_info *P_map, int vec) } /* for (line) */ fprintf(PS.fp, "\n"); + Vect_destroy_cats_struct(Cats); return 0; } From b35b1c36db93f8c85d7a26b0cee3f7616f4e8b70 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2024 17:06:45 -0400 Subject: [PATCH 275/471] CI(deps): Lock file maintenance (#4365) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- flake.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/flake.lock b/flake.lock index 257ec2317b7..fcda6ed01dd 100644 --- a/flake.lock +++ b/flake.lock @@ -5,11 +5,11 @@ "nixpkgs-lib": "nixpkgs-lib" }, "locked": { - "lastModified": 1725234343, - "narHash": "sha256-+ebgonl3NbiKD2UD0x4BszCZQ6sTfL4xioaM49o5B3Y=", + "lastModified": 1726153070, + "narHash": "sha256-HO4zgY0ekfwO5bX0QH/3kJ/h4KvUDFZg8YpkNwIbg1U=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "567b938d64d4b4112ee253b9274472dc3a346eb6", + "rev": "bcef6817a8b2aa20a5a6dbb19b43e63c5bf8619a", "type": "github" }, "original": { @@ -19,11 +19,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1725194671, - "narHash": "sha256-tLGCFEFTB5TaOKkpfw3iYT9dnk4awTP/q4w+ROpMfuw=", + "lastModified": 1726871744, + "narHash": "sha256-V5LpfdHyQkUF7RfOaDPrZDP+oqz88lTJrMT1+stXNwo=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "b833ff01a0d694b910daca6e2ff4a3f26dee478c", + "rev": "a1d92660c6b3b7c26fb883500a80ea9d33321be2", "type": "github" }, "original": { From c266f46ba50090bb4ad2f08ad4e3855ff42909c5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2024 21:28:14 +0000 Subject: [PATCH 276/471] CI(deps): Update dependency bandit to v1.7.10 (#4370) --- .github/workflows/python-code-quality.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index 92ef7d788b2..aed00117956 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -34,7 +34,7 @@ jobs: # renovate: datasource=pypi depName=pylint PYLINT_VERSION: "2.12.2" # renovate: datasource=pypi depName=bandit - BANDIT_VERSION: "1.7.9" + BANDIT_VERSION: "1.7.10" # renovate: datasource=pypi depName=ruff RUFF_VERSION: "0.6.7" From c593eb02da0112e7b118904245c20915ccc4148e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Mon, 23 Sep 2024 20:56:39 -0400 Subject: [PATCH 277/471] docker: Add liblapacke-dev package to Ubuntu builds (#4371) Adds liblapacke-dev package that was added to the apt.txt of the grass-addons repo. --- Dockerfile | 1 + docker/ubuntu/Dockerfile | 1 + 2 files changed, 2 insertions(+) diff --git a/Dockerfile b/Dockerfile index c6366eeb873..6ea64aadab7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -41,6 +41,7 @@ ARG GRASS_RUN_PACKAGES="build-essential \ libgsl27 \ libjpeg-turbo8 \ libjsoncpp-dev \ + liblapacke-dev \ libmagic1 \ libmagic-mgc \ libncurses5 \ diff --git a/docker/ubuntu/Dockerfile b/docker/ubuntu/Dockerfile index c6366eeb873..6ea64aadab7 100644 --- a/docker/ubuntu/Dockerfile +++ b/docker/ubuntu/Dockerfile @@ -41,6 +41,7 @@ ARG GRASS_RUN_PACKAGES="build-essential \ libgsl27 \ libjpeg-turbo8 \ libjsoncpp-dev \ + liblapacke-dev \ libmagic1 \ libmagic-mgc \ libncurses5 \ From 16de4d31526a149f0f70e23ba50b85a223d351c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Mon, 23 Sep 2024 21:16:49 -0400 Subject: [PATCH 278/471] grass.jupyter: Fix session tests on Windows (#4368) The interpolated path strings aren't escaped when evaluated, so using a raw string allows C:\Users to not have an invalid \U escape sequence. --- .../tests/grass_jupyter_session_test.py | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/python/grass/jupyter/tests/grass_jupyter_session_test.py b/python/grass/jupyter/tests/grass_jupyter_session_test.py index fd725303844..17b6e7fbf5d 100644 --- a/python/grass/jupyter/tests/grass_jupyter_session_test.py +++ b/python/grass/jupyter/tests/grass_jupyter_session_test.py @@ -4,8 +4,6 @@ import os import sys -import pytest - # All init tests change the global environment, but we use a separate process # only when it is necessary. @@ -22,10 +20,6 @@ def run_in_subprocess(file): return process.stdout -@pytest.mark.xfail( - sys.platform == "win32", - reason="tmp_path string in interpolated code should be escaped or use raw strings", -) def test_init_finish(tmp_path): """Check that init function works with an explicit session finish""" location = "test" @@ -33,8 +27,8 @@ def test_init_finish(tmp_path): import os import grass.script as gs import grass.jupyter as gj -gs.core._create_location_xy("{tmp_path}", "{location}") -session = gj.init("{tmp_path / location}") +gs.core._create_location_xy(r"{tmp_path}", r"{location}") +session = gj.init(r"{tmp_path / location}") gs.read_command("g.region", flags="p") print(os.environ["GISRC"]) session.finish() @@ -48,10 +42,6 @@ def test_init_finish(tmp_path): assert not os.path.exists(session_file), f"Session file {session_file} not deleted" -@pytest.mark.xfail( - sys.platform == "win32", - reason="tmp_path string in interpolated code should be escaped or use raw strings", -) def test_init_with_auto_finish(tmp_path): """Check that init function works with an implicit session finish""" location = "test" @@ -59,8 +49,8 @@ def test_init_with_auto_finish(tmp_path): import os import grass.script as gs import grass.jupyter as gj -gs.core._create_location_xy("{tmp_path}", "{location}") -session = gj.init("{tmp_path / location}") +gs.core._create_location_xy(r"{tmp_path}", r"{location}") +session = gj.init(r"{tmp_path / location}") print(os.environ["GISRC"]) """ From 04cc9ead399a85475b5390741645a7e56b39f7a8 Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Mon, 23 Sep 2024 21:41:28 -0400 Subject: [PATCH 279/471] checks: Replace bare except (Flake8 E722) in wxGUI/core (#4367) This addresses the PEP8 style error E722 by specifying exception types in all bare except clauses found in wxGUI/core. Unclear cases use Exception. --------- Co-authored-by: Vaclav Petras --- .flake8 | 6 ------ gui/wxpython/core/gconsole.py | 2 +- gui/wxpython/core/render.py | 2 +- gui/wxpython/core/settings.py | 4 ++-- gui/wxpython/core/toolboxes.py | 2 +- gui/wxpython/core/utils.py | 10 +++++----- gui/wxpython/core/workspace.py | 6 +++--- 7 files changed, 13 insertions(+), 19 deletions(-) diff --git a/.flake8 b/.flake8 index 7092b7d15e3..af8a021bd4c 100644 --- a/.flake8 +++ b/.flake8 @@ -25,12 +25,6 @@ per-file-ignores = doc/python/m.distance.py: E501 doc/gui/wxpython/example/dialogs.py: F401 gui/scripts/d.wms.py: E501 - gui/wxpython/core/gconsole.py: E722 - gui/wxpython/core/render.py: E722 - gui/wxpython/core/settings.py: E722 - gui/wxpython/core/toolboxes.py: E722 - gui/wxpython/core/utils.py: E722 - gui/wxpython/core/workspace.py: E722 gui/wxpython/datacatalog/tree.py: E731, E402 gui/wxpython/dbmgr/base.py: E722 gui/wxpython/dbmgr/dialogs.py: E722 diff --git a/gui/wxpython/core/gconsole.py b/gui/wxpython/core/gconsole.py index bad4aac29cd..7575c401c98 100644 --- a/gui/wxpython/core/gconsole.py +++ b/gui/wxpython/core/gconsole.py @@ -691,7 +691,7 @@ def load_source(modname, filename): if len(command) == 1 and not skipInterface: try: task = gtask.parse_interface(command[0]) - except: + except Exception: task = None else: task = None diff --git a/gui/wxpython/core/render.py b/gui/wxpython/core/render.py index b98e4323578..5b3e3096043 100644 --- a/gui/wxpython/core/render.py +++ b/gui/wxpython/core/render.py @@ -1237,7 +1237,7 @@ def SetRegion(self, windres=False, windres3=False): return grass_region - except: + except Exception: return None def GetListOfLayers( diff --git a/gui/wxpython/core/settings.py b/gui/wxpython/core/settings.py index 45620220aed..2b2d3b28526 100644 --- a/gui/wxpython/core/settings.py +++ b/gui/wxpython/core/settings.py @@ -110,7 +110,7 @@ def _generateLocale(self): self.locs.sort() # Add a default choice to not override system locale self.locs.insert(0, "system") - except: + except Exception: # No NLS self.locs = ["system"] @@ -992,7 +992,7 @@ def SaveToFile(self, settings=None): if not os.path.exists(dirPath): try: os.mkdir(dirPath) - except: + except OSError: GError(_("Unable to create settings directory")) return try: diff --git a/gui/wxpython/core/toolboxes.py b/gui/wxpython/core/toolboxes.py index ae6b1b6a0a6..e77010ff213 100644 --- a/gui/wxpython/core/toolboxes.py +++ b/gui/wxpython/core/toolboxes.py @@ -209,7 +209,7 @@ def getMenudataFile(userRootFile, newFile, fallback): fh.write(xml) fh.close() return menudataFile - except: + except Exception: _debug( 2, ( diff --git a/gui/wxpython/core/utils.py b/gui/wxpython/core/utils.py index 10d6dca2759..4be6c18e2a2 100644 --- a/gui/wxpython/core/utils.py +++ b/gui/wxpython/core/utils.py @@ -77,7 +77,7 @@ def GetTempfile(pref=None): return os.path.join(pref, file) else: return tempfile - except: + except Exception: return None @@ -255,7 +255,7 @@ def ListOfCatsToRange(cats): try: cats = list(map(int, cats)) - except: + except ValueError: return catstr i = 0 @@ -579,7 +579,7 @@ def GetListOfLocations(dbase): os.path.join(location, "*") ): listOfLocations.append(os.path.basename(location)) - except: + except OSError: pass ListSortLower(listOfLocations) @@ -632,7 +632,7 @@ def _getGDALFormats(): """Get dictionary of available GDAL drivers""" try: ret = grass.read_command("r.in.gdal", quiet=True, flags="f") - except: + except grass.CalledModuleError: ret = None return _parseFormats(ret), _parseFormats(ret, writableOnly=True) @@ -642,7 +642,7 @@ def _getOGRFormats(): """Get dictionary of available OGR drivers""" try: ret = grass.read_command("v.in.ogr", quiet=True, flags="f") - except: + except grass.CalledModuleError: ret = None return _parseFormats(ret), _parseFormats(ret, writableOnly=True) diff --git a/gui/wxpython/core/workspace.py b/gui/wxpython/core/workspace.py index 63f8b91cb89..9550aa73488 100644 --- a/gui/wxpython/core/workspace.py +++ b/gui/wxpython/core/workspace.py @@ -123,7 +123,7 @@ def __processFile(self): try: self.layerManager["pos"] = (posVal[0], posVal[1]) self.layerManager["size"] = (posVal[2], posVal[3]) - except: + except IndexError: pass # current working directory cwdPath = self.__getNodeText(node_lm, "cwd") @@ -155,7 +155,7 @@ def __processFile(self): try: pos = (posVal[0], posVal[1]) size = (posVal[2], posVal[3]) - except: + except IndexError: pos = None size = None # this happens on Windows when mapwindow is minimized when @@ -2019,7 +2019,7 @@ def _get_value(self, line): """Get value of element""" try: return line.strip(" ").split(" ")[1].strip(" ") - except: + except IndexError: return "" def _get_element(self, line): From ac664487845546c2b8b30ddbc15c503897eba262 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 24 Sep 2024 07:15:05 -0400 Subject: [PATCH 280/471] CI(deps): Update pre-commit hook igorshubovych/markdownlint-cli to v0.42.0 (#4374) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e1f98c7a2a5..74423979b22 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -43,7 +43,7 @@ repos: - id: ruff args: [--fix, --preview] - repo: https://github.com/igorshubovych/markdownlint-cli - rev: v0.41.0 + rev: v0.42.0 hooks: - id: markdownlint-fix # Using this mirror lets us use mypyc-compiled black, which is about 2x faster From 234209ae04f45060534534a88c4eb377a9be131a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 24 Sep 2024 17:04:38 -0400 Subject: [PATCH 281/471] CI(deps): Update github/codeql-action action to v3.26.9 (#4380) --- .github/workflows/codeql-analysis.yml | 4 ++-- .github/workflows/python-code-quality.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index b4fb254d11d..bf07004bc82 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -56,7 +56,7 @@ jobs: if: ${{ matrix.language == 'c-cpp' }} - name: Initialize CodeQL - uses: github/codeql-action/init@294a9d92911152fe08befb9ec03e240add280cb3 # v3.26.8 + uses: github/codeql-action/init@461ef6c76dfe95d5c364de2f431ddbd31a417628 # v3.26.9 with: languages: ${{ matrix.language }} config-file: ./.github/codeql/codeql-config.yml @@ -81,6 +81,6 @@ jobs: run: .github/workflows/build_ubuntu-22.04.sh "${HOME}/install" - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@294a9d92911152fe08befb9ec03e240add280cb3 # v3.26.8 + uses: github/codeql-action/analyze@461ef6c76dfe95d5c364de2f431ddbd31a417628 # v3.26.9 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index aed00117956..403d7393d0e 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -135,7 +135,7 @@ jobs: path: bandit.sarif - name: Upload SARIF File into Security Tab - uses: github/codeql-action/upload-sarif@294a9d92911152fe08befb9ec03e240add280cb3 # v3.26.8 + uses: github/codeql-action/upload-sarif@461ef6c76dfe95d5c364de2f431ddbd31a417628 # v3.26.9 with: sarif_file: bandit.sarif From 8c671caeb0fa37744ff83e24645be0d8d40fff61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Tue, 24 Sep 2024 17:22:41 -0400 Subject: [PATCH 282/471] docker: Sort package lists and configure options in Dockerfiles (#4373) * docker: Sort packages and configure options in Ubuntu Dockerfiles * docker: Sort packages and configure options in debian Dockerfile * docker: Sort packages and configure options in Alpine Dockerfile * docker: Sort packages and configure options in Ubuntu wxgui Dockerfile --- Dockerfile | 104 +++++++++++++++++---------------- docker/alpine/Dockerfile | 36 ++++++------ docker/debian/Dockerfile | 24 ++++---- docker/ubuntu/Dockerfile | 104 +++++++++++++++++---------------- docker/ubuntu_wxgui/Dockerfile | 32 +++++----- 5 files changed, 156 insertions(+), 144 deletions(-) diff --git a/Dockerfile b/Dockerfile index 6ea64aadab7..41fb3264c07 100644 --- a/Dockerfile +++ b/Dockerfile @@ -19,8 +19,9 @@ WORKDIR /tmp ARG GUI # Todo: re-consider required dev packages for addons (~400MB in dev packages) -ARG GRASS_RUN_PACKAGES="build-essential \ +ARG GRASS_RUN_PACKAGES="\ bison \ + build-essential \ bzip2 \ curl \ flex \ @@ -28,8 +29,6 @@ ARG GRASS_RUN_PACKAGES="build-essential \ gcc \ gdal-bin \ geos-bin \ - proj-bin \ - netcdf-bin \ git \ language-pack-en-base \ libcairo2 \ @@ -38,29 +37,29 @@ ARG GRASS_RUN_PACKAGES="build-essential \ libfftw3-dev \ libfreetype6 \ libgdal-dev \ + libgeos-dev \ + libgsl-dev \ libgsl27 \ libjpeg-turbo8 \ libjsoncpp-dev \ liblapacke-dev \ - libmagic1 \ libmagic-mgc \ + libmagic1 \ libncurses5 \ - libopenblas-dev \ + libomp-dev \ + libomp5 \ libopenblas-base \ + libopenblas-dev \ libopenjp2-7 \ - libomp5 \ - libomp-dev \ - libgeos-dev \ - libpdal-dev \ - libproj-dev \ - libpq-dev \ - libgsl-dev \ libpdal-base13 \ + libpdal-dev \ libpdal-plugin-hdf \ libpdal-plugins \ libpdal-util13 \ libpnglite0 \ + libpq-dev \ libpq5 \ + libproj-dev \ libpython3-all-dev \ libreadline8 \ libsqlite3-0 \ @@ -71,7 +70,9 @@ ARG GRASS_RUN_PACKAGES="build-essential \ mesa-utils \ moreutils \ ncurses-bin \ + netcdf-bin \ pdal \ + proj-bin \ proj-data \ python-is-python3 \ python3 \ @@ -87,56 +88,58 @@ ARG GRASS_RUN_PACKAGES="build-essential \ ENV GRASS_RUN_PACKAGES=${GRASS_RUN_PACKAGES} # Define build packages -ARG GRASS_BUILD_PACKAGES="cmake \ +ARG GRASS_BUILD_PACKAGES="\ + cmake \ libbz2-dev \ libcairo2-dev \ libfreetype6-dev \ - zlib1g-dev \ + libjpeg-dev \ + libncurses5-dev \ libnetcdf-dev \ libopenjp2-7-dev \ - libreadline-dev \ - libjpeg-dev \ libpnglite-dev \ + libreadline-dev \ libsqlite3-dev \ libtiff-dev \ libzstd-dev \ - libncurses5-dev \ mesa-common-dev \ zlib1g-dev \ " ENV GRASS_BUILD_PACKAGES=${GRASS_BUILD_PACKAGES} -ARG GRASS_CONFIG="--with-cxx \ +ARG GRASS_CONFIG="\ --enable-largefile \ - --with-proj-share=/usr/share/proj \ - --with-gdal=/usr/bin/gdal-config \ - --with-geos \ - --with-sqlite \ + --with-blas \ + --with-bzlib \ --with-cairo --with-cairo-ldflags=-lfontconfig \ - --with-freetype --with-freetype-includes=/usr/include/freetype2/ \ + --with-cxx \ --with-fftw \ - --with-postgres --with-postgres-includes=/usr/include/postgresql \ - --with-netcdf \ - --with-zstd \ - --with-bzlib \ - --with-pdal \ - --without-mysql \ - --with-blas \ + --with-freetype --with-freetype-includes=/usr/include/freetype2/ \ + --with-gdal=/usr/bin/gdal-config \ + --with-geos \ --with-lapack \ - --with-readline \ + --with-netcdf \ --with-odbc \ --with-openmp \ + --with-pdal \ + --with-postgres --with-postgres-includes=/usr/include/postgresql \ + --with-proj-share=/usr/share/proj \ + --with-readline \ + --with-sqlite \ + --with-zstd \ + --without-mysql \ " -ARG GRASS_PYTHON_PACKAGES="pip \ - setuptools \ - python-dateutil \ - python-magic \ - numpy \ +ARG GRASS_PYTHON_PACKAGES="\ Pillow \ - ply \ matplotlib \ + numpy \ + pip \ + ply \ psycopg2 \ + python-dateutil \ + python-magic \ + setuptools \ " ENV GRASS_PYTHON_PACKAGES=${GRASS_PYTHON_PACKAGES} @@ -148,31 +151,33 @@ ENV GRASS_CONFIG=${GRASS_CONFIG} FROM common_start as grass_with_gui -ARG GRASS_RUN_PACKAGES="${GRASS_RUN_PACKAGES} adwaita-icon-theme-full \ - libglu1-mesa \ - libgtk-3-0 \ - libnotify4 \ - libsdl2-2.0-0 \ - libxtst6 \ - librsvg2-common \ - gettext \ +ARG GRASS_RUN_PACKAGES="${GRASS_RUN_PACKAGES} \ + adwaita-icon-theme-full \ freeglut3 \ + gettext \ + libglu1-mesa \ libgstreamer-plugins-base1.0 \ + libgtk-3-0 \ libjpeg8 \ + libnotify4 \ libpng16-16 \ + librsvg2-common \ + libsdl2-2.0-0 \ libsm6 \ libtiff5 \ libwebkit2gtk-4.0 \ + libxtst6 \ " # librsvg2-common \ # (fix error (wxgui.py:7782): Gtk-WARNING **: 19:53:09.774: # Could not load a pixbuf from /org/gtk/libgtk/theme/Adwaita/assets/check-symbolic.svg. # This may indicate that pixbuf loaders or the mime database could not be found.) -ARG GRASS_BUILD_PACKAGES="${GRASS_BUILD_PACKAGES} adwaita-icon-theme-full \ +ARG GRASS_BUILD_PACKAGES="${GRASS_BUILD_PACKAGES} \ + adwaita-icon-theme-full \ + freeglut3-dev \ libgl1-mesa-dev \ libglu1-mesa-dev \ - freeglut3-dev \ libgstreamer-plugins-base1.0-dev \ libgtk-3-dev \ libjpeg-dev \ @@ -185,10 +190,11 @@ ARG GRASS_BUILD_PACKAGES="${GRASS_BUILD_PACKAGES} adwaita-icon-theme-full \ libxtst-dev \ " -ARG GRASS_CONFIG="${GRASS_CONFIG} --with-opengl \ - --with-x \ +ARG GRASS_CONFIG="${GRASS_CONFIG} \ --with-nls \ + --with-opengl \ --with-readline \ + --with-x \ " ARG GRASS_PYTHON_PACKAGES="${GRASS_PYTHON_PACKAGES} wxPython" # If you do not use any Gnome Accessibility features, to suppress warning diff --git a/docker/alpine/Dockerfile b/docker/alpine/Dockerfile index a099617759c..b6dbebf6666 100644 --- a/docker/alpine/Dockerfile +++ b/docker/alpine/Dockerfile @@ -11,9 +11,9 @@ ARG PYTHON_VERSION=3 # List of packages to be installed (proj-data omitted: 570.04 MB) ENV GRASS_RUN_PACKAGES="\ attr \ - build-base \ bash \ bison \ + build-base \ bzip2 \ cairo \ curl \ @@ -29,15 +29,15 @@ ENV GRASS_RUN_PACKAGES="\ gdal-driver-JP2OpenJPEG \ gdal-driver-LIBKML \ gdal-driver-MSSQLSpatial \ - gdal-driver-netCDF \ gdal-driver-ODBC \ gdal-driver-PG \ gdal-driver-PNG \ gdal-driver-WMS \ + gdal-driver-netCDF \ gdal-tools \ - gettext \ geos \ geos-dev \ + gettext \ git \ gnutls \ jsoncpp \ @@ -52,15 +52,15 @@ ENV GRASS_RUN_PACKAGES="\ musl \ musl-utils \ ncurses \ - openjpeg \ openblas \ - py3-numpy \ - py3-pillow \ - python3 \ + openjpeg \ pdal \ pdal-dev \ postgresql15-client \ proj-util \ + py3-numpy \ + py3-pillow \ + python3 \ sqlite \ sqlite-libs \ subversion \ @@ -89,23 +89,23 @@ FROM common as build # set configuration options, without wxGUI ENV GRASS_CONFIG="\ --enable-largefile \ + --with-bzlib \ + --with-cairo --with-cairo-ldflags=-lfontconfig \ --with-cxx \ - --with-proj-share=/usr/share/proj \ + --with-fftw \ --with-gdal \ - --with-pdal \ --with-geos \ + --with-openmp \ + --with-pdal \ + --with-postgres --with-postgres-includes=/usr/include/postgresql \ + --with-proj-share=/usr/share/proj \ --with-sqlite \ - --with-bzlib \ --with-zstd \ - --with-cairo --with-cairo-ldflags=-lfontconfig \ - --with-fftw \ - --with-postgres --with-postgres-includes=/usr/include/postgresql \ - --with-openmp \ --without-freetype \ - --without-opengl \ - --without-nls \ --without-mysql \ + --without-nls \ --without-odbc \ + --without-opengl \ " # Set environmental variables for GRASS GIS compilation, without debug symbols @@ -132,13 +132,13 @@ ENV GRASS_BUILD_PACKAGES="\ libjpeg-turbo-dev \ libpng-dev \ libpq-dev \ - openjpeg-dev \ openblas-dev \ + openjpeg-dev \ pdal \ pdal-dev \ proj-dev \ - python3-dev \ py3-numpy-dev \ + python3-dev \ sqlite-dev \ tar \ tiff-dev \ diff --git a/docker/debian/Dockerfile b/docker/debian/Dockerfile index 2ed59e1482e..8021ccf409e 100644 --- a/docker/debian/Dockerfile +++ b/docker/debian/Dockerfile @@ -18,8 +18,8 @@ WORKDIR /tmp RUN apt-get update && apt-get upgrade -y && \ apt-get install -y --no-install-recommends --no-install-suggests \ - build-essential \ bison \ + build-essential \ bzip2 \ cmake \ curl \ @@ -40,8 +40,8 @@ RUN apt-get update && apt-get upgrade -y && \ libgsl0-dev \ libjpeg-dev \ libjsoncpp-dev \ - libnetcdf-dev \ libncurses-dev \ + libnetcdf-dev \ libopenblas-dev \ libopenjp2-7 \ libopenjp2-7-dev \ @@ -153,24 +153,24 @@ ENV CXXFLAGS "$MYCXXFLAGS" ENV NUMTHREADS=4 RUN make distclean || echo "nothing to clean" RUN /src/grass_build/configure \ - --with-cxx \ --enable-largefile \ - --with-proj-share=/usr/share/proj \ - --with-gdal=/usr/bin/gdal-config \ - --with-geos \ - --with-sqlite \ + --with-bzlib \ --with-cairo --with-cairo-ldflags=-lfontconfig \ - --with-freetype --with-freetype-includes="/usr/include/freetype2/" \ + --with-cxx \ --with-fftw \ - --with-postgres --with-postgres-includes="/usr/include/postgresql" \ + --with-freetype --with-freetype-includes="/usr/include/freetype2/" \ + --with-gdal=/usr/bin/gdal-config \ + --with-geos \ --with-netcdf \ - --with-zstd \ - --with-bzlib \ --with-pdal \ + --with-postgres --with-postgres-includes="/usr/include/postgresql" \ + --with-proj-share=/usr/share/proj \ + --with-sqlite \ + --with-zstd \ --without-mysql \ --without-odbc \ - --without-openmp \ --without-opengl \ + --without-openmp \ && make -j $NUMTHREADS \ && make install && ldconfig diff --git a/docker/ubuntu/Dockerfile b/docker/ubuntu/Dockerfile index 6ea64aadab7..41fb3264c07 100644 --- a/docker/ubuntu/Dockerfile +++ b/docker/ubuntu/Dockerfile @@ -19,8 +19,9 @@ WORKDIR /tmp ARG GUI # Todo: re-consider required dev packages for addons (~400MB in dev packages) -ARG GRASS_RUN_PACKAGES="build-essential \ +ARG GRASS_RUN_PACKAGES="\ bison \ + build-essential \ bzip2 \ curl \ flex \ @@ -28,8 +29,6 @@ ARG GRASS_RUN_PACKAGES="build-essential \ gcc \ gdal-bin \ geos-bin \ - proj-bin \ - netcdf-bin \ git \ language-pack-en-base \ libcairo2 \ @@ -38,29 +37,29 @@ ARG GRASS_RUN_PACKAGES="build-essential \ libfftw3-dev \ libfreetype6 \ libgdal-dev \ + libgeos-dev \ + libgsl-dev \ libgsl27 \ libjpeg-turbo8 \ libjsoncpp-dev \ liblapacke-dev \ - libmagic1 \ libmagic-mgc \ + libmagic1 \ libncurses5 \ - libopenblas-dev \ + libomp-dev \ + libomp5 \ libopenblas-base \ + libopenblas-dev \ libopenjp2-7 \ - libomp5 \ - libomp-dev \ - libgeos-dev \ - libpdal-dev \ - libproj-dev \ - libpq-dev \ - libgsl-dev \ libpdal-base13 \ + libpdal-dev \ libpdal-plugin-hdf \ libpdal-plugins \ libpdal-util13 \ libpnglite0 \ + libpq-dev \ libpq5 \ + libproj-dev \ libpython3-all-dev \ libreadline8 \ libsqlite3-0 \ @@ -71,7 +70,9 @@ ARG GRASS_RUN_PACKAGES="build-essential \ mesa-utils \ moreutils \ ncurses-bin \ + netcdf-bin \ pdal \ + proj-bin \ proj-data \ python-is-python3 \ python3 \ @@ -87,56 +88,58 @@ ARG GRASS_RUN_PACKAGES="build-essential \ ENV GRASS_RUN_PACKAGES=${GRASS_RUN_PACKAGES} # Define build packages -ARG GRASS_BUILD_PACKAGES="cmake \ +ARG GRASS_BUILD_PACKAGES="\ + cmake \ libbz2-dev \ libcairo2-dev \ libfreetype6-dev \ - zlib1g-dev \ + libjpeg-dev \ + libncurses5-dev \ libnetcdf-dev \ libopenjp2-7-dev \ - libreadline-dev \ - libjpeg-dev \ libpnglite-dev \ + libreadline-dev \ libsqlite3-dev \ libtiff-dev \ libzstd-dev \ - libncurses5-dev \ mesa-common-dev \ zlib1g-dev \ " ENV GRASS_BUILD_PACKAGES=${GRASS_BUILD_PACKAGES} -ARG GRASS_CONFIG="--with-cxx \ +ARG GRASS_CONFIG="\ --enable-largefile \ - --with-proj-share=/usr/share/proj \ - --with-gdal=/usr/bin/gdal-config \ - --with-geos \ - --with-sqlite \ + --with-blas \ + --with-bzlib \ --with-cairo --with-cairo-ldflags=-lfontconfig \ - --with-freetype --with-freetype-includes=/usr/include/freetype2/ \ + --with-cxx \ --with-fftw \ - --with-postgres --with-postgres-includes=/usr/include/postgresql \ - --with-netcdf \ - --with-zstd \ - --with-bzlib \ - --with-pdal \ - --without-mysql \ - --with-blas \ + --with-freetype --with-freetype-includes=/usr/include/freetype2/ \ + --with-gdal=/usr/bin/gdal-config \ + --with-geos \ --with-lapack \ - --with-readline \ + --with-netcdf \ --with-odbc \ --with-openmp \ + --with-pdal \ + --with-postgres --with-postgres-includes=/usr/include/postgresql \ + --with-proj-share=/usr/share/proj \ + --with-readline \ + --with-sqlite \ + --with-zstd \ + --without-mysql \ " -ARG GRASS_PYTHON_PACKAGES="pip \ - setuptools \ - python-dateutil \ - python-magic \ - numpy \ +ARG GRASS_PYTHON_PACKAGES="\ Pillow \ - ply \ matplotlib \ + numpy \ + pip \ + ply \ psycopg2 \ + python-dateutil \ + python-magic \ + setuptools \ " ENV GRASS_PYTHON_PACKAGES=${GRASS_PYTHON_PACKAGES} @@ -148,31 +151,33 @@ ENV GRASS_CONFIG=${GRASS_CONFIG} FROM common_start as grass_with_gui -ARG GRASS_RUN_PACKAGES="${GRASS_RUN_PACKAGES} adwaita-icon-theme-full \ - libglu1-mesa \ - libgtk-3-0 \ - libnotify4 \ - libsdl2-2.0-0 \ - libxtst6 \ - librsvg2-common \ - gettext \ +ARG GRASS_RUN_PACKAGES="${GRASS_RUN_PACKAGES} \ + adwaita-icon-theme-full \ freeglut3 \ + gettext \ + libglu1-mesa \ libgstreamer-plugins-base1.0 \ + libgtk-3-0 \ libjpeg8 \ + libnotify4 \ libpng16-16 \ + librsvg2-common \ + libsdl2-2.0-0 \ libsm6 \ libtiff5 \ libwebkit2gtk-4.0 \ + libxtst6 \ " # librsvg2-common \ # (fix error (wxgui.py:7782): Gtk-WARNING **: 19:53:09.774: # Could not load a pixbuf from /org/gtk/libgtk/theme/Adwaita/assets/check-symbolic.svg. # This may indicate that pixbuf loaders or the mime database could not be found.) -ARG GRASS_BUILD_PACKAGES="${GRASS_BUILD_PACKAGES} adwaita-icon-theme-full \ +ARG GRASS_BUILD_PACKAGES="${GRASS_BUILD_PACKAGES} \ + adwaita-icon-theme-full \ + freeglut3-dev \ libgl1-mesa-dev \ libglu1-mesa-dev \ - freeglut3-dev \ libgstreamer-plugins-base1.0-dev \ libgtk-3-dev \ libjpeg-dev \ @@ -185,10 +190,11 @@ ARG GRASS_BUILD_PACKAGES="${GRASS_BUILD_PACKAGES} adwaita-icon-theme-full \ libxtst-dev \ " -ARG GRASS_CONFIG="${GRASS_CONFIG} --with-opengl \ - --with-x \ +ARG GRASS_CONFIG="${GRASS_CONFIG} \ --with-nls \ + --with-opengl \ --with-readline \ + --with-x \ " ARG GRASS_PYTHON_PACKAGES="${GRASS_PYTHON_PACKAGES} wxPython" # If you do not use any Gnome Accessibility features, to suppress warning diff --git a/docker/ubuntu_wxgui/Dockerfile b/docker/ubuntu_wxgui/Dockerfile index 36d33ffeb20..e15d5dcf478 100644 --- a/docker/ubuntu_wxgui/Dockerfile +++ b/docker/ubuntu_wxgui/Dockerfile @@ -29,8 +29,8 @@ WORKDIR /tmp RUN apt-get update && apt-get upgrade -y && \ apt-get install -y --no-install-recommends --no-install-suggests \ adwaita-icon-theme-full \ - build-essential \ bison \ + build-essential \ bzip2 \ cmake \ curl \ @@ -48,17 +48,17 @@ RUN apt-get update && apt-get upgrade -y && \ libfftw3-bin \ libfftw3-dev \ libfreetype6-dev \ - libgl1-mesa-dev \ libgdal-dev \ libgeos-dev \ + libgl1-mesa-dev \ libglu1-mesa-dev \ libgsl0-dev \ libgtk-3-0 \ libgtk-3-dev \ libjpeg-dev \ libjsoncpp-dev \ - libnetcdf-dev \ libncurses5-dev \ + libnetcdf-dev \ libnotify4 \ libopenblas-base \ libopenblas-dev \ @@ -68,8 +68,8 @@ RUN apt-get update && apt-get upgrade -y && \ libpq-dev \ libproj-dev \ libpython3-all-dev \ - librsvg2-common \ libreadline-dev \ + librsvg2-common \ libsdl2-2.0-0 \ libsqlite3-dev \ libtiff-dev \ @@ -191,26 +191,26 @@ ENV CXXFLAGS "$MYCXXFLAGS" ENV NUMTHREADS=4 RUN make distclean || echo "nothing to clean" RUN /src/grass_build/configure \ - --with-cxx \ --enable-largefile \ - --with-proj-share=/usr/share/proj \ - --with-gdal=/usr/bin/gdal-config \ - --with-geos \ - --with-sqlite \ + --with-bzlib \ --with-cairo --with-cairo-ldflags=-lfontconfig \ - --with-freetype --with-freetype-includes="/usr/include/freetype2/" \ + --with-cxx \ --with-fftw \ - --with-postgres --with-postgres-includes="/usr/include/postgresql" \ + --with-freetype --with-freetype-includes="/usr/include/freetype2/" \ + --with-gdal=/usr/bin/gdal-config \ + --with-geos \ --with-netcdf \ - --with-zstd \ - --with-bzlib \ + --with-nls \ --with-pdal \ + --with-postgres --with-postgres-includes="/usr/include/postgresql" \ + --with-proj-share=/usr/share/proj \ + --with-readline \ + --with-sqlite \ + --with-x \ + --with-zstd \ --without-mysql \ --without-odbc \ --without-openmp \ - --with-x \ - --with-nls \ - --with-readline \ && make -j $NUMTHREADS \ && make install && ldconfig From d99d840c160c8e90411d077e751dca9d0051b7b9 Mon Sep 17 00:00:00 2001 From: Anna Petrasova Date: Tue, 24 Sep 2024 22:38:14 -0400 Subject: [PATCH 283/471] grass.jupyter: fix interactivemap tests (#4379) --- .../jupyter/testsuite/interactivemap_test.py | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/python/grass/jupyter/testsuite/interactivemap_test.py b/python/grass/jupyter/testsuite/interactivemap_test.py index 6b8a7379548..493578596b5 100644 --- a/python/grass/jupyter/testsuite/interactivemap_test.py +++ b/python/grass/jupyter/testsuite/interactivemap_test.py @@ -103,24 +103,26 @@ def test_query_button(self): # Create InteractiveMap with ipyleaflet backend interactive_map = gj.InteractiveMap(map_backend="ipyleaflet") interactive_map.add_raster("elevation") - interactive_map.add_vector("roadsmajor") - interactive_map.add_query_button() - self.assertIsNotNone(interactive_map.map) - self.assertTrue(interactive_map.query_mode is False) - # Toggle query button to activate - interactive_map.query_mode = True - self.assertTrue(interactive_map.query_mode) - # Toggle query button to deactivate - interactive_map.query_mode = False - self.assertFalse(interactive_map.query_mode) + button = interactive_map.setup_query_interface() + self.assertIsNotNone(interactive_map._controllers[button].query_raster((0, 0))) + + @unittest.skipIf(not can_import_ipyleaflet(), "Cannot import ipyleaflet") + def test_draw(self): + """Test the draw_computational_region method.""" + # Create InteractiveMap + interactive_map = gj.InteractiveMap(map_backend="ipyleaflet") + button = interactive_map.setup_drawing_interface() + interactive_map._controllers[button].activate() + self.assertIsNotNone(interactive_map._controllers[button].save_button_control) @unittest.skipIf(not can_import_ipyleaflet(), "Cannot import ipyleaflet") def test_draw_computational_region(self): """Test the draw_computational_region method.""" # Create InteractiveMap - interactive_map = gj.InteractiveMap() - interactive_map.draw_computational_region() - self.assertTrue(callable(interactive_map.draw_computational_region)) + interactive_map = gj.InteractiveMap(map_backend="ipyleaflet") + button = interactive_map.setup_computational_region_interface() + interactive_map._controllers[button].activate() + self.assertIsNotNone(interactive_map._controllers[button].save_button_control) if __name__ == "__main__": From 45a0486cdbd5d7e930aca16c4a6a6397e7f6d221 Mon Sep 17 00:00:00 2001 From: Vaclav Petras Date: Tue, 24 Sep 2024 23:45:39 -0400 Subject: [PATCH 284/471] grass.app: Move mapset locking to the library (#4158) This moves the lock_mapset function to the library. It introduces only small changes to the code, i.e., proper refactoring and de-duplication in relation to the code elsewhere is still needed. However, this unifies the error handling to always raising a custom exception. It also fixes the reported user using the mapset (before always the current user, now, the owner of the lock file; for that, lock file is removed only after getting its owner). This also removes the debug message which I don't find particularly useful, for example it is currently misleading on Windows and it is relatively easy to confirm the actual existence in process manager when debugging. Change message wording to add clarity. Fix translatable message are fixed. Uses format function everywhere. Gets install path (GISBASE) automatically (requires caller to have environment set up which is likely a reasonable requirement at this point). Fixes order of reporting owner and deleting the file. Cleans up order of paths. --- lib/init/grass.py | 69 ++++----------------------------------- python/grass/app/data.py | 70 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 63 deletions(-) diff --git a/lib/init/grass.py b/lib/init/grass.py index 30c0a1a68ba..40312471e44 100755 --- a/lib/init/grass.py +++ b/lib/init/grass.py @@ -1281,65 +1281,6 @@ def set_language(grass_config_dir): gettext.install("grasslibs", gpath("locale")) -def lock_mapset(mapset_path, force_gislock_removal, user): - """Lock the mapset and return name of the lock file - - Behavior on error must be changed somehow; now it fatals but GUI case is - unresolved. - """ - if not os.path.exists(mapset_path): - fatal(_("Path '%s' doesn't exist") % mapset_path) - if not os.access(mapset_path, os.W_OK): - error = _("Path '%s' not accessible.") % mapset_path - stat_info = os.stat(mapset_path) - mapset_uid = stat_info.st_uid - if mapset_uid != os.getuid(): - # GTC %s is mapset's folder path - error = "%s\n%s" % ( - error, - _("You are not the owner of '%s'.") % mapset_path, - ) - fatal(error) - # Check for concurrent use - lockfile = os.path.join(mapset_path, ".gislock") - ret = call([gpath("etc", "lock"), lockfile, "%d" % os.getpid()]) - msg = None - if ret == 2: - if not force_gislock_removal: - msg = _( - "%(user)s is currently running GRASS in selected mapset" - " (file %(file)s found). Concurrent use not allowed.\n" - "You can force launching GRASS using -f flag" - " (note that you need permission for this operation)." - " Have another look in the processor " - "manager just to be sure..." - ) % {"user": user, "file": lockfile} - - else: - try_remove(lockfile) - message( - _( - "%(user)s is currently running GRASS in selected mapset" - " (file %(file)s found). Forcing to launch GRASS..." - ) - % {"user": user, "file": lockfile} - ) - elif ret != 0: - msg = ( - _("Unable to properly access '%s'.\nPlease notify system personnel.") - % lockfile - ) - - if msg: - raise Exception(msg) - debug( - "Mapset <{mapset}> locked using '{lockfile}'".format( - mapset=mapset_path, lockfile=lockfile - ) - ) - return lockfile - - # TODO: the gisrcrc here does not make sense, remove it from load_gisrc def unlock_gisrc_mapset(gisrc, gisrcrc): """Unlock mapset from the gisrc file""" @@ -2421,14 +2362,16 @@ def main(): location = mapset_settings.full_mapset + from grass.app.data import lock_mapset, MapsetLockingException + try: # check and create .gislock file lock_mapset( - mapset_settings.full_mapset, - user=user, - force_gislock_removal=params.force_gislock_removal, + mapset_path=mapset_settings.full_mapset, + force_lock_removal=params.force_gislock_removal, + message_callback=message, ) - except Exception as e: + except MapsetLockingException as e: fatal(e.args[0]) sys.exit(_("Exiting...")) diff --git a/python/grass/app/data.py b/python/grass/app/data.py index 439a6c3c4d1..2853b573f9c 100644 --- a/python/grass/app/data.py +++ b/python/grass/app/data.py @@ -15,8 +15,12 @@ import os import tempfile import getpass +import subprocess import sys from shutil import copytree, ignore_patterns +from pathlib import Path + +import grass.script as gs import grass.grassdb.config as cfg from grass.grassdb.checks import is_location_valid @@ -162,3 +166,69 @@ def ensure_default_data_hierarchy(): mapset_path = os.path.join(gisdbase, location, mapset) return gisdbase, location, mapset, mapset_path + + +class MapsetLockingException(Exception): + pass + + +def lock_mapset(mapset_path, force_lock_removal, message_callback): + """Acquire a lock for a mapset and return name of new lock file + + Raises MapsetLockingException when it is not possible to acquire a lock for the + given mapset either because of existing lock or due to insufficient permissions. + A corresponding localized message is given in the exception. + + A *message_callback* is a function which will be called to report messages about + certain states. Specifically, the function is called when forcibly unlocking the + mapset. + + Assumes that the runtime is set up (specifically that GISBASE is in + the environment). + """ + if not os.path.exists(mapset_path): + raise MapsetLockingException(_("Path '{}' doesn't exist").format(mapset_path)) + if not os.access(mapset_path, os.W_OK): + error = _("Path '{}' not accessible.").format(mapset_path) + stat_info = os.stat(mapset_path) + mapset_uid = stat_info.st_uid + if mapset_uid != os.getuid(): + error = "{error}\n{detail}".format( + error=error, + detail=_("You are not the owner of '{}'.").format(mapset_path), + ) + raise MapsetLockingException(error) + # Check for concurrent use + lockfile = os.path.join(mapset_path, ".gislock") + locker_path = os.path.join(os.environ["GISBASE"], "etc", "lock") + ret = subprocess.run( + [locker_path, lockfile, "%d" % os.getpid()], check=False + ).returncode + msg = None + if ret == 2: + if not force_lock_removal: + msg = _( + "{user} is currently running GRASS in selected mapset" + " (file {file} found). Concurrent use of one mapset not allowed.\n" + "You can force launching GRASS using -f flag" + " (assuming your have sufficient access permissions)." + " Confirm in a process manager " + "that there is no other process using the mapset." + ).format(user=Path(lockfile).owner(), file=lockfile) + else: + message_callback( + _( + "{user} is currently running GRASS in selected mapset" + " (file {file} found), but forcing to launch GRASS anyway..." + ).format(user=Path(lockfile).owner(), file=lockfile) + ) + gs.try_remove(lockfile) + elif ret != 0: + msg = _( + "Unable to properly access lock file '{name}'.\n" + "Please resolve this with your system administrator." + ).format(name=lockfile) + + if msg: + raise MapsetLockingException(msg) + return lockfile From 68c72455d926019017d71b709a7a8d80c67ea270 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 25 Sep 2024 03:56:23 +0000 Subject: [PATCH 285/471] CI(deps): Update alpine:3.20 Docker digest to e72ad07 (#4381) --- docker/alpine/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/alpine/Dockerfile b/docker/alpine/Dockerfile index b6dbebf6666..70c7e82afba 100644 --- a/docker/alpine/Dockerfile +++ b/docker/alpine/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.20@sha256:beefdbd8a1da6d2915566fde36db9db0b524eb737fc57cd1367effd16dc0d06d as common +FROM alpine:3.20@sha256:e72ad0747b9dc266fca31fb004580d316b6ae5b0fdbbb65f17bbe371a5b24cff as common # Based on: # https://github.com/mundialis/docker-grass-gis/blob/master/Dockerfile From 0f75c27476061785194994b43efecffc822e57de Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 25 Sep 2024 07:19:59 -0400 Subject: [PATCH 286/471] CI(deps): Update alpine:3.20 Docker digest to beefdbd (#4384) --- docker/alpine/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/alpine/Dockerfile b/docker/alpine/Dockerfile index 70c7e82afba..b6dbebf6666 100644 --- a/docker/alpine/Dockerfile +++ b/docker/alpine/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.20@sha256:e72ad0747b9dc266fca31fb004580d316b6ae5b0fdbbb65f17bbe371a5b24cff as common +FROM alpine:3.20@sha256:beefdbd8a1da6d2915566fde36db9db0b524eb737fc57cd1367effd16dc0d06d as common # Based on: # https://github.com/mundialis/docker-grass-gis/blob/master/Dockerfile From 82613638c197b04735be5b83d01f9f91a94456f8 Mon Sep 17 00:00:00 2001 From: Anna Petrasova Date: Wed, 25 Sep 2024 09:49:07 -0400 Subject: [PATCH 287/471] grass.jupyter: move save() to BaseSeriesMap class to reduce redundancy (#4378) --- python/grass/jupyter/baseseriesmap.py | 48 ++++++++++++++++++- python/grass/jupyter/seriesmap.py | 47 ------------------ .../grass/jupyter/tests/timeseriesmap_test.py | 5 -- python/grass/jupyter/timeseriesmap.py | 47 ------------------ 4 files changed, 47 insertions(+), 100 deletions(-) diff --git a/python/grass/jupyter/baseseriesmap.py b/python/grass/jupyter/baseseriesmap.py index 634e172f3c5..4497b16593d 100644 --- a/python/grass/jupyter/baseseriesmap.py +++ b/python/grass/jupyter/baseseriesmap.py @@ -25,7 +25,7 @@ import grass.script as gs from .map import Map -from .utils import get_number_of_cores +from .utils import get_number_of_cores, save_gif class BaseSeriesMap: @@ -210,3 +210,49 @@ def change_image(index): width="100%", display="inline-flex", flex_flow="row wrap" ) return widgets.HBox([play, slider, out_img], layout=layout) + + def save( + self, + filename, + duration=500, + label=True, + font=None, + text_size=12, + text_color="gray", + ): + """ + Creates a GIF animation of rendered layers. + + Text color must be in a format accepted by PIL ImageColor module. For supported + formats, visit: + https://pillow.readthedocs.io/en/stable/reference/ImageColor.html#color-names + + param str filename: name of output GIF file + param int duration: time to display each frame; milliseconds + param bool label: include label on each frame + param str font: font file + param int text_size: size of label text + param str text_color: color to use for the text. + """ + + # Render images if they have not been already + if not self._layers_rendered: + self.render() + + input_files = [] + for index in self._indices: + input_files.append(self._base_filename_dict[index]) + + save_gif( + input_files, + filename, + duration=duration, + label=label, + labels=self._labels, + font=font, + text_size=text_size, + text_color=text_color, + ) + + # Display the GIF + return filename diff --git a/python/grass/jupyter/seriesmap.py b/python/grass/jupyter/seriesmap.py index 8615ad488fb..ae6bde911b6 100644 --- a/python/grass/jupyter/seriesmap.py +++ b/python/grass/jupyter/seriesmap.py @@ -20,7 +20,6 @@ from .map import Map from .region import RegionManagerForSeries -from .utils import save_gif from .baseseriesmap import BaseSeriesMap @@ -165,49 +164,3 @@ def render(self): ) tasks = [(i,) for i in range(self.baseseries)] self._render(tasks) - - def save( - self, - filename, - duration=500, - label=True, - font=None, - text_size=12, - text_color="gray", - ): - """ - Creates a GIF animation of rendered layers. - - Text color must be in a format accepted by PIL ImageColor module. For supported - formats, visit: - https://pillow.readthedocs.io/en/stable/reference/ImageColor.html#color-names - - param str filename: name of output GIF file - param int duration: time to display each frame; milliseconds - param bool label: include label on each frame - param str font: font file - param int text_size: size of label text - param str text_color: color to use for the text - """ - - # Render images if they have not been already - if not self._layers_rendered: - self.render() - - tmp_files = [] - for file in self._base_filename_dict.values(): - tmp_files.append(file) - - save_gif( - tmp_files, - filename, - duration=duration, - label=label, - labels=self._labels, - font=font, - text_size=text_size, - text_color=text_color, - ) - - # Display the GIF - return filename diff --git a/python/grass/jupyter/tests/timeseriesmap_test.py b/python/grass/jupyter/tests/timeseriesmap_test.py index c4e20f7704e..d65f7fcb700 100644 --- a/python/grass/jupyter/tests/timeseriesmap_test.py +++ b/python/grass/jupyter/tests/timeseriesmap_test.py @@ -1,7 +1,6 @@ """Test TimeSeriesMap functions""" from pathlib import Path -import sys import pytest @@ -67,10 +66,6 @@ def test_render_layers(space_time_raster_dataset, fill_gaps): assert Path(filename).is_file() -@pytest.mark.xfail( - sys.platform == "win32", - reason="DejaVuSans.ttf file isn't found and not installed with GRASS", -) @pytest.mark.needs_solo_run def test_save(space_time_raster_dataset, tmp_path): """Test returns from animate and time_slider are correct object types""" diff --git a/python/grass/jupyter/timeseriesmap.py b/python/grass/jupyter/timeseriesmap.py index 3ac94bea93e..2acfa575126 100644 --- a/python/grass/jupyter/timeseriesmap.py +++ b/python/grass/jupyter/timeseriesmap.py @@ -20,7 +20,6 @@ from .map import Map from .region import RegionManagerForTimeSeries -from .utils import save_gif from .baseseriesmap import BaseSeriesMap @@ -312,49 +311,3 @@ def render(self): filename = os.path.join(self._tmpdir.name, f"{layer}.png") tasks.append((date, layer, filename)) self._render(tasks) - - def save( - self, - filename, - duration=500, - label=True, - font="DejaVuSans.ttf", - text_size=12, - text_color="gray", - ): - """ - Creates a GIF animation of rendered layers. - - Text color must be in a format accepted by PIL ImageColor module. For supported - formats, visit: - https://pillow.readthedocs.io/en/stable/reference/ImageColor.html#color-names - - param str filename: name of output GIF file - param int duration: time to display each frame; milliseconds - param bool label: include date/time stamp on each frame - param str font: font file - param int text_size: size of date/time text - param str text_color: color to use for the text. - """ - - # Render images if they have not been already - if not self._layers_rendered: - self.render() - - input_files = [] - for date in self._labels: - input_files.append(self._base_filename_dict[date]) - - save_gif( - input_files, - filename, - duration=duration, - label=label, - labels=self._labels, - font=font, - text_size=text_size, - text_color=text_color, - ) - - # Display the GIF - return filename From 7c651c3a672d5f36585cd6da2063654dea0e3b01 Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Wed, 25 Sep 2024 10:40:06 -0400 Subject: [PATCH 288/471] wxGUI/datacatalog: Replace lambda expression with named Function to fix E731 (#4377) --- .flake8 | 1 - gui/wxpython/datacatalog/tree.py | 23 ++++++++++++++--------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/.flake8 b/.flake8 index af8a021bd4c..d570fac7990 100644 --- a/.flake8 +++ b/.flake8 @@ -25,7 +25,6 @@ per-file-ignores = doc/python/m.distance.py: E501 doc/gui/wxpython/example/dialogs.py: F401 gui/scripts/d.wms.py: E501 - gui/wxpython/datacatalog/tree.py: E731, E402 gui/wxpython/dbmgr/base.py: E722 gui/wxpython/dbmgr/dialogs.py: E722 gui/wxpython/dbmgr/sqlbuilder.py: E722 diff --git a/gui/wxpython/datacatalog/tree.py b/gui/wxpython/datacatalog/tree.py index faa0a6aac73..9a460d05f53 100644 --- a/gui/wxpython/datacatalog/tree.py +++ b/gui/wxpython/datacatalog/tree.py @@ -1378,15 +1378,20 @@ def OnPasteMap(self, event): ) if not new_name: continue - callback = lambda gisrc2=gisrc2, gisrc=gisrc, cLayer=self.copy_layer[ - i - ], cMapset=self.copy_mapset[ - i - ], cMode=self.copy_mode, sMapset=self.selected_mapset[ - 0 - ], name=new_name: self._onDoneReprojection( - env2, gisrc2, gisrc, cLayer, cMapset, cMode, sMapset, name - ) + + def callback( + gisrc2=gisrc2, + gisrc=gisrc, + cLayer=self.copy_layer[i], + cMapset=self.copy_mapset[i], + cMode=self.copy_mode, + sMapset=self.selected_mapset[0], + name=new_name, + ): + self._onDoneReprojection( + env2, gisrc2, gisrc, cLayer, cMapset, cMode, sMapset, name + ) + dlg = CatalogReprojectionDialog( self, self._giface, From 09c629dee487247df4ac768294dd9bae5eb42b99 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 25 Sep 2024 17:09:09 -0400 Subject: [PATCH 289/471] CI(deps): Update actions/checkout action to v4.2.0 (#4387) --- .github/workflows/additional_checks.yml | 2 +- .github/workflows/clang-format-check.yml | 2 +- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/coverity.yml | 2 +- .github/workflows/create_release_draft.yml | 2 +- .github/workflows/docker.yml | 2 +- .github/workflows/gcc.yml | 2 +- .github/workflows/macos.yml | 2 +- .github/workflows/osgeo4w.yml | 2 +- .github/workflows/periodic_update.yml | 2 +- .github/workflows/pytest.yml | 2 +- .github/workflows/python-code-quality.yml | 2 +- .github/workflows/super-linter.yml | 2 +- .github/workflows/test-nix.yml | 2 +- .github/workflows/titles.yml | 2 +- .github/workflows/ubuntu.yml | 2 +- 16 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/workflows/additional_checks.yml b/.github/workflows/additional_checks.yml index d25cbb49194..1f8e355b487 100644 --- a/.github/workflows/additional_checks.yml +++ b/.github/workflows/additional_checks.yml @@ -24,7 +24,7 @@ jobs: steps: - name: Checkout repository contents - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 with: fetch-depth: 31 diff --git a/.github/workflows/clang-format-check.yml b/.github/workflows/clang-format-check.yml index 12b0b070ec9..f68483dea20 100644 --- a/.github/workflows/clang-format-check.yml +++ b/.github/workflows/clang-format-check.yml @@ -16,7 +16,7 @@ jobs: name: Formatting Check runs-on: ubuntu-latest steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 with: persist-credentials: false - uses: DoozyX/clang-format-lint-action@c71d0bf4e21876ebec3e5647491186f8797fde31 # v0.18.2 diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index bf07004bc82..14017253fd5 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -40,7 +40,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 - name: Set up Python uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 with: diff --git a/.github/workflows/coverity.yml b/.github/workflows/coverity.yml index 9f1fc5d843c..24c98c96601 100644 --- a/.github/workflows/coverity.yml +++ b/.github/workflows/coverity.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-22.04 if: github.repository == 'OSGeo/grass' steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 - name: Get dependencies run: | diff --git a/.github/workflows/create_release_draft.yml b/.github/workflows/create_release_draft.yml index 09da8ad1317..4751c3c43a0 100644 --- a/.github/workflows/create_release_draft.yml +++ b/.github/workflows/create_release_draft.yml @@ -30,7 +30,7 @@ jobs: contents: write steps: - name: Checks-out repository - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 with: ref: ${{ github.ref }} fetch-depth: 0 diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 1f60ed4c6d7..06b4c14ccbd 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -49,7 +49,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 with: fetch-depth: 0 - name: Docker meta diff --git a/.github/workflows/gcc.yml b/.github/workflows/gcc.yml index e3e23f43c04..24b9dcb4043 100644 --- a/.github/workflows/gcc.yml +++ b/.github/workflows/gcc.yml @@ -26,7 +26,7 @@ jobs: fail-fast: false steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 - name: Get dependencies run: | sudo apt-get update -y diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 4fad9d315a7..664649fa5c6 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -44,7 +44,7 @@ jobs: -mindepth 1 -maxdepth 1 -type f -print -delete # Rehash to forget about the deleted files hash -r - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 - name: Get current date cache key segment id: date # Year and week of year so cache key changes weekly diff --git a/.github/workflows/osgeo4w.yml b/.github/workflows/osgeo4w.yml index 027cecebf21..d9c67cc37c9 100644 --- a/.github/workflows/osgeo4w.yml +++ b/.github/workflows/osgeo4w.yml @@ -31,7 +31,7 @@ jobs: run: | git config --global core.autocrlf false git config --global core.eol lf - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 - uses: msys2/setup-msys2@ddf331adaebd714795f1042345e6ca57bd66cea8 # v2.24.1 with: path-type: inherit diff --git a/.github/workflows/periodic_update.yml b/.github/workflows/periodic_update.yml index bea3b53af4a..f5441297250 100644 --- a/.github/workflows/periodic_update.yml +++ b/.github/workflows/periodic_update.yml @@ -21,7 +21,7 @@ jobs: - name: Create URL to the run output id: vars run: echo "run-url=https://github.com/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" >> $GITHUB_OUTPUT - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 - name: "Check that autoconf scripts are up-to-date:" run: | rm -f config.guess config.sub diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 8e3b2286b1c..4ff31728abe 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -32,7 +32,7 @@ jobs: PYTHONWARNINGS: always steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 - name: Set up Python uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index 403d7393d0e..385ad6b9dd5 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -54,7 +54,7 @@ jobs: echo Bandit: ${{ env.BANDIT_VERSION }} echo Ruff: ${{ env.RUFF_VERSION }} - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 - name: Set up Python uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 diff --git a/.github/workflows/super-linter.yml b/.github/workflows/super-linter.yml index 44da8d9acb7..117f5cd43fb 100644 --- a/.github/workflows/super-linter.yml +++ b/.github/workflows/super-linter.yml @@ -25,7 +25,7 @@ jobs: statuses: write steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 with: # super-linter needs the full git history to get the # list of files that changed across commits diff --git a/.github/workflows/test-nix.yml b/.github/workflows/test-nix.yml index 22fa4f0061f..03f767730a9 100644 --- a/.github/workflows/test-nix.yml +++ b/.github/workflows/test-nix.yml @@ -28,7 +28,7 @@ jobs: contents: read steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 - name: Install nix uses: DeterminateSystems/nix-installer-action@da36cb69b1c3247ad7a1f931ebfd954a1105ef14 # v14 diff --git a/.github/workflows/titles.yml b/.github/workflows/titles.yml index b8e33c2fb37..f90e7275a2e 100644 --- a/.github/workflows/titles.yml +++ b/.github/workflows/titles.yml @@ -21,7 +21,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout base repository (doesn't include the PR changes) - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 - name: Call PR title validation function run: python utils/generate_release_notes.py check "${PR_TITLE}" "" "" env: diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 57b0821eec4..25ce2afdd67 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -59,7 +59,7 @@ jobs: fail-fast: false steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 - name: Invert inclusion list to an exclusion list id: get-exclude From 20985774a0965549fcc43c500f697aac5172c546 Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Thu, 26 Sep 2024 08:29:28 -0400 Subject: [PATCH 290/471] v.vol.rst: Fix resource leak issue in user1.c file (#4389) --- vector/v.vol.rst/user1.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/vector/v.vol.rst/user1.c b/vector/v.vol.rst/user1.c index f47324dff36..6a1c2f441e6 100644 --- a/vector/v.vol.rst/user1.c +++ b/vector/v.vol.rst/user1.c @@ -220,6 +220,7 @@ int INPUT(struct Map_info *In, char *column, char *scol, char *wheresql) if (a < 0) { G_warning(_("Can't insert %lf,%lf,%lf,%lf,%lf a=%d"), x, y, z, w, sm, a); + Vect_destroy_field_info(Fi); return -1; } @@ -322,6 +323,7 @@ int INPUT(struct Map_info *In, char *column, char *scol, char *wheresql) } else { fprintf(stderr, "ERROR: zero points in the given region!\n"); + Vect_destroy_field_info(Fi); return -1; } } @@ -332,6 +334,7 @@ int INPUT(struct Map_info *In, char *column, char *scol, char *wheresql) KMIN, KMAX); fprintf(stderr, "for smooth connection of segments, npmin > segmax " "(see manual) \n"); + Vect_destroy_field_info(Fi); return -1; } @@ -382,6 +385,7 @@ int INPUT(struct Map_info *In, char *column, char *scol, char *wheresql) } G_message(_("Bitmap mask created")); } + Vect_destroy_field_info(Fi); return 1; } From 845dfd066146b40539a02b6952ac2ea4eb92a82d Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Thu, 26 Sep 2024 10:13:52 -0400 Subject: [PATCH 291/471] wxGUI: Refactored try-except block to be more robust in dialogs.py (#4383) --- .flake8 | 1 - gui/wxpython/dbmgr/dialogs.py | 7 ++++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.flake8 b/.flake8 index d570fac7990..93ed48e30a6 100644 --- a/.flake8 +++ b/.flake8 @@ -26,7 +26,6 @@ per-file-ignores = doc/gui/wxpython/example/dialogs.py: F401 gui/scripts/d.wms.py: E501 gui/wxpython/dbmgr/base.py: E722 - gui/wxpython/dbmgr/dialogs.py: E722 gui/wxpython/dbmgr/sqlbuilder.py: E722 gui/wxpython/dbmgr/manager.py: E722 gui/wxpython/docs/wxgui_sphinx/conf.py: E402, W291 diff --git a/gui/wxpython/dbmgr/dialogs.py b/gui/wxpython/dbmgr/dialogs.py index 26e6d876371..0d7c6e9e523 100644 --- a/gui/wxpython/dbmgr/dialogs.py +++ b/gui/wxpython/dbmgr/dialogs.py @@ -222,10 +222,11 @@ def GetSQLString(self, updateValues=False): ctype = columns[name]["ctype"] value = columns[name]["values"][idx] id = columns[name]["ids"][idx] + widget = self.FindWindowById(id) try: - newvalue = self.FindWindowById(id).GetValue() - except: - newvalue = self.FindWindowById(id).GetLabel() + newvalue = widget.GetValue() + except AttributeError: + newvalue = widget.GetLabel() if newvalue: try: From b835f4d82ac15bd5ac3c4ac8d5df46ccb1765714 Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Thu, 26 Sep 2024 11:21:24 -0400 Subject: [PATCH 292/471] wxGUI: Fixed E266 - extra '#' for comments in gui_core/ (#4393) --- .flake8 | 7 +++---- gui/wxpython/gui_core/gselect.py | 2 +- gui/wxpython/gui_core/preferences.py | 8 ++++---- gui/wxpython/gui_core/widgets.py | 1 - 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/.flake8 b/.flake8 index 93ed48e30a6..f7949259d93 100644 --- a/.flake8 +++ b/.flake8 @@ -30,13 +30,12 @@ per-file-ignores = gui/wxpython/dbmgr/manager.py: E722 gui/wxpython/docs/wxgui_sphinx/conf.py: E402, W291 gui/wxpython/gcp/manager.py: E722 - gui/wxpython/gui_core/*: E266, E722 + gui/wxpython/gui_core/*: E722 gui/wxpython/gui_core/dialogs.py: E722 gui/wxpython/gui_core/forms.py: E722 gui/wxpython/gui_core/ghelp.py: E722 - gui/wxpython/gui_core/gselect.py: E266, E722 - gui/wxpython/gui_core/preferences.py: E266 - gui/wxpython/gui_core/widgets.py: E722, E266 + gui/wxpython/gui_core/gselect.py: E722 + gui/wxpython/gui_core/widgets.py: E722 gui/wxpython/image2target/*: F841, E722, E265 gui/wxpython/image2target/g.gui.image2target.py: E501, E265, F841 gui/wxpython/iscatt/*: F841, E722, F405, F403 diff --git a/gui/wxpython/gui_core/gselect.py b/gui/wxpython/gui_core/gselect.py index cec6963a527..6ad22403ecd 100644 --- a/gui/wxpython/gui_core/gselect.py +++ b/gui/wxpython/gui_core/gselect.py @@ -1289,7 +1289,7 @@ def __init__( style = 0 # disabled, read-only widget has no TextCtrl children (TODO: rewrite) # if not new and not multiple: - ### style = wx.CB_READONLY + # style = wx.CB_READONLY wx.ComboBox.__init__(self, parent, id, size=size, style=style, **kwargs) self.searchPath = searchPath diff --git a/gui/wxpython/gui_core/preferences.py b/gui/wxpython/gui_core/preferences.py index c2020b2ffba..a35d902bc7f 100644 --- a/gui/wxpython/gui_core/preferences.py +++ b/gui/wxpython/gui_core/preferences.py @@ -2419,7 +2419,7 @@ def LoadData(self): """Load data into list""" self.InsertColumn(0, _("Mapset")) self.InsertColumn(1, _("Owner")) - ### self.InsertColumn(2, _('Group')) + # self.InsertColumn(2, _('Group')) gisenv = gs.gisenv() locationPath = os.path.join(gisenv["GISDBASE"], gisenv["LOCATION_NAME"]) @@ -2433,14 +2433,14 @@ def LoadData(self): except KeyError: self.SetItem(index, 1, "nobody") # FIXME: get group name - ### self.SetStringItem(index, 2, "%-8s" % stat_info.st_gid) + # self.SetStringItem(index, 2, "%-8s" % stat_info.st_gid) else: # FIXME: no pwd under MS Windows (owner: 0, group: 0) self.SetItem(index, 1, "%-8s" % stat_info.st_uid) - ### self.SetStringItem(index, 2, "%-8s" % stat_info.st_gid) + # self.SetStringItem(index, 2, "%-8s" % stat_info.st_gid) self.SetColumnWidth(col=0, width=wx.LIST_AUTOSIZE) - ### self.SetColumnWidth(col = 1, width = wx.LIST_AUTOSIZE) + # self.SetColumnWidth(col = 1, width = wx.LIST_AUTOSIZE) def OnCheckItem(self, index, flag): """Mapset checked/unchecked""" diff --git a/gui/wxpython/gui_core/widgets.py b/gui/wxpython/gui_core/widgets.py index 0ec50553114..c959ebbbf50 100644 --- a/gui/wxpython/gui_core/widgets.py +++ b/gui/wxpython/gui_core/widgets.py @@ -435,7 +435,6 @@ class NumTextCtrl(TextCtrl): """Class derived from wx.TextCtrl for numerical values only""" def __init__(self, parent, **kwargs): - ## self.precision = kwargs.pop('prec') TextCtrl.__init__( self, parent=parent, validator=NTCValidator(flag="DIGIT_ONLY"), **kwargs ) From 549ea90713722361b7aa80f61016747e2e07ac80 Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Thu, 26 Sep 2024 11:30:56 -0400 Subject: [PATCH 293/471] r3.in.v5d: Fix unchecked return value from lseek (#4141) This addresses an issue identified by Coverity Scan (CID: 1207300), where the return value of the lseek function is not checked. Functions return as in other error states after issuing a warning. Small step was taken towards consistency and simplification in another function which uses prints otherwise. --- raster3d/r3.in.v5d/v5d.c | 62 +++++++++++++++++++++++++++++++--------- 1 file changed, 49 insertions(+), 13 deletions(-) diff --git a/raster3d/r3.in.v5d/v5d.c b/raster3d/r3.in.v5d/v5d.c index 445d6f29b9f..54f75f2361a 100644 --- a/raster3d/r3.in.v5d/v5d.c +++ b/raster3d/r3.in.v5d/v5d.c @@ -63,6 +63,9 @@ #include #include #include +#include +#include + #include "binio.h" #include "v5d.h" #include "vis5d.h" @@ -1232,7 +1235,10 @@ static int read_comp_header(int f, v5dstruct *v) unsigned int id; /* reset file position to start of file */ - lseek(f, 0, SEEK_SET); + if (lseek(f, 0, SEEK_SET) == -1) { + G_warning(_("Unable to seek: %s"), strerror(errno)); + return 0; + } /* read file ID */ read_int4(f, (int *)&id); @@ -1334,8 +1340,8 @@ static int read_comp_header(int f, v5dstruct *v) /* skip ahead by 'gridsize' bytes */ if (lseek(f, gridsize, SEEK_CUR) == -1) { - printf("Error: Unexpected end of file, "); - printf("file may be corrupted.\n"); + G_warning(_("Error: Unexpected end of file, file may be " + "corrupted.")); return 0; } min = -(125.0 + gb) / ga; @@ -1478,7 +1484,10 @@ static int read_comp_grid(v5dstruct *v, int time, int var, float *ga, float *gb, /* move to position in file */ pos = grid_position(v, time, var); - lseek(f, pos, SEEK_SET); + if (lseek(f, pos, SEEK_SET) == -1) { + G_warning(_("Unable to seek: %s"), strerror(errno)); + return 0; + } if (v->FileFormat == 0x80808083) { /* read McIDAS grid and file numbers */ @@ -1551,7 +1560,13 @@ static int read_comp_grid(v5dstruct *v, int time, int var, float *ga, float *gb, */ static int read_v5d_header(v5dstruct *v) { -#define SKIP(N) lseek(f, N, SEEK_CUR) +#define SKIP(N) \ + do { \ + if (lseek(f, N, SEEK_CUR) == -1) { \ + G_warning(_("Unable to seek: %s"), strerror(errno)); \ + return 0; \ + } \ + } while (0) int end_of_header = 0; unsigned int id; int idlen, var, numargs; @@ -1870,13 +1885,19 @@ static int read_v5d_header(v5dstruct *v) case TAG_END: /* end of header */ end_of_header = 1; - lseek(f, length, SEEK_CUR); + if (lseek(f, length, SEEK_CUR) == -1) { + G_warning(_("Unable to seek: %s"), strerror(errno)); + return 0; + } break; default: /* unknown tag, skip to next tag */ printf("Unknown tag: %d length=%d\n", tag, length); - lseek(f, length, SEEK_CUR); + if (lseek(f, length, SEEK_CUR) == -1) { + G_warning(_("Unable to seek: %s"), strerror(errno)); + return 0; + } break; } } @@ -1966,7 +1987,10 @@ int v5dReadCompressedGrid(v5dstruct *v, int time, int var, float *ga, float *gb, /* move to position in file */ pos = grid_position(v, time, var); - lseek(v->FileDesc, pos, SEEK_SET); + if (lseek(v->FileDesc, pos, SEEK_SET) == -1) { + G_warning(_("Unable to seek: %s"), strerror(errno)); + return 0; + } /* read ga, gb arrays */ read_float4_array(v->FileDesc, ga, v->Nl[var]); @@ -2118,7 +2142,10 @@ static int write_v5d_header(v5dstruct *v) } /* set file pointer to start of file */ - lseek(f, 0, SEEK_SET); + if (lseek(f, 0, SEEK_SET) == -1) { + G_warning(_("Unable to seek: %s"), strerror(errno)); + return 0; + } v->CurPos = 0; /* @@ -2222,7 +2249,10 @@ static int write_v5d_header(v5dstruct *v) /* We're writing to a brand new file. Reserve 10000 bytes */ /* for future header growth. */ WRITE_TAG(v, TAG_END, 10000); - lseek(f, 10000, SEEK_CUR); + if (lseek(f, 10000, SEEK_CUR) == -1) { + G_warning(_("Unable to seek: %s"), strerror(errno)); + return 0; + } /* Let file pointer indicate where first grid is stored */ v->FirstGridPos = ltell(f); @@ -2336,7 +2366,7 @@ int v5dWriteCompressedGrid(const v5dstruct *v, int time, int var, pos = grid_position(v, time, var); if (lseek(v->FileDesc, pos, SEEK_SET) < 0) { /* lseek failed, return error */ - printf("Error in v5dWrite[Compressed]Grid: seek failed, disk full?\n"); + G_warning(_("Unable to seek: %s"), strerror(errno)); return 0; } @@ -2452,9 +2482,15 @@ int v5dCloseFile(v5dstruct *v) if (v->Mode == 'w') { /* rewrite header because writing grids updates the minval and */ /* maxval fields */ - lseek(v->FileDesc, 0, SEEK_SET); + if (lseek(v->FileDesc, 0, SEEK_SET) == -1) { + G_warning(_("Unable to seek: %s"), strerror(errno)); + return 0; + } status = write_v5d_header(v); - lseek(v->FileDesc, 0, SEEK_END); + if (lseek(v->FileDesc, 0, SEEK_END) == -1) { + G_warning(_("Unable to seek: %s"), strerror(errno)); + return 0; + } close(v->FileDesc); } else if (v->Mode == 'r') { From edea8103d927cf4a269fcdd12fbbab9aa4b66e3d Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Thu, 26 Sep 2024 12:55:56 -0400 Subject: [PATCH 294/471] wxGUI: Fix E722 Warnings by Specifying Exception Types in dbmgr/ (#4382) --- .flake8 | 3 --- gui/wxpython/dbmgr/base.py | 23 +++++++++++++++-------- gui/wxpython/dbmgr/manager.py | 2 +- gui/wxpython/dbmgr/sqlbuilder.py | 2 +- 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/.flake8 b/.flake8 index f7949259d93..dd80ca9b6a0 100644 --- a/.flake8 +++ b/.flake8 @@ -25,9 +25,6 @@ per-file-ignores = doc/python/m.distance.py: E501 doc/gui/wxpython/example/dialogs.py: F401 gui/scripts/d.wms.py: E501 - gui/wxpython/dbmgr/base.py: E722 - gui/wxpython/dbmgr/sqlbuilder.py: E722 - gui/wxpython/dbmgr/manager.py: E722 gui/wxpython/docs/wxgui_sphinx/conf.py: E402, W291 gui/wxpython/gcp/manager.py: E722 gui/wxpython/gui_core/*: E722 diff --git a/gui/wxpython/dbmgr/base.py b/gui/wxpython/dbmgr/base.py index 66321fcafda..d7ea818da4c 100644 --- a/gui/wxpython/dbmgr/base.py +++ b/gui/wxpython/dbmgr/base.py @@ -208,7 +208,7 @@ def LoadData(self, layer, columns=None, where=None, sql=None): try: # for maps connected via v.external keyId = columns.index(keyColumn) - except: + except ValueError: keyId = -1 # read data @@ -961,7 +961,7 @@ def OnLayerPageChanged(self, event): self.layerPage[self.selLayer]["data"] ).GetItemCount() ) - except: + except Exception: pass if idCol: @@ -1640,7 +1640,7 @@ def OnDataItemAdd(self, event): if dlg.ShowModal() == wx.ID_OK: try: # get category number cat = int(dlg.GetValues(columns=[keyColumn])[0]) - except: + except ValueError: cat = -1 try: @@ -1672,7 +1672,7 @@ def OnDataItemAdd(self, event): values[i] = int(float(values[i])) elif tlist.columns[columnName[i]]["ctype"] == float: values[i] = float(values[i]) - except: + except ValueError: raise ValueError( _("Value '%(value)s' needs to be entered as %(type)s.") % { @@ -1680,6 +1680,13 @@ def OnDataItemAdd(self, event): "type": tlist.columns[columnName[i]]["type"], } ) + except KeyError: + raise KeyError( + _("Column '%(column)s' does not exist.") + % { + "column": columnName[i], + } + ) columnsString += "%s," % columnName[i] if tlist.columns[columnName[i]]["ctype"] == str: @@ -3809,7 +3816,7 @@ def OnDeleteLayer(self, event): """Delete layer""" try: layer = int(self.deleteLayer.GetValue()) - except: + except ValueError: return RunCommand( @@ -3856,10 +3863,10 @@ def OnChangeLayer(self, event): """Layer number of layer to be deleted is changed""" try: layer = int(event.GetString()) - except: + except ValueError: try: - layer = self.mapDBInfo.layers.keys()[0] - except: + layer = list(self.mapDBInfo.layers.keys())[0] + except IndexError: return if self.GetCurrentPage() == self.modifyPanel: diff --git a/gui/wxpython/dbmgr/manager.py b/gui/wxpython/dbmgr/manager.py index c866bd55650..b1cbd93ab71 100644 --- a/gui/wxpython/dbmgr/manager.py +++ b/gui/wxpython/dbmgr/manager.py @@ -70,7 +70,7 @@ def __init__( self.parent = parent try: mapdisplay = self.parent.GetMapDisplay() - except: + except AttributeError: mapdisplay = None DbMgrBase.__init__( diff --git a/gui/wxpython/dbmgr/sqlbuilder.py b/gui/wxpython/dbmgr/sqlbuilder.py index 36827698efc..5e6fe0e7706 100644 --- a/gui/wxpython/dbmgr/sqlbuilder.py +++ b/gui/wxpython/dbmgr/sqlbuilder.py @@ -359,7 +359,7 @@ def OnUniqueValues(self, event, justsample=False): try: idx = self.list_columns.GetSelections()[0] column = self.list_columns.GetString(idx) - except: + except IndexError: self.list_values.Clear() return From c502c1c05f7208a48f1b44b835ab23ac9d1708be Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Thu, 26 Sep 2024 18:35:30 -0400 Subject: [PATCH 295/471] checks: Fix E402 Flake8 in for wxGUI Sphinx doc (#4391) This fixes Flake8 E402 (Module level import not at top of file) for Sphinx documentation generator configuration script by removing import path manipulation block which was not properly working anyway. --- .flake8 | 1 - gui/wxpython/docs/wxgui_sphinx/conf.py | 12 ------------ 2 files changed, 13 deletions(-) diff --git a/.flake8 b/.flake8 index dd80ca9b6a0..7bad3afc185 100644 --- a/.flake8 +++ b/.flake8 @@ -25,7 +25,6 @@ per-file-ignores = doc/python/m.distance.py: E501 doc/gui/wxpython/example/dialogs.py: F401 gui/scripts/d.wms.py: E501 - gui/wxpython/docs/wxgui_sphinx/conf.py: E402, W291 gui/wxpython/gcp/manager.py: E722 gui/wxpython/gui_core/*: E722 gui/wxpython/gui_core/dialogs.py: E722 diff --git a/gui/wxpython/docs/wxgui_sphinx/conf.py b/gui/wxpython/docs/wxgui_sphinx/conf.py index 3641c2aab48..f0b61a31996 100644 --- a/gui/wxpython/docs/wxgui_sphinx/conf.py +++ b/gui/wxpython/docs/wxgui_sphinx/conf.py @@ -10,21 +10,9 @@ # All configuration values have a default; values that are commented out # serve to show the default. -import sys -import os from datetime import date import string from shutil import copy - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -if not os.getenv("GISBASE"): - sys.exit("GISBASE not defined") -sys.path.insert( - 0, os.path.abspath(os.path.join(os.environ["GISBASE"], "etc", "python", "grass")) -) - from grass.script import core footer_tmpl = string.Template( From e4c87ece77a2f35784e344a08acf4257ccfd4d50 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 26 Sep 2024 23:01:44 +0000 Subject: [PATCH 296/471] CI(deps): Update ruff to v0.6.8 (#4394) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * CI(deps): Update ruff to v0.6.8 * style: Use `kargs.get("layerTree")` instead of `kargs.get("layerTree", None)` (SIM910) * style: Simplify chained boolean comparison * Apply black formatting --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Edouard Choinière <27212526+echoix@users.noreply.github.com> --- .github/workflows/python-code-quality.yml | 2 +- .pre-commit-config.yaml | 2 +- gui/wxpython/gui_core/gselect.py | 2 +- scripts/r.in.wms/wms_drv.py | 8 ++------ 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index 385ad6b9dd5..9febdd990e3 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -36,7 +36,7 @@ jobs: # renovate: datasource=pypi depName=bandit BANDIT_VERSION: "1.7.10" # renovate: datasource=pypi depName=ruff - RUFF_VERSION: "0.6.7" + RUFF_VERSION: "0.6.8" runs-on: ${{ matrix.os }} permissions: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 74423979b22..b7a517fba37 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -37,7 +37,7 @@ repos: ) - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.6.7 + rev: v0.6.8 hooks: # Run the linter. - id: ruff diff --git a/gui/wxpython/gui_core/gselect.py b/gui/wxpython/gui_core/gselect.py index 6ad22403ecd..4bb9af5b50e 100644 --- a/gui/wxpython/gui_core/gselect.py +++ b/gui/wxpython/gui_core/gselect.py @@ -414,7 +414,7 @@ def SetData(self, **kargs): self.multiple = kargs["multiple"] if "onPopup" in kargs: self.onPopup = kargs["onPopup"] - if kargs.get("layerTree", None): + if kargs.get("layerTree"): self.filterItems = [] # reset ltype = kargs["type"] for layer in kargs["layerTree"].GetVisibleLayers(skipDigitized=True): diff --git a/scripts/r.in.wms/wms_drv.py b/scripts/r.in.wms/wms_drv.py index 186157ad6db..dd666573cfe 100644 --- a/scripts/r.in.wms/wms_drv.py +++ b/scripts/r.in.wms/wms_drv.py @@ -736,9 +736,7 @@ def _findTileMats(self, tile_mats, region, bbox): best_diff = best_scale_den - scale_den mat_diff = mat_scale_den - scale_den - if (best_diff < mat_diff and mat_diff < 0) or ( - best_diff > mat_diff and best_diff > 0 - ): + if (best_diff < mat_diff < 0) or (best_diff > mat_diff and best_diff > 0): best_t_mat = t_mat best_scale_den = mat_scale_den @@ -1020,9 +1018,7 @@ def _parseTilePattern(self, group_t_patts, bbox, region): best_diff = best_res - res[comp_res] tile_diff = t_res[comp_res] - res[comp_res] - if (best_diff < tile_diff and tile_diff < 0) or ( - best_diff > tile_diff and best_diff > 0 - ): + if (best_diff < tile_diff < 0) or (best_diff > tile_diff and best_diff > 0): best_res = t_res[comp_res] best_patt = pattern From bcb29389919dce73ccb9339a568b7bc6d5920a39 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 27 Sep 2024 10:39:34 -0400 Subject: [PATCH 297/471] CI(deps): Update docker/build-push-action action to v6.8.0 (#4398) --- .github/workflows/docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 06b4c14ccbd..824fbe18e1e 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -76,7 +76,7 @@ jobs: password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build and push id: docker_build - uses: docker/build-push-action@5cd11c3a4ced054e52742c5fd54dca954e0edd85 # v6.7.0 + uses: docker/build-push-action@32945a339266b759abcbdc89316275140b0fc960 # v6.8.0 with: push: true pull: true From 7f05bb3574f4fcd0cfc97dbb91d8fc9666b39661 Mon Sep 17 00:00:00 2001 From: Vaclav Petras Date: Sat, 28 Sep 2024 05:39:56 -0400 Subject: [PATCH 298/471] style: Remove misleading author info from wxGUI photo and image code (#4403) Just to be clear that I did not create these files, I'm removing my name which was copy-pasted into these files while duplicating the code from somewhere else. --- gui/wxpython/image2target/g.gui.image2target.py | 2 -- gui/wxpython/image2target/ii2t_statusbar.py | 3 --- gui/wxpython/photo2image/g.gui.photo2image.py | 1 - gui/wxpython/photo2image/ip2i_statusbar.py | 3 --- 4 files changed, 9 deletions(-) diff --git a/gui/wxpython/image2target/g.gui.image2target.py b/gui/wxpython/image2target/g.gui.image2target.py index 97c67b20354..aabfbbe87d2 100755 --- a/gui/wxpython/image2target/g.gui.image2target.py +++ b/gui/wxpython/image2target/g.gui.image2target.py @@ -98,8 +98,6 @@ """ Module to run GCP management tool as stadalone application. - -@author Vaclav Petras (standalone module) """ import os diff --git a/gui/wxpython/image2target/ii2t_statusbar.py b/gui/wxpython/image2target/ii2t_statusbar.py index 3c5ee378a1e..7ff80e621d3 100644 --- a/gui/wxpython/image2target/ii2t_statusbar.py +++ b/gui/wxpython/image2target/ii2t_statusbar.py @@ -11,9 +11,6 @@ This program is free software under the GNU General Public License (>=v2). Read the file COPYING that comes with GRASS for details. - -@author Vaclav Petras (statusbar refactoring) -@author Anna Kratochvilova (statusbar refactoring) """ import wx diff --git a/gui/wxpython/photo2image/g.gui.photo2image.py b/gui/wxpython/photo2image/g.gui.photo2image.py index 21635175374..65b4141873e 100755 --- a/gui/wxpython/photo2image/g.gui.photo2image.py +++ b/gui/wxpython/photo2image/g.gui.photo2image.py @@ -68,7 +68,6 @@ """ Module to run GCP management tool as stadalone application. -@author Vaclav Petras (standalone module) """ import os import grass.script as gs diff --git a/gui/wxpython/photo2image/ip2i_statusbar.py b/gui/wxpython/photo2image/ip2i_statusbar.py index 336dcd6903f..75a39b8141e 100644 --- a/gui/wxpython/photo2image/ip2i_statusbar.py +++ b/gui/wxpython/photo2image/ip2i_statusbar.py @@ -11,9 +11,6 @@ This program is free software under the GNU General Public License (>=v2). Read the file COPYING that comes with GRASS for details. - -@author Vaclav Petras (statusbar refactoring) -@author Anna Kratochvilova (statusbar refactoring) """ import wx From 4f689febc42a95e7a8fb113122a43fa96596c0a2 Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Sat, 28 Sep 2024 06:11:03 -0400 Subject: [PATCH 299/471] docs: fixed E265 from image2target/ (#4399) * removed e265 * update .flake8 * removed commented out parser code * removed parser code --- .flake8 | 4 +- .../image2target/g.gui.image2target.py | 111 ------------------ 2 files changed, 2 insertions(+), 113 deletions(-) diff --git a/.flake8 b/.flake8 index 7bad3afc185..c3256ec22bf 100644 --- a/.flake8 +++ b/.flake8 @@ -32,8 +32,8 @@ per-file-ignores = gui/wxpython/gui_core/ghelp.py: E722 gui/wxpython/gui_core/gselect.py: E722 gui/wxpython/gui_core/widgets.py: E722 - gui/wxpython/image2target/*: F841, E722, E265 - gui/wxpython/image2target/g.gui.image2target.py: E501, E265, F841 + gui/wxpython/image2target/*: F841, E722 + gui/wxpython/image2target/g.gui.image2target.py: E501, F841 gui/wxpython/iscatt/*: F841, E722, F405, F403 gui/wxpython/lmgr/frame.py: F841, E722 # layertree still includes some formatting issues (it is ignored by Black) diff --git a/gui/wxpython/image2target/g.gui.image2target.py b/gui/wxpython/image2target/g.gui.image2target.py index aabfbbe87d2..37d33c2d70e 100755 --- a/gui/wxpython/image2target/g.gui.image2target.py +++ b/gui/wxpython/image2target/g.gui.image2target.py @@ -30,71 +30,6 @@ # % keyword: GCP # %end -##%option G_OPT_M_LOCATION -##% key: source_project -##% label: The name of the source project (has no projection) -##% description: The source project (location) has no CRS -###% section: source -##% required: yes -##%end - -##%option G_OPT_M_MAPSET -##% key: source_mapset -##% label: The name of the source mapset (has no projection) -##% description: The name of the source mapset (has no projection) -###% section: source -##% required: yes -##%end - -##%option G_OPT_I_GROUP -##% key: source_group -##% required: yes -##% section: source -##%end - -##%option G_OPT_R_INPUT -##% key: source_image -##% required: yes -###% section: source -##%end - -##%option G_OPT_R_INPUT -##% key: target_image -##% label: The name of the image that is already georeferenced used to find location of GCPs -##% description: The name of the image that is already georeferenced used to find the location of GCPs -###% section: target -##% required: no -##%end - -##%option -##% key: camera -##% type: string -##% label: The name of the camera (generated in i.ortho.camera) -##% description: The name of the camera (generated in i.ortho.camera) -##% required: yes -###% section: parameters -##%end - -##%option -##% key: order -##% type: string -##% label: The rectification order -##% description: The rectification order -##% required: yes -##% answer: 1 -###% section: parameters -##%end - -##%option -##% key: extension -##% type: string -##% label: The name of the output files extension -##% description: The name of the output files extension -##% required: yes -##% answer: _ii2t_out -##% section: target -##%end - """ Module to run GCP management tool as stadalone application. @@ -127,54 +62,8 @@ def main(): else: os.environ["GRASS_RENDER_IMMEDIATE"] = "cairo" - # if options["source_location"]: - # src_loc = options["source_location"] - # else: - # gscript.fatal(_("No georeferenced source location provided")) - - # if options["source_mapset"]: - # src_mpt = options["source_mapset"] - # else: - # gscript.fatal(_("No georeferenced source mapset provided")) - - # if options["source_group"]: - # src_grp = options["source_group"] - # else: - # gscript.fatal(_("Please provide a source group name to process")) - - # if options['source_image']: - # src_ras = options["source_image"] - # else: - # gscript.fatal(_("Please provide a source image map name to process")) - - # if options["target_image"]: - # tgt_ras = options["target_image"] - # else: - # gscript.fatal(_("No georeferenced target map provided")) - - # if options["camera"]: - # camera = options["camera"] - # else: - # gscript.fatal(_( - # "Please provide a camera name (generated by i.ortho.camera)" - # )) - - # if options["order"]: - # order = options["order"] - # else: - # gscript.fatal(_("Please provive an order value")) - - # if options["extension"]: - # extension = options["extension"] - # else: - # gscript.fatal(_("Please provide an output file extension")) - app = wx.App() - # wizard = GCPWizard(parent=None, giface=StandaloneGrassInterface(), - # srcloc=src_loc,srcmpt=src_mpt,srcgrp=src_grp,srcras=src_ras, - # tgtras=tgt_ras,camera=camera, order=order, extension=extension) - wizard = GCPWizard(parent=None, giface=StandaloneGrassInterface()) app.MainLoop() From fee26ece86a1ee18a85528244bcc750b77e6b535 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hern=C3=A1n=20De=20Angelis?= <51515911+dhdeangelis@users.noreply.github.com> Date: Sat, 28 Sep 2024 19:44:32 +0200 Subject: [PATCH 300/471] g.gui.iclass: Add explanation of expected format for vector layers (#4411) --- gui/wxpython/iclass/g.gui.iclass.html | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/gui/wxpython/iclass/g.gui.iclass.html b/gui/wxpython/iclass/g.gui.iclass.html index f6169b1d8f6..dd789cd75b5 100644 --- a/gui/wxpython/iclass/g.gui.iclass.html +++ b/gui/wxpython/iclass/g.gui.iclass.html @@ -35,7 +35,6 @@

    DESCRIPTION

  • write signature file
  • import vector map
  • export vector map with attribute table
  • -
    @@ -62,6 +61,13 @@

    DESCRIPTION

    By doing this, the user can see how much of the image is likely to be put into the class associated with the signature. +

    wxIClass can also import training areas defined in a vector layer. In that case the program expects the vector layer to have the following columns defined: +

      +
    • cat: category value
    • +
    • class: a string with the class name
    • +
    • color: a color defined using format "RRR:GGG:BBB"
    • +
    +

    The spectral signatures are composed of region means and covariance matrices. These region means and covariance matrices are used in From 49f67fdf5355ad825e1849fe490021c30af7b673 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Sat, 28 Sep 2024 15:11:20 -0400 Subject: [PATCH 301/471] style: Fix negate-equal-op (SIM201) (#4407) * style: Fix negate-equal-op (SIM201) Ruff rule: https://docs.astral.sh/ruff/rules/negate-equal-op/ * style: Fix repeated-equality-comparison (PLR1714) on changed code --- gui/wxpython/animation/dialogs.py | 2 +- gui/wxpython/gcp/manager.py | 10 +++++----- gui/wxpython/gcp/mapdisplay.py | 2 +- gui/wxpython/gui_core/vselect.py | 2 +- gui/wxpython/iclass/frame.py | 4 ++-- gui/wxpython/image2target/ii2t_manager.py | 6 +++--- gui/wxpython/image2target/ii2t_mapdisplay.py | 2 +- gui/wxpython/iscatt/plots.py | 10 +++++----- gui/wxpython/mapdisp/frame.py | 6 +++--- gui/wxpython/nviz/mapwindow.py | 4 ++-- gui/wxpython/nviz/wxnviz.py | 2 +- gui/wxpython/photo2image/ip2i_manager.py | 6 +++--- gui/wxpython/photo2image/ip2i_mapdisplay.py | 2 +- gui/wxpython/psmap/frame.py | 2 +- gui/wxpython/rdigit/dialogs.py | 2 +- gui/wxpython/vdigit/wxdigit.py | 4 ++-- gui/wxpython/vnet/widgets.py | 2 +- lib/init/grass.py | 4 ++-- man/build_keywords.py | 2 +- pyproject.toml | 1 - python/grass/gunittest/case.py | 2 +- python/grass/pygrass/modules/interface/parameter.py | 2 +- python/grass/temporal/spatial_extent.py | 4 ++-- scripts/g.extension.all/g.extension.all.py | 2 +- .../testsuite/test_r_semantic_label.py | 2 +- utils/mkhtml.py | 2 +- 26 files changed, 44 insertions(+), 45 deletions(-) diff --git a/gui/wxpython/animation/dialogs.py b/gui/wxpython/animation/dialogs.py index 3d70dbd2639..428abdb2626 100644 --- a/gui/wxpython/animation/dialogs.py +++ b/gui/wxpython/animation/dialogs.py @@ -1572,7 +1572,7 @@ def _export_file_validation(self, filebrowsebtn, file_path, file_postfix): caption=_("Overwrite?"), style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION, ) - if not overwrite_dlg.ShowModal() == wx.ID_YES: + if overwrite_dlg.ShowModal() != wx.ID_YES: overwrite_dlg.Destroy() return False overwrite_dlg.Destroy() diff --git a/gui/wxpython/gcp/manager.py b/gui/wxpython/gcp/manager.py index e5e0dd37471..a96555fe271 100644 --- a/gui/wxpython/gcp/manager.py +++ b/gui/wxpython/gcp/manager.py @@ -1810,7 +1810,7 @@ def OnGeorect(self, event): overwrite=self.overwrite, ) if overwrite_dlg: - if not overwrite_dlg.ShowModal() == wx.ID_YES: + if overwrite_dlg.ShowModal() != wx.ID_YES: overwrite_dlg.Destroy() return overwrite_dlg.Destroy() @@ -1870,7 +1870,7 @@ def OnGeorect(self, event): overwrite=self.overwrite, ) if overwrite_dlg: - if not overwrite_dlg.ShowModal() == wx.ID_YES: + if overwrite_dlg.ShowModal() != wx.ID_YES: overwrite_dlg.Destroy() return overwrite_dlg.Destroy() @@ -2300,7 +2300,7 @@ def AdjustMap(self, newreg): def OnZoomToSource(self, event): """Set target map window to match extents of source map window""" - if not self.MapWindow == self.TgtMapWindow: + if self.MapWindow != self.TgtMapWindow: self.MapWindow = self.TgtMapWindow self.Map = self.TgtMap self.UpdateActive(self.TgtMapWindow) @@ -2313,7 +2313,7 @@ def OnZoomToSource(self, event): def OnZoomToTarget(self, event): """Set source map window to match extents of target map window""" - if not self.MapWindow == self.SrcMapWindow: + if self.MapWindow != self.SrcMapWindow: self.MapWindow = self.SrcMapWindow self.Map = self.SrcMap self.UpdateActive(self.SrcMapWindow) @@ -3323,7 +3323,7 @@ def OnSrcSelection(self, event): tmp_map = self.srcselection.GetValue() - if not tmp_map == "" and not tmp_map == src_map: + if tmp_map not in ("", src_map): self.new_src_map = tmp_map def OnTgtRastSelection(self, event): diff --git a/gui/wxpython/gcp/mapdisplay.py b/gui/wxpython/gcp/mapdisplay.py index 66daa70d2ca..0681b03e4e8 100644 --- a/gui/wxpython/gcp/mapdisplay.py +++ b/gui/wxpython/gcp/mapdisplay.py @@ -575,7 +575,7 @@ def GetMapToolbar(self): return self.toolbars["gcpdisp"] def _setActiveMapWindow(self, mapWindow): - if not self.MapWindow == mapWindow: + if self.MapWindow != mapWindow: self.MapWindow = mapWindow self.Map = mapWindow.Map self.UpdateActive(mapWindow) diff --git a/gui/wxpython/gui_core/vselect.py b/gui/wxpython/gui_core/vselect.py index 0ff30afaafd..16bb64b7a57 100644 --- a/gui/wxpython/gui_core/vselect.py +++ b/gui/wxpython/gui_core/vselect.py @@ -232,7 +232,7 @@ def AddVecInfo(self, vInfoDictTMP) -> bool: if self._dialog: self.slist.AddItem(vInfoDictTMP) - return not len(self.selectedFeatures) == 0 + return len(self.selectedFeatures) != 0 def _draw(self): """Call class 'VectorSelectHighlighter' to draw selected features""" diff --git a/gui/wxpython/iclass/frame.py b/gui/wxpython/iclass/frame.py index 69c18780249..606c364c76f 100644 --- a/gui/wxpython/iclass/frame.py +++ b/gui/wxpython/iclass/frame.py @@ -570,7 +570,7 @@ def OnZoomMenu(self, event): def OnZoomToTraining(self, event): """Set preview display to match extents of training display""" - if not self.MapWindow == self.GetSecondWindow(): + if self.MapWindow != self.GetSecondWindow(): self.MapWindow = self.GetSecondWindow() self.Map = self.GetSecondMap() self.UpdateActive(self.GetSecondWindow()) @@ -583,7 +583,7 @@ def OnZoomToTraining(self, event): def OnZoomToPreview(self, event): """Set preview display to match extents of training display""" - if not self.MapWindow == self.GetFirstWindow(): + if self.MapWindow != self.GetFirstWindow(): self.MapWindow = self.GetFirstWindow() self.Map = self.GetFirstMap() self.UpdateActive(self.GetFirstWindow()) diff --git a/gui/wxpython/image2target/ii2t_manager.py b/gui/wxpython/image2target/ii2t_manager.py index 17811ceceb1..1c9958c9cef 100644 --- a/gui/wxpython/image2target/ii2t_manager.py +++ b/gui/wxpython/image2target/ii2t_manager.py @@ -2245,7 +2245,7 @@ def AdjustMap(self, newreg): def OnZoomToSource(self, event): """Set target map window to match extents of source map window""" - if not self.MapWindow == self.TgtMapWindow: + if self.MapWindow != self.TgtMapWindow: self.MapWindow = self.TgtMapWindow self.Map = self.TgtMap self.UpdateActive(self.TgtMapWindow) @@ -2258,7 +2258,7 @@ def OnZoomToSource(self, event): def OnZoomToTarget(self, event): """Set source map window to match extents of target map window""" - if not self.MapWindow == self.SrcMapWindow: + if self.MapWindow != self.SrcMapWindow: self.MapWindow = self.SrcMapWindow self.Map = self.SrcMap self.UpdateActive(self.SrcMapWindow) @@ -3282,7 +3282,7 @@ def OnSrcSelection(self, event): tmp_map = self.srcselection.GetValue() - if not tmp_map == "" and not tmp_map == src_map: + if tmp_map not in ("", src_map): self.new_src_map = tmp_map def OnTgtRastSelection(self, event): diff --git a/gui/wxpython/image2target/ii2t_mapdisplay.py b/gui/wxpython/image2target/ii2t_mapdisplay.py index b85a70a152f..c148eb314d2 100644 --- a/gui/wxpython/image2target/ii2t_mapdisplay.py +++ b/gui/wxpython/image2target/ii2t_mapdisplay.py @@ -567,7 +567,7 @@ def GetMapToolbar(self): return self.toolbars["gcpdisp"] def _setActiveMapWindow(self, mapWindow): - if not self.MapWindow == mapWindow: + if self.MapWindow != mapWindow: self.MapWindow = mapWindow self.Map = mapWindow.Map self.UpdateActive(mapWindow) diff --git a/gui/wxpython/iscatt/plots.py b/gui/wxpython/iscatt/plots.py index 08afe5906ed..32ce111c428 100644 --- a/gui/wxpython/iscatt/plots.py +++ b/gui/wxpython/iscatt/plots.py @@ -168,7 +168,7 @@ def SetEmpty(self): return self.polygon_drawer.SetEmpty() def OnRelease(self, event): - if not self.mode == "zoom": + if self.mode != "zoom": return self.zoom_rect.set_visible(False) self.ZoomRectangle(event) @@ -348,7 +348,7 @@ def ZoomWheel(self, event): def ZoomRectangle(self, event): # get the current x and y limits - if not self.mode == "zoom": + if self.mode != "zoom": return if event.inaxes is None: return @@ -394,7 +394,7 @@ def OnCanvasLeave(self, event): def PanMotion(self, event): "on mouse movement" - if not self.mode == "pan": + if self.mode != "pan": return if event.inaxes is None: return @@ -426,7 +426,7 @@ def PanMotion(self, event): self.canvas.draw() def ZoomRectMotion(self, event): - if not self.mode == "zoom": + if self.mode != "zoom": return if event.inaxes is None: return @@ -866,7 +866,7 @@ def _addVertex(self, event): def motion_notify_callback(self, event): "on mouse movement" - if not self.mode == "move_vertex": + if self.mode != "move_vertex": return if not self.showverts: return diff --git a/gui/wxpython/mapdisp/frame.py b/gui/wxpython/mapdisp/frame.py index 0b948f63794..f13ffbe0a81 100644 --- a/gui/wxpython/mapdisp/frame.py +++ b/gui/wxpython/mapdisp/frame.py @@ -831,7 +831,7 @@ def _DToRastDone(): overwrite=overwrite, getErrorMsg=True, ) - if not returncode == 0: + if returncode != 0: self._giface.WriteError(_("Failed to run d.to.rast:\n") + messages) return # set region for composite @@ -839,7 +839,7 @@ def _DToRastDone(): returncode, messages = RunCommand( "g.region", raster=tmpName + ".red", quiet=True, getErrorMsg=True ) - if not returncode == 0: + if returncode != 0: gs.del_temp_region() self._giface.WriteError(_("Failed to run d.to.rast:\n") + messages) return @@ -862,7 +862,7 @@ def _DToRastDone(): quiet=True, name=[tmpName + ".red", tmpName + ".green", tmpName + ".blue"], ) - if not returncode == 0: + if returncode != 0: self._giface.WriteError(_("Failed to run d.to.rast:\n") + messages) gs.try_remove(pngFile) return diff --git a/gui/wxpython/nviz/mapwindow.py b/gui/wxpython/nviz/mapwindow.py index eaf0d266d4e..550939da7ce 100644 --- a/gui/wxpython/nviz/mapwindow.py +++ b/gui/wxpython/nviz/mapwindow.py @@ -534,7 +534,7 @@ def OnKeyDown(self, event): Used for fly-through mode. """ - if not self.mouse["use"] == "fly": + if self.mouse["use"] != "fly": return key = event.GetKeyCode() @@ -596,7 +596,7 @@ def OnKeyUp(self, event): Used for fly-through mode. """ - if not self.mouse["use"] == "fly": + if self.mouse["use"] != "fly": return key = event.GetKeyCode() diff --git a/gui/wxpython/nviz/wxnviz.py b/gui/wxpython/nviz/wxnviz.py index 8002d6f32d2..e80a9fc9382 100644 --- a/gui/wxpython/nviz/wxnviz.py +++ b/gui/wxpython/nviz/wxnviz.py @@ -83,7 +83,7 @@ def print_progress(value): """Redirect progress info""" global progress if progress: - if not progress.GetRange() == 100: + if progress.GetRange() != 100: progress.SetRange(100) progress.SetValue(value) else: diff --git a/gui/wxpython/photo2image/ip2i_manager.py b/gui/wxpython/photo2image/ip2i_manager.py index f251a1fa36e..e27e763fb2a 100644 --- a/gui/wxpython/photo2image/ip2i_manager.py +++ b/gui/wxpython/photo2image/ip2i_manager.py @@ -1531,7 +1531,7 @@ def AdjustMap(self, newreg): def OnZoomToSource(self, event): """Set target map window to match extents of source map window""" - if not self.MapWindow == self.TgtMapWindow: + if self.MapWindow != self.TgtMapWindow: self.MapWindow = self.TgtMapWindow self.Map = self.TgtMap self.UpdateActive(self.TgtMapWindow) @@ -1544,7 +1544,7 @@ def OnZoomToSource(self, event): def OnZoomToTarget(self, event): """Set source map window to match extents of target map window""" - if not self.MapWindow == self.SrcMapWindow: + if self.MapWindow != self.SrcMapWindow: self.MapWindow = self.SrcMapWindow self.Map = self.SrcMap self.UpdateActive(self.SrcMapWindow) @@ -2381,7 +2381,7 @@ def OnSrcSelection(self, event): tmp_map = self.srcselection.GetValue() - if not tmp_map == "" and not tmp_map == src_map: + if tmp_map not in ("", src_map): self.new_src_map = tmp_map def OnTgtRastSelection(self, event): diff --git a/gui/wxpython/photo2image/ip2i_mapdisplay.py b/gui/wxpython/photo2image/ip2i_mapdisplay.py index f85be35ec4a..aa085f2b43a 100644 --- a/gui/wxpython/photo2image/ip2i_mapdisplay.py +++ b/gui/wxpython/photo2image/ip2i_mapdisplay.py @@ -559,7 +559,7 @@ def GetMapToolbar(self): return self.toolbars["gcpdisp"] def _setActiveMapWindow(self, mapWindow): - if not self.MapWindow == mapWindow: + if self.MapWindow != mapWindow: self.MapWindow = mapWindow self.Map = mapWindow.Map self.UpdateActive(mapWindow) diff --git a/gui/wxpython/psmap/frame.py b/gui/wxpython/psmap/frame.py index 3effc33cd4d..fa4a150aa3b 100644 --- a/gui/wxpython/psmap/frame.py +++ b/gui/wxpython/psmap/frame.py @@ -308,7 +308,7 @@ def OnPsMapDialog(self, event): def OnPDFFile(self, event): """Generate PDF from PS with ps2pdf if available""" - if not sys.platform == "win32": + if sys.platform != "win32": try: p = gs.Popen(["ps2pdf"], stderr=gs.PIPE) p.stderr.close() diff --git a/gui/wxpython/rdigit/dialogs.py b/gui/wxpython/rdigit/dialogs.py index 650b75feb63..c632a84c0fe 100644 --- a/gui/wxpython/rdigit/dialogs.py +++ b/gui/wxpython/rdigit/dialogs.py @@ -114,7 +114,7 @@ def OnOK(self, event): caption=_("Overwrite?"), style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION, ) - if not dlgOverwrite.ShowModal() == wx.ID_YES: + if dlgOverwrite.ShowModal() != wx.ID_YES: dlgOverwrite.Destroy() return else: diff --git a/gui/wxpython/vdigit/wxdigit.py b/gui/wxpython/vdigit/wxdigit.py index ddcdc5a2755..154d5ab7c81 100644 --- a/gui/wxpython/vdigit/wxdigit.py +++ b/gui/wxpython/vdigit/wxdigit.py @@ -1084,7 +1084,7 @@ def EditLine(self, line, coords): # apply snapping (node or vertex) snap = self._getSnapMode() if snap != NO_SNAP: - modeSnap = not (snap == SNAP) + modeSnap = snap != SNAP Vedit_snap_line( self.poMapInfo, self.popoBgMapInfo, @@ -1889,7 +1889,7 @@ def _addFeature(self, ftype, coords, layer, cat, snap, threshold): if snap != NO_SNAP: # apply snapping (node or vertex) - modeSnap = not (snap == SNAP) + modeSnap = snap != SNAP Vedit_snap_line( self.poMapInfo, self.popoBgMapInfo, diff --git a/gui/wxpython/vnet/widgets.py b/gui/wxpython/vnet/widgets.py index 12477cc93ea..ed1f2aa514c 100644 --- a/gui/wxpython/vnet/widgets.py +++ b/gui/wxpython/vnet/widgets.py @@ -542,7 +542,7 @@ def IsShown(self, colName) -> bool: :return: False - if is not shown """ - return not self._getColumnNum(colName) == -1 + return self._getColumnNum(colName) != -1 class EditItem(wx.Dialog): diff --git a/lib/init/grass.py b/lib/init/grass.py index 40312471e44..f32f290d090 100755 --- a/lib/init/grass.py +++ b/lib/init/grass.py @@ -400,7 +400,7 @@ def create_grass_config_dir(): os.makedirs(directory) except OSError as e: # Can happen as a race condition - if not e.errno == errno.EEXIST or not os.path.isdir(directory): + if e.errno != errno.EEXIST or not os.path.isdir(directory): fatal( _( "Failed to create configuration directory '{}' with error: {}" @@ -1631,7 +1631,7 @@ def sh_like_startup(location, location_name, grass_env_file, sh): # save command history in mapset dir and remember more # bash history file handled in specific_addition - if not sh == "bash": + if sh != "bash": os.environ["HISTFILE"] = os.path.join(location, sh_history) # instead of changing $HOME, start bash with: diff --git a/man/build_keywords.py b/man/build_keywords.py index 6070fad1a28..b0dfe95e0e3 100644 --- a/man/build_keywords.py +++ b/man/build_keywords.py @@ -158,7 +158,7 @@ def get_module_man_html_file_path(module): for k in sorted(char_list.keys()): test_length += 1 # toc += '

  • %s
  • ' % (char_list[k], k) - if test_length % 4 == 0 and not test_length == all_keys: + if test_length % 4 == 0 and test_length != all_keys: toc += '\n%s, ' % (char_list[k], k) elif test_length % 4 == 0 and test_length == all_keys: toc += '\n%s' % (char_list[k], k) diff --git a/pyproject.toml b/pyproject.toml index 649e309a73f..fa607b9a2b1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -256,7 +256,6 @@ ignore = [ "SIM114", # if-with-same-arms "SIM116", # if-else-block-instead-of-dict-lookup "SIM118", # in-dict-keys - "SIM201", # negate-equal-op "SIM223", # expr-and-false "SIM401", # if-else-block-instead-of-dict-get "SLF001", # private-member-access diff --git a/python/grass/gunittest/case.py b/python/grass/gunittest/case.py index da2c7486d16..b5563e47e9e 100644 --- a/python/grass/gunittest/case.py +++ b/python/grass/gunittest/case.py @@ -695,7 +695,7 @@ def assertFileMd5(self, filename, md5, text=False, msg=None): actual = text_file_md5(filename) else: actual = file_md5(filename) - if not actual == md5: + if actual != md5: standardMsg = ( "File <{name}> does not have the right MD5 sum.\n" "Expected is <{expected}>," diff --git a/python/grass/pygrass/modules/interface/parameter.py b/python/grass/pygrass/modules/interface/parameter.py index 798efde56b7..3b58ea2b372 100644 --- a/python/grass/pygrass/modules/interface/parameter.py +++ b/python/grass/pygrass/modules/interface/parameter.py @@ -216,7 +216,7 @@ def __init__(self, xparameter=None, diz=None): # if "gisprompt" in diz and diz["gisprompt"]: self.typedesc = diz["gisprompt"].get("prompt", "") - self.input = not diz["gisprompt"]["age"] == "new" + self.input = diz["gisprompt"]["age"] != "new" else: self.input = True diff --git a/python/grass/temporal/spatial_extent.py b/python/grass/temporal/spatial_extent.py index 329a0a4a684..26498e0385e 100644 --- a/python/grass/temporal/spatial_extent.py +++ b/python/grass/temporal/spatial_extent.py @@ -875,7 +875,7 @@ def cover_2d(self, extent) -> bool: if eS > S and eS < N: edge_count += 1 - return not edge_count == 0 + return edge_count != 0 def cover(self, extent) -> bool: """Return True if this extent covers the provided spatial @@ -956,7 +956,7 @@ def cover(self, extent) -> bool: if eB > B and eB < T: edge_count += 1 - return not edge_count == 0 + return edge_count != 0 def covered_2d(self, extent): """Return True if this extent is covered by the provided spatial diff --git a/scripts/g.extension.all/g.extension.all.py b/scripts/g.extension.all/g.extension.all.py index 7f0b36f4dc6..43d50073178 100644 --- a/scripts/g.extension.all/g.extension.all.py +++ b/scripts/g.extension.all/g.extension.all.py @@ -104,7 +104,7 @@ def download_modules_xml_file(url, response_format, *args, **kwargs): try: response = urlopen(url, *args, **kwargs) - if not response.code == 200: + if response.code != 200: index = HTTP_STATUS_CODES.index(response.code) desc = HTTP_STATUS_CODES[index].description gs.fatal( diff --git a/scripts/r.semantic.label/testsuite/test_r_semantic_label.py b/scripts/r.semantic.label/testsuite/test_r_semantic_label.py index b7456e22633..990954dbb5f 100644 --- a/scripts/r.semantic.label/testsuite/test_r_semantic_label.py +++ b/scripts/r.semantic.label/testsuite/test_r_semantic_label.py @@ -27,7 +27,7 @@ def read_semantic_label(self): return rast.info.semantic_label def test_semantic_label_assign_not_current_mapset(self): - if not self.mapset == "PERMANENT": + if self.mapset != "PERMANENT": self.mapset.name = "PERMANENT" a_map = self.mapset.glist(type="raster")[0] module = SimpleModule( diff --git a/utils/mkhtml.py b/utils/mkhtml.py index d36ea78663d..54b2b80e87d 100644 --- a/utils/mkhtml.py +++ b/utils/mkhtml.py @@ -159,7 +159,7 @@ def download_git_commit(url, response_format, *args, **kwargs): """ try: response = urlopen(url, *args, **kwargs) - if not response.code == 200: + if response.code != 200: index = HTTP_STATUS_CODES.index(response.code) desc = HTTP_STATUS_CODES[index].description gs.fatal( From 5eca6d67a20d8b4d868952e57340f1beacf569d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Sat, 28 Sep 2024 15:11:39 -0400 Subject: [PATCH 302/471] style: Fix os-stat (PTH116) by replacing os.stat() calls with pathlib (#4412) * style: Fix os-stat (PTH116) by replacing os.stat() calls with pathlib Ruff rule: https://docs.astral.sh/ruff/rules/os-stat/ * Change remaining os.stat() usages * Fix Path join with a string --- gui/wxpython/core/watchdog.py | 5 ++++- gui/wxpython/gui_core/preferences.py | 5 +++-- imagery/i.gensig/testsuite/test_i_gensig.py | 3 ++- lib/imagery/testsuite/test_imagery_sigfile.py | 10 +++++----- lib/imagery/testsuite/test_imagery_sigsetfile.py | 8 ++++---- pyproject.toml | 1 - python/grass/app/data.py | 2 +- python/grass/grassdb/checks.py | 2 +- 8 files changed, 20 insertions(+), 16 deletions(-) diff --git a/gui/wxpython/core/watchdog.py b/gui/wxpython/core/watchdog.py index 652bc1d755d..591bb36b477 100644 --- a/gui/wxpython/core/watchdog.py +++ b/gui/wxpython/core/watchdog.py @@ -19,6 +19,9 @@ import os import time + +from pathlib import Path + import wx from wx.lib.newevent import NewEvent @@ -59,7 +62,7 @@ def on_modified(self, event): not event.is_directory and os.path.basename(event.src_path) == self.rcfile_name ): - timestamp = os.stat(event.src_path).st_mtime + timestamp = Path(event.src_path).stat().st_mtime if timestamp - self.modified_time < 0.5: return self.modified_time = timestamp diff --git a/gui/wxpython/gui_core/preferences.py b/gui/wxpython/gui_core/preferences.py index a35d902bc7f..724e4a5aec0 100644 --- a/gui/wxpython/gui_core/preferences.py +++ b/gui/wxpython/gui_core/preferences.py @@ -28,6 +28,8 @@ import os import sys +from pathlib import Path + try: import pwd @@ -2425,8 +2427,7 @@ def LoadData(self): for mapset in self.parent.all_mapsets_ordered: index = self.InsertItem(self.GetItemCount(), mapset) - mapsetPath = os.path.join(locationPath, mapset) - stat_info = os.stat(mapsetPath) + stat_info = Path(locationPath, mapset).stat() if havePwd: try: self.SetItem(index, 1, "%s" % pwd.getpwuid(stat_info.st_uid)[0]) diff --git a/imagery/i.gensig/testsuite/test_i_gensig.py b/imagery/i.gensig/testsuite/test_i_gensig.py index 0498cabaf79..d75e3da2af4 100644 --- a/imagery/i.gensig/testsuite/test_i_gensig.py +++ b/imagery/i.gensig/testsuite/test_i_gensig.py @@ -13,6 +13,7 @@ import stat import ctypes import shutil +from pathlib import Path from grass.pygrass import utils from grass.pygrass.gis import Mapset @@ -107,7 +108,7 @@ def test_creation(self): ) # File must be present - sig_stat = os.stat(f"{self.sig_dir1}/sig") + sig_stat = Path(self.sig_dir1, "sig").stat() self.assertTrue(stat.S_ISREG(sig_stat.st_mode)) # Compare values within sig file diff --git a/lib/imagery/testsuite/test_imagery_sigfile.py b/lib/imagery/testsuite/test_imagery_sigfile.py index 104cd5a59b4..8d0e288561d 100644 --- a/lib/imagery/testsuite/test_imagery_sigfile.py +++ b/lib/imagery/testsuite/test_imagery_sigfile.py @@ -9,10 +9,10 @@ for details """ -import os import stat import ctypes import shutil +from pathlib import Path from grass.gunittest.case import TestCase from grass.gunittest.main import test @@ -80,7 +80,7 @@ def test_roundtrip_signature_v1_norgb_one_label(self): # Write signatures to file p_new_sigfile = I_fopen_signature_file_new(self.sig_name) - sig_stat = os.stat(f"{self.sig_dir}/sig") + sig_stat = Path(self.sig_dir, "sig").stat() self.assertTrue(stat.S_ISREG(sig_stat.st_mode)) I_write_signatures(p_new_sigfile, ctypes.byref(So)) self.libc.fclose(p_new_sigfile) @@ -136,7 +136,7 @@ def test_broken_signature_v1_norgb(self): # Write signatures to file p_new_sigfile = I_fopen_signature_file_new(self.sig_name) - sig_stat = os.stat(f"{self.sig_dir}/sig") + sig_stat = Path(self.sig_dir, "sig").stat() self.assertTrue(stat.S_ISREG(sig_stat.st_mode)) I_write_signatures(p_new_sigfile, ctypes.byref(So)) self.libc.fclose(p_new_sigfile) @@ -188,7 +188,7 @@ def test_roundtrip_signature_v1_norgb_two_labelss(self): # Write signatures to file p_new_sigfile = I_fopen_signature_file_new(self.sig_name) - sig_stat = os.stat(f"{self.sig_dir}/sig") + sig_stat = Path(self.sig_dir, "sig").stat() self.assertTrue(stat.S_ISREG(sig_stat.st_mode)) I_write_signatures(p_new_sigfile, ctypes.byref(So)) self.libc.fclose(p_new_sigfile) @@ -277,7 +277,7 @@ def test_roundtrip_signature_v2_norgb_two_labels_oclass(self): # Write signatures to file p_new_sigfile = I_fopen_signature_file_new(self.sig_name) - sig_stat = os.stat(f"{self.sig_dir}/sig") + sig_stat = Path(self.sig_dir, "sig").stat() self.assertTrue(stat.S_ISREG(sig_stat.st_mode)) I_write_signatures(p_new_sigfile, ctypes.byref(So)) self.libc.fclose(p_new_sigfile) diff --git a/lib/imagery/testsuite/test_imagery_sigsetfile.py b/lib/imagery/testsuite/test_imagery_sigsetfile.py index b8bcb566520..d526e920885 100644 --- a/lib/imagery/testsuite/test_imagery_sigsetfile.py +++ b/lib/imagery/testsuite/test_imagery_sigsetfile.py @@ -9,10 +9,10 @@ for details """ -import os import stat import ctypes import shutil +from pathlib import Path from grass.gunittest.case import TestCase from grass.gunittest.main import test @@ -84,7 +84,7 @@ def test_roundtrip_sigset_v1_one_label(self): # Write signatures to file p_new_sigfile = I_fopen_sigset_file_new(self.sig_name) - sig_stat = os.stat(f"{self.sig_dir}/sig") + sig_stat = Path(self.sig_dir, "sig").stat() self.assertTrue(stat.S_ISREG(sig_stat.st_mode)) I_WriteSigSet(p_new_sigfile, ctypes.byref(So)) self.libc.fclose(p_new_sigfile) @@ -139,7 +139,7 @@ def test_read_fail_sigset_v1_one_label(self): # Write signatures to file p_new_sigfile = I_fopen_sigset_file_new(self.sig_name) - sig_stat = os.stat(f"{self.sig_dir}/sig") + sig_stat = Path(self.sig_dir, "sig").stat() self.assertTrue(stat.S_ISREG(sig_stat.st_mode)) I_WriteSigSet(p_new_sigfile, ctypes.byref(So)) self.libc.fclose(p_new_sigfile) @@ -183,7 +183,7 @@ def test_roundtrip_sigset_v1_two_labels(self): # Write signatures to file p_new_sigfile = I_fopen_sigset_file_new(self.sig_name) - sig_stat = os.stat(f"{self.sig_dir}/sig") + sig_stat = Path(self.sig_dir, "sig").stat() self.assertTrue(stat.S_ISREG(sig_stat.st_mode)) I_WriteSigSet(p_new_sigfile, ctypes.byref(So)) self.libc.fclose(p_new_sigfile) diff --git a/pyproject.toml b/pyproject.toml index fa607b9a2b1..d3aa0049c28 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -200,7 +200,6 @@ ignore = [ "PTH111", # os-path-expanduser "PTH112", # os-path-isdir "PTH113", # os-path-isfile - "PTH116", # os-stat "PTH117", # os-path-isabs "PTH118", # os-path-join "PTH119", # os-path-basename diff --git a/python/grass/app/data.py b/python/grass/app/data.py index 2853b573f9c..10997cd59e8 100644 --- a/python/grass/app/data.py +++ b/python/grass/app/data.py @@ -190,7 +190,7 @@ def lock_mapset(mapset_path, force_lock_removal, message_callback): raise MapsetLockingException(_("Path '{}' doesn't exist").format(mapset_path)) if not os.access(mapset_path, os.W_OK): error = _("Path '{}' not accessible.").format(mapset_path) - stat_info = os.stat(mapset_path) + stat_info = Path(mapset_path).stat() mapset_uid = stat_info.st_uid if mapset_uid != os.getuid(): error = "{error}\n{detail}".format( diff --git a/python/grass/grassdb/checks.py b/python/grass/grassdb/checks.py index 4de65a4cba3..6bf77a3bf65 100644 --- a/python/grass/grassdb/checks.py +++ b/python/grass/grassdb/checks.py @@ -113,7 +113,7 @@ def is_current_user_mapset_owner(mapset_path): # Mapset needs to be owned by user. if sys.platform == "win32": return True - stat_info = os.stat(mapset_path) + stat_info = Path(mapset_path).stat() mapset_uid = stat_info.st_uid return mapset_uid == os.getuid() From ead659fc4262a72ceeeca4a051d5288aa91f1214 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Sat, 28 Sep 2024 16:09:53 -0400 Subject: [PATCH 303/471] style: Fix os-getcwd (PTH109) by replacing `os.getcwd()` calls with `Path.cwd()` (#4413) --- gui/wxpython/animation/dialogs.py | 15 +++++++++------ gui/wxpython/datacatalog/catalog.py | 7 ++++++- gui/wxpython/gmodeler/panels.py | 8 +++++--- gui/wxpython/gui_core/forms.py | 7 ++++--- gui/wxpython/gui_core/gselect.py | 10 ++++++---- gui/wxpython/gui_core/pyedit.py | 4 ++-- gui/wxpython/image2target/ii2t_gis_set.py | 4 +++- gui/wxpython/lmgr/frame.py | 12 ++++++------ gui/wxpython/lmgr/workspace.py | 6 ++++-- gui/wxpython/location_wizard/wizard.py | 11 ++++++++--- gui/wxpython/main_window/frame.py | 12 ++++++------ gui/wxpython/modules/colorrules.py | 6 ++++-- gui/wxpython/modules/import_export.py | 4 +++- gui/wxpython/nviz/tools.py | 4 +++- gui/wxpython/psmap/dialogs.py | 3 ++- gui/wxpython/tplot/frame.py | 3 ++- gui/wxpython/wxplot/profile.py | 4 +++- lib/init/grass.py | 4 ++-- locale/grass_po_stats.py | 4 +++- pyproject.toml | 1 - python/grass/grassdb/checks.py | 2 +- python/grass/pygrass/tests/benchmark.py | 6 +++--- python/grass/script/core.py | 2 +- python/grass/script/utils.py | 12 +++++++----- python/grass/temporal/stds_export.py | 2 +- python/grass/temporal/stds_import.py | 2 +- scripts/r.pack/r.pack.py | 4 +++- scripts/v.pack/v.pack.py | 4 +++- utils/mkhtml.py | 2 +- 29 files changed, 102 insertions(+), 63 deletions(-) diff --git a/gui/wxpython/animation/dialogs.py b/gui/wxpython/animation/dialogs.py index 428abdb2626..f6e4db341c5 100644 --- a/gui/wxpython/animation/dialogs.py +++ b/gui/wxpython/animation/dialogs.py @@ -24,6 +24,9 @@ import wx import copy import datetime + +from pathlib import Path + import wx.lib.filebrowsebutton as filebrowse import wx.lib.scrolledpanel as SP import wx.lib.colourselect as csel @@ -488,7 +491,7 @@ def _create3DPanel(self, parent): labelText=_("Workspace file:"), dialogTitle=_("Choose workspace file to import 3D view parameters"), buttonText=_("Browse"), - startDirectory=os.getcwd(), + startDirectory=str(Path.cwd()), fileMode=0, fileMask="GRASS Workspace File (*.gxw)|*.gxw", ) @@ -1089,7 +1092,7 @@ def _createDecorationsProperties(self, panel): labelText=_("Image file:"), dialogTitle=_("Choose image file"), buttonText=_("Browse"), - startDirectory=os.getcwd(), + startDirectory=str(Path.cwd()), fileMode=wx.FD_OPEN, changeCallback=self.OnSetImage, ) @@ -1191,7 +1194,7 @@ def _createExportFormatPanel(self, notebook): labelText=_("Directory:"), dialogTitle=_("Choose directory for export"), buttonText=_("Browse"), - startDirectory=os.getcwd(), + startDirectory=str(Path.cwd()), ) dirGridSizer = wx.GridBagSizer(hgap=5, vgap=5) @@ -1219,7 +1222,7 @@ def _createExportFormatPanel(self, notebook): labelText=_("GIF file:"), dialogTitle=_("Choose file to save animation"), buttonText=_("Browse"), - startDirectory=os.getcwd(), + startDirectory=str(Path.cwd()), fileMode=wx.FD_SAVE, ) gifGridSizer = wx.GridBagSizer(hgap=5, vgap=5) @@ -1242,7 +1245,7 @@ def _createExportFormatPanel(self, notebook): labelText=_("SWF file:"), dialogTitle=_("Choose file to save animation"), buttonText=_("Browse"), - startDirectory=os.getcwd(), + startDirectory=str(Path.cwd()), fileMode=wx.FD_SAVE, ) swfGridSizer = wx.GridBagSizer(hgap=5, vgap=5) @@ -1273,7 +1276,7 @@ def _createExportFormatPanel(self, notebook): labelText=_("AVI file:"), dialogTitle=_("Choose file to save animation"), buttonText=_("Browse"), - startDirectory=os.getcwd(), + startDirectory=str(Path.cwd()), fileMode=wx.FD_SAVE, ) encodingLabel = StaticText( diff --git a/gui/wxpython/datacatalog/catalog.py b/gui/wxpython/datacatalog/catalog.py index 880190b0943..4835061d968 100644 --- a/gui/wxpython/datacatalog/catalog.py +++ b/gui/wxpython/datacatalog/catalog.py @@ -19,6 +19,8 @@ import wx import os +from pathlib import Path + from core.debug import Debug from datacatalog.tree import DataCatalogTree from datacatalog.toolbars import DataCatalogToolbar, DataCatalogSearch @@ -200,7 +202,10 @@ def OnReloadCurrentMapset(self, event): def OnAddGrassDB(self, event): """Add grass database""" dlg = wx.DirDialog( - self, _("Choose GRASS data directory:"), os.getcwd(), wx.DD_DEFAULT_STYLE + self, + _("Choose GRASS data directory:"), + str(Path.cwd()), + wx.DD_DEFAULT_STYLE, ) if dlg.ShowModal() == wx.ID_OK: grassdatabase = dlg.GetPath() diff --git a/gui/wxpython/gmodeler/panels.py b/gui/wxpython/gmodeler/panels.py index 93ef770efe1..416a9b85592 100644 --- a/gui/wxpython/gmodeler/panels.py +++ b/gui/wxpython/gmodeler/panels.py @@ -25,6 +25,8 @@ import random import math +from pathlib import Path + import wx from wx.lib import ogl @@ -834,7 +836,7 @@ def OnModelOpen(self, event): dlg = wx.FileDialog( parent=self, message=_("Choose model file"), - defaultDir=os.getcwd(), + defaultDir=str(Path.cwd()), wildcard=_("GRASS Model File (*.gxm)|*.gxm"), ) if dlg.ShowModal() == wx.ID_OK: @@ -892,7 +894,7 @@ def OnModelSaveAs(self, event=None): dlg = wx.FileDialog( parent=self, message=_("Choose file to save current model"), - defaultDir=os.getcwd(), + defaultDir=str(Path.cwd()), wildcard=_("GRASS Model File (*.gxm)|*.gxm"), style=wx.FD_SAVE, ) @@ -1740,7 +1742,7 @@ def SaveAs(self, force=False): parent=self, message=_("Choose file to save"), defaultFile=os.path.basename(self.parent.GetModelFile(ext=False)), - defaultDir=os.getcwd(), + defaultDir=str(Path.cwd()), wildcard=fn_wildcard, style=wx.FD_SAVE, ) diff --git a/gui/wxpython/gui_core/forms.py b/gui/wxpython/gui_core/forms.py index 84fd849908c..74bcc00f85a 100644 --- a/gui/wxpython/gui_core/forms.py +++ b/gui/wxpython/gui_core/forms.py @@ -58,6 +58,7 @@ import codecs from threading import Thread +from pathlib import Path import wx @@ -2034,7 +2035,7 @@ def __init__(self, parent, giface, task, id=wx.ID_ANY, frame=None, *args, **kwar dialogTitle=_("Choose %s") % p.get("description", _("file")).lower(), buttonText=_("Browse"), - startDirectory=os.getcwd(), + startDirectory=str(Path.cwd()), fileMode=fmode, changeCallback=self.OnSetValue, ) @@ -2145,7 +2146,7 @@ def __init__(self, parent, giface, task, id=wx.ID_ANY, frame=None, *args, **kwar dialogTitle=_("Choose %s") % p.get("description", _("Directory")), buttonText=_("Browse"), - startDirectory=os.getcwd(), + startDirectory=str(Path.cwd()), newDirectory=True, changeCallback=self.OnSetValue, ) @@ -2624,7 +2625,7 @@ def OnFileSave(self, event): dlg = wx.FileDialog( parent=self, message=_("Save input as..."), - defaultDir=os.getcwd(), + defaultDir=str(Path.cwd()), style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT, ) diff --git a/gui/wxpython/gui_core/gselect.py b/gui/wxpython/gui_core/gselect.py index 4bb9af5b50e..af71cc4f550 100644 --- a/gui/wxpython/gui_core/gselect.py +++ b/gui/wxpython/gui_core/gselect.py @@ -47,6 +47,8 @@ import glob import ctypes +from pathlib import Path + import wx from core import globalvar @@ -1558,7 +1560,7 @@ def __init__( labelText=_("File:"), dialogTitle=_("Choose file to import"), buttonText=_("Browse"), - startDirectory=os.getcwd(), + startDirectory=str(Path.cwd()), changeCallback=self.OnUpdate, fileMask=fileMask, ) @@ -1575,7 +1577,7 @@ def __init__( labelText=_("Directory:"), dialogTitle=_("Choose input directory"), buttonText=_("Browse"), - startDirectory=os.getcwd(), + startDirectory=str(Path.cwd()), changeCallback=self.OnUpdate, ) browse.GetChildren()[1].SetName("GdalSelectDataSource") @@ -1628,7 +1630,7 @@ def __init__( labelText=_("Name:"), dialogTitle=_("Choose file"), buttonText=_("Browse"), - startDirectory=os.getcwd(), + startDirectory=str(Path.cwd()), changeCallback=self.OnUpdate, ) browse.GetChildren()[1].SetName("GdalSelectDataSource") @@ -1663,7 +1665,7 @@ def __init__( labelText=_("Directory:"), dialogTitle=_("Choose input directory"), buttonText=_("Browse"), - startDirectory=os.getcwd(), + startDirectory=str(Path.cwd()), changeCallback=self.OnUpdate, ) self.dbWidgets["dirbrowse"] = browse diff --git a/gui/wxpython/gui_core/pyedit.py b/gui/wxpython/gui_core/pyedit.py index 2de2d45b066..5958ffb6d68 100644 --- a/gui/wxpython/gui_core/pyedit.py +++ b/gui/wxpython/gui_core/pyedit.py @@ -395,7 +395,7 @@ def SaveAs(self): dlg = wx.FileDialog( parent=self.guiparent, message=_("Choose file to save"), - defaultDir=os.getcwd(), + defaultDir=str(Path.cwd()), wildcard=_("Python script (*.py)|*.py"), style=wx.FD_SAVE, ) @@ -466,7 +466,7 @@ def Open(self): dlg = wx.FileDialog( parent=self.guiparent, message=_("Open file"), - defaultDir=os.getcwd(), + defaultDir=str(Path.cwd()), wildcard=_("Python script (*.py)|*.py"), style=wx.FD_OPEN, ) diff --git a/gui/wxpython/image2target/ii2t_gis_set.py b/gui/wxpython/image2target/ii2t_gis_set.py index 0f51d969e93..187b01d1740 100644 --- a/gui/wxpython/image2target/ii2t_gis_set.py +++ b/gui/wxpython/image2target/ii2t_gis_set.py @@ -27,6 +27,8 @@ import platform import getpass +from pathlib import Path + from core import globalvar import wx import wx.lib.mixins.listctrl as listmix @@ -315,7 +317,7 @@ def _set_properties(self, version, revision): if os.path.isdir(os.getenv("HOME")): self.gisdbase = os.getenv("HOME") else: - self.gisdbase = os.getcwd() + self.gisdbase = str(Path.cwd()) try: self.tgisdbase.SetValue(self.gisdbase) except UnicodeDecodeError: diff --git a/gui/wxpython/lmgr/frame.py b/gui/wxpython/lmgr/frame.py index a17b168f747..4eae371f68b 100644 --- a/gui/wxpython/lmgr/frame.py +++ b/gui/wxpython/lmgr/frame.py @@ -24,6 +24,8 @@ import platform import re +from pathlib import Path + from core import globalvar import wx import wx.aui @@ -848,7 +850,7 @@ def OnRunModel(self, event): dlg = wx.FileDialog( parent=self, message=_("Choose model to run"), - defaultDir=os.getcwd(), + defaultDir=str(Path.cwd()), wildcard=_("GRASS Model File (*.gxm)|*.gxm"), ) if dlg.ShowModal() == wx.ID_OK: @@ -1223,7 +1225,7 @@ def OnRunScript(self, event): dlg = wx.FileDialog( parent=self, message=_("Choose script file to run"), - defaultDir=os.getcwd(), + defaultDir=str(Path.cwd()), wildcard=_("Python script (*.py)|*.py|Bash script (*.sh)|*.sh"), ) @@ -1377,9 +1379,7 @@ def write_beginning(parameter=None, command=None): self._giface.WriteCmdLog(" ".join(command)) def write_changed(): - self._giface.WriteLog( - _('Working directory changed to:\n"%s"') % os.getcwd() - ) + self._giface.WriteLog(_('Working directory changed to:\n"%s"') % Path.cwd()) def write_end(): self._giface.WriteCmdLog(" ") @@ -1433,7 +1433,7 @@ def write_help(): dlg = wx.DirDialog( parent=self, message=_("Choose a working directory"), - defaultPath=os.getcwd(), + defaultPath=str(Path.cwd()), ) if dlg.ShowModal() == wx.ID_OK: diff --git a/gui/wxpython/lmgr/workspace.py b/gui/wxpython/lmgr/workspace.py index bb40469fa56..a593c19b19c 100644 --- a/gui/wxpython/lmgr/workspace.py +++ b/gui/wxpython/lmgr/workspace.py @@ -18,6 +18,8 @@ import xml.etree.ElementTree as ET +from pathlib import Path + import wx import wx.aui @@ -103,7 +105,7 @@ def Open(self): dlg = wx.FileDialog( parent=self.lmgr, message=_("Choose workspace file"), - defaultDir=os.getcwd(), + defaultDir=str(Path.cwd()), wildcard=_("GRASS Workspace File (*.gxw)|*.gxw"), ) @@ -362,7 +364,7 @@ def SaveAs(self): dlg = wx.FileDialog( parent=self.lmgr, message=_("Choose file to save current workspace"), - defaultDir=os.getcwd(), + defaultDir=str(Path.cwd()), wildcard=_("GRASS Workspace File (*.gxw)|*.gxw"), style=wx.FD_SAVE, ) diff --git a/gui/wxpython/location_wizard/wizard.py b/gui/wxpython/location_wizard/wizard.py index 7ba03ebee04..2cccdf5e301 100644 --- a/gui/wxpython/location_wizard/wizard.py +++ b/gui/wxpython/location_wizard/wizard.py @@ -38,6 +38,8 @@ import locale import functools +from pathlib import Path + import wx import wx.lib.mixins.listctrl as listmix from core import globalvar @@ -318,7 +320,10 @@ def OnChangeName(self, event): def OnBrowse(self, event): """Choose GRASS data directory""" dlg = wx.DirDialog( - self, _("Choose GRASS data directory:"), os.getcwd(), wx.DD_DEFAULT_STYLE + self, + _("Choose GRASS data directory:"), + str(Path.cwd()), + wx.DD_DEFAULT_STYLE, ) if dlg.ShowModal() == wx.ID_OK: self.grassdatabase = dlg.GetPath() @@ -1493,7 +1498,7 @@ def OnText(self, event): def OnBrowse(self, event): """Choose file""" dlg = wx.FileDialog( - self, _("Select georeferenced file"), os.getcwd(), "", "*.*", wx.FD_OPEN + self, _("Select georeferenced file"), str(Path.cwd()), "", "*.*", wx.FD_OPEN ) if dlg.ShowModal() == wx.ID_OK: path = dlg.GetPath() @@ -1977,7 +1982,7 @@ def OnBrowse(self, event): """Define path for IAU code file""" path = os.path.dirname(self.tfile.GetValue()) if not path: - path = os.getcwd() + path = str(Path.cwd()) dlg = wx.FileDialog( parent=self, diff --git a/gui/wxpython/main_window/frame.py b/gui/wxpython/main_window/frame.py index 3b9b7f50261..8fc8dc8dde7 100644 --- a/gui/wxpython/main_window/frame.py +++ b/gui/wxpython/main_window/frame.py @@ -25,6 +25,8 @@ import platform import re +from pathlib import Path + from core import globalvar try: @@ -971,7 +973,7 @@ def OnRunModel(self, event): dlg = wx.FileDialog( parent=self, message=_("Choose model to run"), - defaultDir=os.getcwd(), + defaultDir=str(Path.cwd()), wildcard=_("GRASS Model File (*.gxm)|*.gxm"), ) if dlg.ShowModal() == wx.ID_OK: @@ -1374,7 +1376,7 @@ def OnRunScript(self, event): dlg = wx.FileDialog( parent=self, message=_("Choose script file to run"), - defaultDir=os.getcwd(), + defaultDir=str(Path.cwd()), wildcard=_("Python script (*.py)|*.py|Bash script (*.sh)|*.sh"), ) @@ -1528,9 +1530,7 @@ def write_beginning(parameter=None, command=None): self._giface.WriteCmdLog(" ".join(command)) def write_changed(): - self._giface.WriteLog( - _('Working directory changed to:\n"%s"') % os.getcwd() - ) + self._giface.WriteLog(_('Working directory changed to:\n"%s"') % Path.cwd()) def write_end(): self._giface.WriteCmdLog(" ") @@ -1584,7 +1584,7 @@ def write_help(): dlg = wx.DirDialog( parent=self, message=_("Choose a working directory"), - defaultPath=os.getcwd(), + defaultPath=str(Path.cwd()), ) if dlg.ShowModal() == wx.ID_OK: diff --git a/gui/wxpython/modules/colorrules.py b/gui/wxpython/modules/colorrules.py index b5b1047382e..5093cd7cf25 100644 --- a/gui/wxpython/modules/colorrules.py +++ b/gui/wxpython/modules/colorrules.py @@ -27,6 +27,8 @@ import copy import tempfile +from pathlib import Path + import wx import wx.lib.colourselect as csel import wx.lib.scrolledpanel as scrolled @@ -448,7 +450,7 @@ def _createFileSelection(self, parent): dialogTitle=_("Choose file to load color table"), buttonText=_("Load"), toolTip=_("Type filename or click to choose file and load color table"), - startDirectory=os.getcwd(), + startDirectory=str(Path.cwd()), fileMode=wx.FD_OPEN, changeCallback=self.OnLoadRulesFile, ) @@ -460,7 +462,7 @@ def _createFileSelection(self, parent): dialogTitle=_("Choose file to save color table"), toolTip=_("Type filename or click to choose file and save color table"), buttonText=_("Save"), - startDirectory=os.getcwd(), + startDirectory=str(Path.cwd()), fileMode=wx.FD_SAVE, changeCallback=self.OnSaveRulesFile, ) diff --git a/gui/wxpython/modules/import_export.py b/gui/wxpython/modules/import_export.py index 15d4f76b586..03d179ffc2f 100644 --- a/gui/wxpython/modules/import_export.py +++ b/gui/wxpython/modules/import_export.py @@ -22,6 +22,8 @@ import os +from pathlib import Path + import wx from core import globalvar import wx.lib.filebrowsebutton as filebrowse @@ -836,7 +838,7 @@ def __init__(self, parent, giface): labelText="", dialogTitle=_("Choose DXF file to import"), buttonText=_("Browse"), - startDirectory=os.getcwd(), + startDirectory=str(Path.cwd()), fileMode=0, changeCallback=self.OnSetDsn, fileMask="DXF File (*.dxf)|*.dxf", diff --git a/gui/wxpython/nviz/tools.py b/gui/wxpython/nviz/tools.py index a1b0a9fcb1b..0659c5cc2ba 100644 --- a/gui/wxpython/nviz/tools.py +++ b/gui/wxpython/nviz/tools.py @@ -23,6 +23,8 @@ import sys import copy +from pathlib import Path + import wx import wx.lib.colourselect as csel import wx.lib.scrolledpanel as SP @@ -668,7 +670,7 @@ def _createAnimationPage(self): vSizer = wx.BoxSizer(wx.VERTICAL) gridSizer = wx.GridBagSizer(vgap=5, hgap=10) - pwd = os.getcwd() + pwd = str(Path.cwd()) dir = filebrowse.DirBrowseButton( parent=panel, id=wx.ID_ANY, diff --git a/gui/wxpython/psmap/dialogs.py b/gui/wxpython/psmap/dialogs.py index 216782037be..4eea798f731 100644 --- a/gui/wxpython/psmap/dialogs.py +++ b/gui/wxpython/psmap/dialogs.py @@ -37,6 +37,7 @@ import os import string from copy import deepcopy +from pathlib import Path import wx import wx.lib.agw.floatspin as fs @@ -5965,7 +5966,7 @@ def OnPositionType(self, event): def _getImageDirectory(self): """Default image directory""" - return os.getcwd() + return str(Path.cwd()) def _addConvergence(self, panel, gridBagSizer): pass diff --git a/gui/wxpython/tplot/frame.py b/gui/wxpython/tplot/frame.py index db21da4cd14..2c7d49c3d72 100755 --- a/gui/wxpython/tplot/frame.py +++ b/gui/wxpython/tplot/frame.py @@ -20,6 +20,7 @@ """ import os from itertools import cycle +from pathlib import Path import numpy as np import wx @@ -371,7 +372,7 @@ def _layout(self): labelText="", dialogTitle=_("CVS path"), buttonText=_("Browse"), - startDirectory=os.getcwd(), + startDirectory=str(Path.cwd()), fileMode=wx.FD_SAVE, ) self.headerLabel = StaticText( diff --git a/gui/wxpython/wxplot/profile.py b/gui/wxpython/wxplot/profile.py index 1d3de6681af..61efdcd67d0 100644 --- a/gui/wxpython/wxplot/profile.py +++ b/gui/wxpython/wxplot/profile.py @@ -20,6 +20,8 @@ import math import numpy as np +from pathlib import Path + import wx from wx.lib import plot @@ -410,7 +412,7 @@ def SaveProfileToFile(self, event): dlg = wx.FileDialog( parent=self, message=_("Choose prefix for file(s) where to save profile values..."), - defaultDir=os.getcwd(), + defaultDir=str(Path.cwd()), wildcard=_("Comma separated value (*.csv)|*.csv"), style=wx.FD_SAVE, ) diff --git a/lib/init/grass.py b/lib/init/grass.py index f32f290d090..25486d9426c 100755 --- a/lib/init/grass.py +++ b/lib/init/grass.py @@ -620,7 +620,7 @@ def create_initial_gisrc(filename): LOCATION_NAME: MAPSET: """ - % os.getcwd() + % Path.cwd() ) writefile(filename, s) @@ -789,7 +789,7 @@ def set_mapset( # non-empty element as the last element (which is good for both mapset # and location split) if arg == ".": - arg = os.getcwd() + arg = str(Path.cwd()) elif not os.path.isabs(arg): arg = os.path.abspath(arg) if arg.endswith(os.path.sep): diff --git a/locale/grass_po_stats.py b/locale/grass_po_stats.py index 32e3f449cb3..0c49e44bc8d 100644 --- a/locale/grass_po_stats.py +++ b/locale/grass_po_stats.py @@ -21,10 +21,12 @@ import subprocess import sys +from pathlib import Path + def read_po_files(inputdirpath): """Return a dictionary with for each language the list of *.po files""" - originalpath = os.getcwd() + originalpath = Path.cwd() os.chdir(inputdirpath) languages = {} for pofile in sorted(glob.glob("*.po")): diff --git a/pyproject.toml b/pyproject.toml index d3aa0049c28..2a3a4f68890 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -195,7 +195,6 @@ ignore = [ "PTH106", # os-rmdir "PTH107", # os-remove "PTH108", # os-unlink - "PTH109", # os-getcwd "PTH110", # os-path-exists "PTH111", # os-path-expanduser "PTH112", # os-path-isdir diff --git a/python/grass/grassdb/checks.py b/python/grass/grassdb/checks.py index 6bf77a3bf65..6916ea2748a 100644 --- a/python/grass/grassdb/checks.py +++ b/python/grass/grassdb/checks.py @@ -159,7 +159,7 @@ def is_first_time_user(): genv = gisenv() if "LAST_MAPSET_PATH" in genv.keys(): return genv["LAST_MAPSET_PATH"] == os.path.join( - os.getcwd(), cfg.unknown_location, cfg.unknown_mapset + Path.cwd(), cfg.unknown_location, cfg.unknown_mapset ) return False diff --git a/python/grass/pygrass/tests/benchmark.py b/python/grass/pygrass/tests/benchmark.py index 75e32541a59..fa7f0c01a65 100644 --- a/python/grass/pygrass/tests/benchmark.py +++ b/python/grass/pygrass/tests/benchmark.py @@ -12,11 +12,11 @@ import copy import cProfile import sys -import os from jinja2 import Template +from pathlib import Path -sys.path.append(os.getcwd()) -sys.path.append("%s/.." % (os.getcwd())) +sys.path.append(str(Path.cwd())) +sys.path.append("%s/.." % (str(Path.cwd()))) import grass.lib.gis as libgis import grass.lib.raster as libraster diff --git a/python/grass/script/core.py b/python/grass/script/core.py index a86738926f0..40709a11e80 100644 --- a/python/grass/script/core.py +++ b/python/grass/script/core.py @@ -1894,7 +1894,7 @@ def _create_location_xy(database, location): :param database: GRASS database where to create new location :param location: location name """ - cur_dir = os.getcwd() + cur_dir = Path.cwd() try: os.chdir(database) os.mkdir(location) diff --git a/python/grass/script/utils.py b/python/grass/script/utils.py index 13d049412a4..3df6abc7016 100644 --- a/python/grass/script/utils.py +++ b/python/grass/script/utils.py @@ -28,6 +28,8 @@ import random import string +from pathlib import Path + def float_or_dms(s): """Convert DMS to float. @@ -383,7 +385,7 @@ def get_lib_path(modname, libname=None): path = join(os.getenv("GRASS_ADDON_BASE"), modname, modname) else: # used by g.extension compilation process - cwd = os.getcwd() + cwd = str(Path.cwd()) idx = cwd.find(modname) if idx < 0: return None @@ -463,10 +465,10 @@ def set_path(modulename, dirname=None, path="."): import sys # TODO: why dirname is checked first - the logic should be revised - pathlib = None + _pathlib = None if dirname: - pathlib = os.path.join(path, dirname) - if pathlib and os.path.exists(pathlib): + _pathlib = os.path.join(path, dirname) + if _pathlib and os.path.exists(_pathlib): # we are running the script from the script directory, therefore # we add the path to sys.path to reach the directory (dirname) sys.path.append(os.path.abspath(path)) @@ -477,7 +479,7 @@ def set_path(modulename, dirname=None, path="."): pathname = os.path.join(modulename, dirname) if dirname else modulename raise ImportError( "Not able to find the path '%s' directory " - "(current dir '%s')." % (pathname, os.getcwd()) + "(current dir '%s')." % (pathname, Path.cwd()) ) sys.path.insert(0, path) diff --git a/python/grass/temporal/stds_export.py b/python/grass/temporal/stds_export.py index ff2731b102a..a01b94e1071 100644 --- a/python/grass/temporal/stds_export.py +++ b/python/grass/temporal/stds_export.py @@ -348,7 +348,7 @@ def export_stds( """ # Save current working directory path - old_cwd = os.getcwd() + old_cwd = Path.cwd() # Create the temporary directory and jump into it new_cwd = tempfile.mkdtemp(dir=directory) diff --git a/python/grass/temporal/stds_import.py b/python/grass/temporal/stds_import.py index ae587cadb0f..b8223c8ee83 100644 --- a/python/grass/temporal/stds_import.py +++ b/python/grass/temporal/stds_import.py @@ -286,7 +286,7 @@ def import_stds( # We use a new list file name for map registration new_list_file_name = list_file_name + "_new" # Save current working directory path - old_cwd = os.getcwd() + old_cwd = Path.cwd() # Switch into the data directory os.chdir(directory) diff --git a/scripts/r.pack/r.pack.py b/scripts/r.pack/r.pack.py index eb11547322c..bb3332101ee 100644 --- a/scripts/r.pack/r.pack.py +++ b/scripts/r.pack/r.pack.py @@ -37,6 +37,8 @@ import atexit import tarfile +from pathlib import Path + from grass.script.utils import try_rmdir, try_remove from grass.script import core as grass @@ -80,7 +82,7 @@ def main(): grass.message(_("Packing <%s> to <%s>...") % (gfile["fullname"], outfile)) basedir = os.path.sep.join(os.path.normpath(gfile["file"]).split(os.path.sep)[:-2]) - olddir = os.getcwd() + olddir = Path.cwd() # copy elements info = grass.parse_command("r.info", flags="e", map=infile) diff --git a/scripts/v.pack/v.pack.py b/scripts/v.pack/v.pack.py index cd8d96b86cd..4468c888bcb 100755 --- a/scripts/v.pack/v.pack.py +++ b/scripts/v.pack/v.pack.py @@ -38,6 +38,8 @@ import tarfile import atexit +from pathlib import Path + from grass.script.utils import try_rmdir, try_remove from grass.script import core as grass from grass.script import vector @@ -128,7 +130,7 @@ def main(): tar.add(path, "PROJ_" + support) tar.close() - grass.message(_("Pack file <%s> created") % os.path.join(os.getcwd(), outfile)) + grass.message(_("Pack file <%s> created") % Path(outfile).resolve()) if __name__ == "__main__": diff --git a/utils/mkhtml.py b/utils/mkhtml.py index 54b2b80e87d..f64f6d7d97a 100644 --- a/utils/mkhtml.py +++ b/utils/mkhtml.py @@ -366,7 +366,7 @@ def has_src_code_git(src_dir, is_addon): if core module or addon source code has Git """ - actual_dir = os.getcwd() + actual_dir = Path.cwd() if is_addon: os.chdir(src_dir) else: From 70f35998766be162c7bc1e280c83997bd1c3e052 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Sat, 28 Sep 2024 16:31:05 -0400 Subject: [PATCH 304/471] style: Fix if-with-same-arms (SIM114) (#4406) --- gui/wxpython/animation/controller.py | 4 +--- gui/wxpython/core/gcmd.py | 10 +++++----- gui/wxpython/iscatt/plots.py | 10 +++++----- gui/wxpython/mapwin/decorations.py | 14 ++++++-------- gui/wxpython/vdigit/wxdisplay.py | 5 +---- gui/wxpython/vnet/dialogs.py | 4 +--- gui/wxpython/vnet/vnet_core.py | 4 +--- pyproject.toml | 1 - python/grass/script/utils.py | 7 +++---- python/grass/temporal/temporal_algebra.py | 20 +++++--------------- utils/gitlog2changelog.py | 14 ++++++-------- 11 files changed, 34 insertions(+), 59 deletions(-) diff --git a/gui/wxpython/animation/controller.py b/gui/wxpython/animation/controller.py index 1a920f77de7..1a8cb1fe9cc 100644 --- a/gui/wxpython/animation/controller.py +++ b/gui/wxpython/animation/controller.py @@ -465,9 +465,7 @@ def EvaluateInput(self, animationData): mapCount.add(len(layer.maps)) windowIndex.append(anim.windowIndex) - if maps and stds: - temporalMode = TemporalMode.NONTEMPORAL - elif maps: + if (maps and stds) or maps: temporalMode = TemporalMode.NONTEMPORAL elif stds: temporalMode = TemporalMode.TEMPORAL diff --git a/gui/wxpython/core/gcmd.py b/gui/wxpython/core/gcmd.py index b93789f22f7..eb40ec57103 100644 --- a/gui/wxpython/core/gcmd.py +++ b/gui/wxpython/core/gcmd.py @@ -645,11 +645,11 @@ def _formatMsg(text): for line in text.splitlines(): if len(line) == 0: continue - elif "GRASS_INFO_MESSAGE" in line: - message += line.split(":", 1)[1].strip() + "\n" - elif "GRASS_INFO_WARNING" in line: - message += line.split(":", 1)[1].strip() + "\n" - elif "GRASS_INFO_ERROR" in line: + elif ( + "GRASS_INFO_MESSAGE" in line + or "GRASS_INFO_WARNING" in line + or "GRASS_INFO_ERROR" in line + ): message += line.split(":", 1)[1].strip() + "\n" elif "GRASS_INFO_END" in line: return message diff --git a/gui/wxpython/iscatt/plots.py b/gui/wxpython/iscatt/plots.py index 32ce111c428..a29098e8bcd 100644 --- a/gui/wxpython/iscatt/plots.py +++ b/gui/wxpython/iscatt/plots.py @@ -811,11 +811,11 @@ def _deleteVertex(self, event): coords = [] for i, tup in enumerate(self.pol.xy): - if i == ind: - continue - elif i == 0 and ind == len(self.pol.xy) - 1: - continue - elif i == len(self.pol.xy) - 1 and ind == 0: + if ( + i == ind + or (i == 0 and ind == len(self.pol.xy) - 1) + or (i == len(self.pol.xy) - 1 and ind == 0) + ): continue coords.append(tup) diff --git a/gui/wxpython/mapwin/decorations.py b/gui/wxpython/mapwin/decorations.py index 7ab010a1d3f..24db63d8492 100644 --- a/gui/wxpython/mapwin/decorations.py +++ b/gui/wxpython/mapwin/decorations.py @@ -209,9 +209,7 @@ def CmdIsValid(self) -> bool: inputs = 0 for param in self._cmd[1:]: param = param.split("=") - if len(param) == 1: - inputs += 1 - elif param[0] == "text" and len(param) == 2: + if len(param) == 1 or (param[0] == "text" and len(param) == 2): inputs += 1 return inputs >= 1 @@ -313,11 +311,11 @@ def CmdIsValid(self) -> bool: inputs = 0 for param in self._cmd[1:]: param = param.split("=") - if len(param) == 1: - inputs += 1 - elif param[0] == "raster" and len(param) == 2: - inputs += 1 - elif param[0] == "raster_3d" and len(param) == 2: + if ( + len(param) == 1 + or (param[0] == "raster" and len(param) == 2) + or (param[0] == "raster_3d" and len(param) == 2) + ): inputs += 1 return inputs == 1 diff --git a/gui/wxpython/vdigit/wxdisplay.py b/gui/wxpython/vdigit/wxdisplay.py index b747f3fa44f..9085aae8caf 100644 --- a/gui/wxpython/vdigit/wxdisplay.py +++ b/gui/wxpython/vdigit/wxdisplay.py @@ -868,10 +868,7 @@ def GetSelectedVertex(self, pos): pos[0], pos[1], 0.0, points.x[idx], points.y[idx], points.z[idx], 0 ) - if idx == 0: - minDist = dist - Gid = idx - elif minDist > dist: + if idx == 0 or minDist > dist: minDist = dist Gid = idx diff --git a/gui/wxpython/vnet/dialogs.py b/gui/wxpython/vnet/dialogs.py index 3d829ffd7be..a445b01e8eb 100644 --- a/gui/wxpython/vnet/dialogs.py +++ b/gui/wxpython/vnet/dialogs.py @@ -1952,9 +1952,7 @@ def SetVirtualData(self, row, column, text): text = DegreesToRadians(text) # Tested allowed range of values - if text > math.pi: - text = 0.0 - elif text < -math.pi: + if text > math.pi or text < -math.pi: text = 0.0 self.data.SetValue(text, row, column) diff --git a/gui/wxpython/vnet/vnet_core.py b/gui/wxpython/vnet/vnet_core.py index 17d85ef6ca4..ac7625a4c22 100644 --- a/gui/wxpython/vnet/vnet_core.py +++ b/gui/wxpython/vnet/vnet_core.py @@ -828,9 +828,7 @@ def _prepareCmd(self, cmd): if c.find("=") == -1: continue v = c.split("=") - if len(v) != 2: - cmd.remove(c) - elif not v[1].strip(): + if len(v) != 2 or not v[1].strip(): cmd.remove(c) def _setCmdForSpecificAn(self, cmdParams): diff --git a/pyproject.toml b/pyproject.toml index 2a3a4f68890..31ac12eaf24 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -251,7 +251,6 @@ ignore = [ "SIM109", # compare-with-tuple "SIM110", # reimplemented-builtin "SIM113", # enumerate-for-loop - "SIM114", # if-with-same-arms "SIM116", # if-else-block-instead-of-dict-lookup "SIM118", # in-dict-keys "SIM223", # expr-and-false diff --git a/python/grass/script/utils.py b/python/grass/script/utils.py index 3df6abc7016..2868e3111e6 100644 --- a/python/grass/script/utils.py +++ b/python/grass/script/utils.py @@ -373,10 +373,9 @@ def get_lib_path(modname, libname=None): getenv("GRASS_ADDON_BASE") and libname and isdir(join(getenv("GRASS_ADDON_BASE"), "etc", modname, libname)) - ): - path = join(getenv("GRASS_ADDON_BASE"), "etc", modname) - elif getenv("GRASS_ADDON_BASE") and isdir( - join(getenv("GRASS_ADDON_BASE"), "etc", modname) + ) or ( + getenv("GRASS_ADDON_BASE") + and isdir(join(getenv("GRASS_ADDON_BASE"), "etc", modname)) ): path = join(getenv("GRASS_ADDON_BASE"), "etc", modname) elif getenv("GRASS_ADDON_BASE") and isdir( diff --git a/python/grass/temporal/temporal_algebra.py b/python/grass/temporal/temporal_algebra.py index 1b9a126c635..1cf1cfd7366 100644 --- a/python/grass/temporal/temporal_algebra.py +++ b/python/grass/temporal/temporal_algebra.py @@ -1275,15 +1275,9 @@ def check_stds(self, input, clear=False, stds_type=None, check_type=True): self.temporaltype = "absolute" elif map_i.is_time_relative() and self.temporaltype is None: self.temporaltype = "relative" - elif map_i.is_time_absolute() and self.temporaltype == "relative": - self.msgr.fatal( - _( - "Wrong temporal type of space time dataset " - "<%s> <%s> time is required" - ) - % (id_input, self.temporaltype) - ) - elif map_i.is_time_relative() and self.temporaltype == "absolute": + elif ( + map_i.is_time_absolute() and self.temporaltype == "relative" + ) or (map_i.is_time_relative() and self.temporaltype == "absolute"): self.msgr.fatal( _( "Wrong temporal type of space time dataset " @@ -1299,13 +1293,9 @@ def check_stds(self, input, clear=False, stds_type=None, check_type=True): maplist = input # Create map_value as empty list item. for map_i in maplist: - if "map_value" not in dir(map_i): + if ("map_value" not in dir(map_i)) or clear: map_i.map_value = [] - elif clear: - map_i.map_value = [] - if "condition_value" not in dir(map_i): - map_i.condition_value = [] - elif clear: + if ("condition_value" not in dir(map_i)) or clear: map_i.condition_value = [] else: self.msgr.fatal(_("Wrong type of input " + str(input))) diff --git a/utils/gitlog2changelog.py b/utils/gitlog2changelog.py index 43aedcfb491..5ad459aa2b1 100755 --- a/utils/gitlog2changelog.py +++ b/utils/gitlog2changelog.py @@ -78,14 +78,12 @@ dateFound = True except Exception as e: print(f"Could not parse dateList = '{line}'. Error: {e!s}") - # The Fossil-IDs are ignored: - elif line.startswith((" Fossil-ID:", " [[SVN:")): - continue - # The svn-id lines are ignored - elif " git-svn-id:" in line: - continue - # The sign off line is ignored too - elif "Signed-off-by" in line: + # The Fossil-IDs, svn-id, ad sign off lines are ignored: + elif ( + line.startswith((" Fossil-ID:", " [[SVN:")) + or " git-svn-id:" in line + or "Signed-off-by" in line + ): continue # Extract the actual commit message for this commit elif authorFound & dateFound & messageFound is False: From ae5e4dd232230d32eadb49a7a0a71244f95c5972 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Sat, 28 Sep 2024 16:32:29 -0400 Subject: [PATCH 305/471] style: Fix duplicate-isinstance-call (SIM101) (#4405) --- gui/wxpython/gmodeler/canvas.py | 12 ++++-------- gui/wxpython/gmodeler/model.py | 2 +- pyproject.toml | 1 - 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/gui/wxpython/gmodeler/canvas.py b/gui/wxpython/gmodeler/canvas.py index 7c604ec6624..bfe5f7a90af 100644 --- a/gui/wxpython/gmodeler/canvas.py +++ b/gui/wxpython/gmodeler/canvas.py @@ -320,19 +320,19 @@ def OnRightClick(self, x, y, keys=0, attachment=0): popupMenu = Menu() popupMenu.Append(self.popupID["remove"], _("Remove")) self.frame.Bind(wx.EVT_MENU, self.OnRemove, id=self.popupID["remove"]) - if isinstance(shape, ModelAction) or isinstance(shape, ModelLoop): + if isinstance(shape, (ModelAction, ModelLoop)): if shape.IsEnabled(): popupMenu.Append(self.popupID["enable"], _("Disable")) self.frame.Bind(wx.EVT_MENU, self.OnDisable, id=self.popupID["enable"]) else: popupMenu.Append(self.popupID["enable"], _("Enable")) self.frame.Bind(wx.EVT_MENU, self.OnEnable, id=self.popupID["enable"]) - if isinstance(shape, ModelAction) or isinstance(shape, ModelComment): + if isinstance(shape, (ModelAction, ModelComment)): popupMenu.AppendSeparator() if isinstance(shape, ModelAction): popupMenu.Append(self.popupID["label"], _("Set label")) self.frame.Bind(wx.EVT_MENU, self.OnSetLabel, id=self.popupID["label"]) - if isinstance(shape, ModelAction) or isinstance(shape, ModelComment): + if isinstance(shape, (ModelAction, ModelComment)): popupMenu.Append(self.popupID["comment"], _("Set comment")) self.frame.Bind(wx.EVT_MENU, self.OnSetComment, id=self.popupID["comment"]) @@ -377,11 +377,7 @@ def OnRightClick(self, x, y, keys=0, attachment=0): if self.GetShape().IsIntermediate(): popupMenu.Enable(self.popupID["display"], False) - if ( - isinstance(shape, ModelData) - or isinstance(shape, ModelAction) - or isinstance(shape, ModelLoop) - ): + if isinstance(shape, (ModelData, ModelAction, ModelLoop)): popupMenu.AppendSeparator() popupMenu.Append(self.popupID["props"], _("Properties")) self.frame.Bind(wx.EVT_MENU, self.OnProperties, id=self.popupID["props"]) diff --git a/gui/wxpython/gmodeler/model.py b/gui/wxpython/gmodeler/model.py index a609a1c8bd8..59ea791192c 100644 --- a/gui/wxpython/gmodeler/model.py +++ b/gui/wxpython/gmodeler/model.py @@ -2641,7 +2641,7 @@ def _writeItem(self, item, ignoreBlock=True, variables={}): self._writePythonAction( item, variables, self.model.GetIntermediateData()[:3] ) - elif isinstance(item, ModelLoop) or isinstance(item, ModelCondition): + elif isinstance(item, (ModelLoop, ModelCondition)): # substitute condition cond = item.GetLabel() for variable in self.model.GetVariables(): diff --git a/pyproject.toml b/pyproject.toml index 31ac12eaf24..e288be23f10 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -244,7 +244,6 @@ ignore = [ "S606", # start-process-with-no-shell "S607", # start-process-with-partial-path "S608", # hardcoded-sql-expression - "SIM101", # duplicate-isinstance-call "SIM102", # collapsible-if "SIM105", # suppressible-exception "SIM108", # if-else-block-instead-of-if-exp From 0ea5e37d4122cf9df540a324f74ad55836e27d1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Sat, 28 Sep 2024 17:58:09 -0400 Subject: [PATCH 306/471] style: Fix reimplemented-operator (FURB118) (#4408) * style: Fix reimplemented-operator (FURB118) Ruff rule: https://docs.astral.sh/ruff/rules/reimplemented-operator/ Replaced lambdas with `operator.itemgetter()` or `operator.add()`. These are implemented more efficiently in C and have a Python fallback. * utils/g.html2man: Replace children function with operator.itemgetter --- gui/wxpython/animation/temporal_manager.py | 5 +++-- gui/wxpython/gui_core/ghelp.py | 3 ++- gui/wxpython/mapwin/buffered.py | 10 ++++++---- gui/wxpython/psmap/dialogs.py | 7 +++---- gui/wxpython/timeline/frame.py | 5 ++--- gui/wxpython/tplot/frame.py | 5 ++--- man/build_full_index.py | 4 +++- pyproject.toml | 1 - python/grass/imaging/images2ims.py | 3 ++- scripts/g.search.modules/g.search.modules.py | 4 +++- scripts/v.report/v.report.py | 4 +++- utils/g.html2man/rest.py | 4 ++-- 12 files changed, 31 insertions(+), 24 deletions(-) diff --git a/gui/wxpython/animation/temporal_manager.py b/gui/wxpython/animation/temporal_manager.py index 150eeab510f..ef782543089 100644 --- a/gui/wxpython/animation/temporal_manager.py +++ b/gui/wxpython/animation/temporal_manager.py @@ -18,6 +18,7 @@ """ import datetime +from operator import itemgetter import grass.script as gs import grass.temporal as tgis @@ -195,9 +196,9 @@ def GetLabelsAndMaps(self): # by a temporary dataset, I don't know how it would work with point # data if self.temporalType == TemporalType.ABSOLUTE: - timestamps = sorted(list(labelListSet), key=lambda x: x[0]) + timestamps = sorted(list(labelListSet), key=itemgetter(0)) else: - timestamps = sorted(list(labelListSet), key=lambda x: x[0]) + timestamps = sorted(list(labelListSet), key=itemgetter(0)) newMapLists = [] for mapList, labelList in zip(mapLists, labelLists): diff --git a/gui/wxpython/gui_core/ghelp.py b/gui/wxpython/gui_core/ghelp.py index 93879998133..72e2564b74d 100644 --- a/gui/wxpython/gui_core/ghelp.py +++ b/gui/wxpython/gui_core/ghelp.py @@ -25,6 +25,7 @@ import sys import wx from wx.html import HtmlWindow +from operator import itemgetter try: from wx.lib.agw.hyperlink import HyperLinkCtrl @@ -450,7 +451,7 @@ def _pageContributors(self, extra=False): text = StaticText(parent=contribwin, id=wx.ID_ANY, label=item) text.SetFont(wx.Font(10, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, "")) contribBox.Add(text) - for vals in sorted(contribs, key=lambda x: x[0]): + for vals in sorted(contribs, key=itemgetter(0)): for item in vals: contribBox.Add( StaticText(parent=contribwin, id=wx.ID_ANY, label=item) diff --git a/gui/wxpython/mapwin/buffered.py b/gui/wxpython/mapwin/buffered.py index e0442ed94ae..d8c6413017b 100644 --- a/gui/wxpython/mapwin/buffered.py +++ b/gui/wxpython/mapwin/buffered.py @@ -28,6 +28,8 @@ import wx +from operator import itemgetter + from grass.pydispatch.signal import Signal from core.globalvar import wxPythonPhoenix @@ -462,10 +464,10 @@ def Draw( brush = wx.TRANSPARENT_BRUSH pdc.SetBrush(brush) pdc.DrawPolygon(points=coords) - x = min(coords, key=lambda x: x[0])[0] - y = min(coords, key=lambda x: x[1])[1] - w = max(coords, key=lambda x: x[0])[0] - x - h = max(coords, key=lambda x: x[1])[1] - y + x = min(coords, key=itemgetter(0))[0] + y = min(coords, key=itemgetter(1))[1] + w = max(coords, key=itemgetter(0))[0] - x + h = max(coords, key=itemgetter(1))[1] - y pdc.SetIdBounds(drawid, Rect(x, y, w, h)) elif pdctype == "circle": # draw circle diff --git a/gui/wxpython/psmap/dialogs.py b/gui/wxpython/psmap/dialogs.py index 4eea798f731..41493795893 100644 --- a/gui/wxpython/psmap/dialogs.py +++ b/gui/wxpython/psmap/dialogs.py @@ -37,6 +37,7 @@ import os import string from copy import deepcopy +from operator import itemgetter from pathlib import Path import wx @@ -3623,9 +3624,7 @@ def _vectorLegend(self, notebook): self.vectorListCtrl.InsertColumn(0, _("Vector map")) self.vectorListCtrl.InsertColumn(1, _("Label")) if self.vectorId: - vectors = sorted( - self.instruction[self.vectorId]["list"], key=lambda x: x[3] - ) + vectors = sorted(self.instruction[self.vectorId]["list"], key=itemgetter(3)) for vector in vectors: index = self.vectorListCtrl.InsertItem( @@ -4456,7 +4455,7 @@ def updateDialog(self): if self.instruction.FindInstructionByType("vector"): vectors = sorted( self.instruction.FindInstructionByType("vector")["list"], - key=lambda x: x[3], + key=itemgetter(3), ) self.vectorListCtrl.DeleteAllItems() for vector in vectors: diff --git a/gui/wxpython/timeline/frame.py b/gui/wxpython/timeline/frame.py index 3982034619c..9a783f4f5d5 100644 --- a/gui/wxpython/timeline/frame.py +++ b/gui/wxpython/timeline/frame.py @@ -22,6 +22,7 @@ import wx from functools import reduce +from operator import add try: import matplotlib as mpl @@ -495,9 +496,7 @@ def _checkDatasets(self, datasets): ] # flatten this list if allDatasets: - allDatasets = reduce( - lambda x, y: x + y, reduce(lambda x, y: x + y, allDatasets) - ) + allDatasets = reduce(add, reduce(add, allDatasets)) mapsets = tgis.get_tgis_c_library_interface().available_mapsets() allDatasets = [ i diff --git a/gui/wxpython/tplot/frame.py b/gui/wxpython/tplot/frame.py index 2c7d49c3d72..8994485dacc 100755 --- a/gui/wxpython/tplot/frame.py +++ b/gui/wxpython/tplot/frame.py @@ -69,6 +69,7 @@ from gui_core.widgets import GNotebook from gui_core.wrap import CheckBox, TextCtrl, Button, StaticText +from operator import add ALPHA = 0.5 COLORS = ["b", "g", "r", "c", "m", "y", "k"] @@ -1153,9 +1154,7 @@ def _checkDatasets(self, datasets, typ): ] # flatten this list if allDatasets: - allDatasets = reduce( - lambda x, y: x + y, reduce(lambda x, y: x + y, allDatasets) - ) + allDatasets = reduce(add, reduce(add, allDatasets)) mapsets = tgis.get_tgis_c_library_interface().available_mapsets() allDatasets = [ i diff --git a/man/build_full_index.py b/man/build_full_index.py index ead5167cbb1..7d2ce03e342 100644 --- a/man/build_full_index.py +++ b/man/build_full_index.py @@ -9,6 +9,8 @@ import sys import os +from operator import itemgetter + from build_html import ( html_dir, grass_version, @@ -50,7 +52,7 @@ prefix = cmd.split(".")[0] if prefix not in [item[0] for item in classes]: classes.append((prefix, class_labels.get(prefix, prefix))) -classes.sort(key=lambda tup: tup[0]) +classes.sort(key=itemgetter(0)) # begin full index: filename = "full_index.html" diff --git a/pyproject.toml b/pyproject.toml index e288be23f10..3a6d449ec1d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -145,7 +145,6 @@ ignore = [ "FBT001", # boolean-type-hint-positional-argument "FBT002", # boolean-default-value-positional-argument "FBT003", # boolean-positional-value-in-call - "FURB118", # reimplemented-operator "I001", # unsorted-imports "ISC003", # explicit-string-concatenation "PERF203", # try-except-in-loop diff --git a/python/grass/imaging/images2ims.py b/python/grass/imaging/images2ims.py index 31088a22f2e..662e3eb46e0 100644 --- a/python/grass/imaging/images2ims.py +++ b/python/grass/imaging/images2ims.py @@ -31,6 +31,7 @@ """ import os +from operator import itemgetter try: import numpy as np @@ -214,7 +215,7 @@ def readIms(filename, asNumpy=True): images.append((im.copy(), nr)) # Sort images - images.sort(key=lambda x: x[1]) + images.sort(key=itemgetter(1)) images = [im[0] for im in images] # Convert to numpy if needed diff --git a/scripts/g.search.modules/g.search.modules.py b/scripts/g.search.modules/g.search.modules.py index 85247517898..a3bab4325c2 100755 --- a/scripts/g.search.modules/g.search.modules.py +++ b/scripts/g.search.modules/g.search.modules.py @@ -66,6 +66,8 @@ import os import sys +from operator import itemgetter + from grass.script import core as grass from grass.exceptions import CalledModuleError @@ -283,7 +285,7 @@ def _search_module( } ) - return sorted(found_modules, key=lambda k: k["name"]) + return sorted(found_modules, key=itemgetter("name")) def _basic_search(pattern, name, description, module_keywords) -> bool: diff --git a/scripts/v.report/v.report.py b/scripts/v.report/v.report.py index 18752a697cb..9da9a8b1de7 100755 --- a/scripts/v.report/v.report.py +++ b/scripts/v.report/v.report.py @@ -55,6 +55,8 @@ import sys import os +from operator import itemgetter + import grass.script as gs from grass.script.utils import separator, decode @@ -134,7 +136,7 @@ def main(): if p.returncode != 0: sys.exit(1) - records1.sort(key=lambda r: r[catcol]) + records1.sort(key=itemgetter(catcol)) if len(records1) == 0: try: diff --git a/utils/g.html2man/rest.py b/utils/g.html2man/rest.py index 2df87db8e27..c320f18e397 100644 --- a/utils/g.html2man/rest.py +++ b/utils/g.html2man/rest.py @@ -1,4 +1,5 @@ import sys +from operator import itemgetter def match(node, tag, attr=None, val=None): @@ -26,8 +27,7 @@ def find(node, tag, attr=None, val=None): raise ValueError("child not found") -def children(node): - return node[2] +children = itemgetter(2) def text(node): From 16c87b1d0d780fab1cb4025babf6f7db4530a8e5 Mon Sep 17 00:00:00 2001 From: Nicklas Larsson Date: Sun, 29 Sep 2024 10:18:54 +0200 Subject: [PATCH 307/471] lib/shapelib: sync upstream with shapelib 1.6.0 and GDAL 3.9.2 (#4395) * fix win runner (-Wincompatible-pointer-types warnings) * remove patch of http://trac.osgeo.org/gdal/ticket/809 --- lib/external/shapelib/README.md | 44 +- lib/external/shapelib/dbfopen.c | 344 ++++---- lib/external/shapelib/safileio.c | 195 +---- lib/external/shapelib/shapefil.h | 170 ++-- lib/external/shapelib/shapefil_private.h | 115 +++ lib/external/shapelib/shpopen.c | 980 ++++++++++++----------- 6 files changed, 933 insertions(+), 915 deletions(-) create mode 100644 lib/external/shapelib/shapefil_private.h diff --git a/lib/external/shapelib/README.md b/lib/external/shapelib/README.md index e004d2d0419..d446eccd014 100644 --- a/lib/external/shapelib/README.md +++ b/lib/external/shapelib/README.md @@ -1,12 +1,13 @@ # Update history of SHAPELIB copy -* files `shpopen.c`, `shapefil.h`, `dbfopen.c` +* files `shpopen.c`, `shapefil.h`, `dbfopen.c`, `shapefil_private.h` from GDAL [ogr/ogrsf_frmts/shape/](https://github.com/OSGeo/gdal/tree/master/ogr/ogrsf_frmts/shape) * file `safileio.c` from [SHAPELIB](http://download.osgeo.org/shapelib/) ## Last update +* taken from GDAL 3.9.2 and SHAPELIB 1.6.0 (Sep 2024) * taken from GDAL 3.5.3 and SHAPELIB 1.5.0 (Dec 2022) * taken from GDAL 2.1.2 and SHAPELIB 1.3.0 (Thu Nov 24 10:45:41 CET 2016) * taken from GDAL 1.5.1-SVN (Sun Mar 30 11:20:43 CEST 2008) @@ -15,44 +16,5 @@ ## Summary of fixes -* dbfopen.c - around line 1229: GDAL bug [ticket-#809](http://trac.osgeo.org/gdal/ticket/809) - * safileio.c - SHP_CVSID: ISO C does not allow extra ‘;’ outside of a function - -## Full fix - -```diff -diff --git a/lib/external/shapelib/dbfopen.c b/lib/external/shapelib/dbfopen.c -index 5380e3e20b..5151148d33 100644 ---- a/lib/external/shapelib/dbfopen.c -+++ b/lib/external/shapelib/dbfopen.c -@@ -1226,9 +1226,10 @@ DBFGetFieldInfo( DBFHandle psDBF, int iField, char * pszFieldName, - else if( psDBF->pachFieldType[iField] == 'N' - || psDBF->pachFieldType[iField] == 'F' ) - { -- if( psDBF->panFieldDecimals[iField] > 0 -- || psDBF->panFieldSize[iField] >= 10 ) -+ if( psDBF->panFieldDecimals[iField] > 0 ) { -+ /* || psDBF->panFieldSize[iField] >= 10 ) */ /* GDAL bug #809 */ - return( FTDouble ); -+ } - else - return( FTInteger ); - } -diff --git a/lib/external/shapelib/safileio.c b/lib/external/shapelib/safileio.c -index 289d347eaf..7a614a5806 100644 ---- a/lib/external/shapelib/safileio.c -+++ b/lib/external/shapelib/safileio.c -@@ -74,7 +74,7 @@ - #include - #include - --SHP_CVSID("$Id: safileio.c,v 1.6 2018-06-15 19:56:32 erouault Exp $"); -+SHP_CVSID("$Id: safileio.c,v 1.6 2018-06-15 19:56:32 erouault Exp $") - - #ifdef SHPAPI_UTF8_HOOKS - # ifdef SHPAPI_WINDOWS - -``` + [shapelib commit 316ff87](https://github.com/OSGeo/shapelib/commit/316ff872566ea0d91d6b62fe01bfe39931db39aa#diff-f068bc465ca1a32e1b9c214d4eb9504ef9e0f3c4cabc1aa4bab8aa41e2248cc6R153) diff --git a/lib/external/shapelib/dbfopen.c b/lib/external/shapelib/dbfopen.c index 2bbba339e21..9078a29ba95 100644 --- a/lib/external/shapelib/dbfopen.c +++ b/lib/external/shapelib/dbfopen.c @@ -1,5 +1,4 @@ /****************************************************************************** - * $Id$ * * Project: Shapelib * Purpose: Implementation of .dbf access API documented in dbf_api.html. @@ -9,32 +8,10 @@ * Copyright (c) 1999, Frank Warmerdam * Copyright (c) 2012-2019, Even Rouault * - * This software is available under the following "MIT Style" license, - * or at the option of the licensee under the LGPL (see COPYING). This - * option is discussed in more detail in shapelib.html. - * - * -- - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. + * SPDX-License-Identifier: MIT OR LGPL-2.0-or-later ******************************************************************************/ -#include "shapefil.h" +#include "shapefil_private.h" #include #include @@ -47,7 +24,9 @@ #include "cpl_string.h" #else -#if defined(WIN32) || defined(_WIN32) +#if defined(_MSC_VER) +#define STRCASECMP(a, b) (_stricmp(a, b)) +#elif defined(_WIN32) #define STRCASECMP(a, b) (stricmp(a, b)) #else #include @@ -58,7 +37,7 @@ #if _MSC_VER < 1900 #define snprintf _snprintf #endif -#elif defined(WIN32) || defined(_WIN32) +#elif defined(_WIN32) #ifndef snprintf #define snprintf _snprintf #endif @@ -68,8 +47,6 @@ #define CPLsnprintf snprintf #endif -SHP_CVSID("$Id$") - #ifndef FALSE #define FALSE 0 #define TRUE 1 @@ -91,33 +68,6 @@ CPL_INLINE static void CPL_IGNORE_RET_VAL_INT(CPL_UNUSED int unused) #define CPL_IGNORE_RET_VAL_INT(x) x #endif -#ifdef __cplusplus -#define STATIC_CAST(type, x) static_cast(x) -#define REINTERPRET_CAST(type, x) reinterpret_cast(x) -#define CONST_CAST(type, x) const_cast(x) -#define SHPLIB_NULLPTR nullptr -#else -#define STATIC_CAST(type, x) ((type)(x)) -#define REINTERPRET_CAST(type, x) ((type)(x)) -#define CONST_CAST(type, x) ((type)(x)) -#define SHPLIB_NULLPTR NULL -#endif - -/************************************************************************/ -/* SfRealloc() */ -/* */ -/* A realloc cover function that will access a NULL pointer as */ -/* a valid input. */ -/************************************************************************/ - -static void *SfRealloc(void *pMem, int nNewSize) -{ - if (pMem == SHPLIB_NULLPTR) - return malloc(nNewSize); - else - return realloc(pMem, nNewSize); -} - /************************************************************************/ /* DBFWriteHeader() */ /* */ @@ -137,9 +87,9 @@ static void DBFWriteHeader(DBFHandle psDBF) psDBF->bNoHeader = FALSE; /* -------------------------------------------------------------------- */ - /* Initialize the file header information. */ + /* Initialize the file header information. */ /* -------------------------------------------------------------------- */ - abyHeader[0] = 0x03; /* memo field? - just copying */ + abyHeader[0] = 0x03; /* memo field? - just copying */ /* write out update date */ abyHeader[1] = STATIC_CAST(unsigned char, psDBF->nUpdateYearSince1900); @@ -158,7 +108,7 @@ static void DBFWriteHeader(DBFHandle psDBF) /* -------------------------------------------------------------------- */ /* Write the initial 32 byte file header, and all the field */ - /* descriptions. */ + /* descriptions. */ /* -------------------------------------------------------------------- */ psDBF->sHooks.FSeek(psDBF->fp, 0, 0); psDBF->sHooks.FWrite(abyHeader, XBASE_FILEHDR_SZ, 1, psDBF->fp); @@ -344,7 +294,6 @@ void SHPAPI_CALL DBFSetLastModifiedDate(DBFHandle psDBF, int nYYSince1900, /************************************************************************/ DBFHandle SHPAPI_CALL DBFOpen(const char *pszFilename, const char *pszAccess) - { SAHooks sHooks; @@ -376,7 +325,7 @@ static int DBFGetLenWithoutExtension(const char *pszBasename) /************************************************************************/ DBFHandle SHPAPI_CALL DBFOpenLL(const char *pszFilename, const char *pszAccess, - SAHooks *psHooks) + const SAHooks *psHooks) { /* -------------------------------------------------------------------- */ /* We only allow the access strings "rb" and "r+". */ @@ -393,8 +342,8 @@ DBFHandle SHPAPI_CALL DBFOpenLL(const char *pszFilename, const char *pszAccess, pszAccess = "rb+"; /* -------------------------------------------------------------------- */ - /* Compute the base (layer) name. If there is any extension */ - /* on the passed in filename we will strip it off. */ + /* Compute the base (layer) name. If there is any extension */ + /* on the passed in filename we will strip it off. */ /* -------------------------------------------------------------------- */ const int nLenWithoutExtension = DBFGetLenWithoutExtension(pszFilename); char *pszFullname = STATIC_CAST(char *, malloc(nLenWithoutExtension + 5)); @@ -402,19 +351,20 @@ DBFHandle SHPAPI_CALL DBFOpenLL(const char *pszFilename, const char *pszAccess, memcpy(pszFullname + nLenWithoutExtension, ".dbf", 5); DBFHandle psDBF = STATIC_CAST(DBFHandle, calloc(1, sizeof(DBFInfo))); - psDBF->fp = psHooks->FOpen(pszFullname, pszAccess); + psDBF->fp = psHooks->FOpen(pszFullname, pszAccess, psHooks->pvUserData); memcpy(&(psDBF->sHooks), psHooks, sizeof(SAHooks)); if (psDBF->fp == SHPLIB_NULLPTR) { memcpy(pszFullname + nLenWithoutExtension, ".DBF", 5); - psDBF->fp = psDBF->sHooks.FOpen(pszFullname, pszAccess); + psDBF->fp = + psDBF->sHooks.FOpen(pszFullname, pszAccess, psHooks->pvUserData); } memcpy(pszFullname + nLenWithoutExtension, ".cpg", 5); - SAFile pfCPG = psHooks->FOpen(pszFullname, "r"); + SAFile pfCPG = psHooks->FOpen(pszFullname, "r", psHooks->pvUserData); if (pfCPG == SHPLIB_NULLPTR) { memcpy(pszFullname + nLenWithoutExtension, ".CPG", 5); - pfCPG = psHooks->FOpen(pszFullname, "r"); + pfCPG = psHooks->FOpen(pszFullname, "r", psHooks->pvUserData); } free(pszFullname); @@ -495,7 +445,7 @@ DBFHandle SHPAPI_CALL DBFOpenLL(const char *pszFilename, const char *pszAccess, /* -------------------------------------------------------------------- */ /* Read in Field Definitions */ /* -------------------------------------------------------------------- */ - pabyBuf = STATIC_CAST(unsigned char *, SfRealloc(pabyBuf, nHeadLen)); + pabyBuf = STATIC_CAST(unsigned char *, realloc(pabyBuf, nHeadLen)); psDBF->pszHeader = REINTERPRET_CAST(char *, pabyBuf); psDBF->sHooks.FSeek(psDBF->fp, XBASE_FILEHDR_SZ, 0); @@ -583,8 +533,8 @@ void SHPAPI_CALL DBFClose(DBFHandle psDBF) CPL_IGNORE_RET_VAL_INT(DBFFlushRecord(psDBF)); /* -------------------------------------------------------------------- */ - /* Update last access date, and number of records if we have */ - /* write access. */ + /* Update last access date, and number of records if we have */ + /* write access. */ /* -------------------------------------------------------------------- */ if (psDBF->bUpdated) DBFUpdateHeader(psDBF); @@ -645,11 +595,12 @@ DBFHandle SHPAPI_CALL DBFCreateEx(const char *pszFilename, /************************************************************************/ DBFHandle SHPAPI_CALL DBFCreateLL(const char *pszFilename, - const char *pszCodePage, SAHooks *psHooks) + const char *pszCodePage, + const SAHooks *psHooks) { /* -------------------------------------------------------------------- */ - /* Compute the base (layer) name. If there is any extension */ - /* on the passed in filename we will strip it off. */ + /* Compute the base (layer) name. If there is any extension */ + /* on the passed in filename we will strip it off. */ /* -------------------------------------------------------------------- */ const int nLenWithoutExtension = DBFGetLenWithoutExtension(pszFilename); char *pszFullname = STATIC_CAST(char *, malloc(nLenWithoutExtension + 5)); @@ -659,17 +610,7 @@ DBFHandle SHPAPI_CALL DBFCreateLL(const char *pszFilename, /* -------------------------------------------------------------------- */ /* Create the file. */ /* -------------------------------------------------------------------- */ - SAFile fp = psHooks->FOpen(pszFullname, "wb"); - if (fp == SHPLIB_NULLPTR) { - free(pszFullname); - return SHPLIB_NULLPTR; - } - - char chZero = '\0'; - psHooks->FWrite(&chZero, 1, 1, fp); - psHooks->FClose(fp); - - fp = psHooks->FOpen(pszFullname, "rb+"); + SAFile fp = psHooks->FOpen(pszFullname, "wb+", psHooks->pvUserData); if (fp == SHPLIB_NULLPTR) { free(pszFullname); return SHPLIB_NULLPTR; @@ -685,7 +626,8 @@ DBFHandle SHPAPI_CALL DBFCreateLL(const char *pszFilename, // a valid one } if (ldid < 0) { - SAFile fpCPG = psHooks->FOpen(pszFullname, "w"); + SAFile fpCPG = + psHooks->FOpen(pszFullname, "w", psHooks->pvUserData); psHooks->FWrite( CONST_CAST(void *, STATIC_CAST(const void *, pszCodePage)), strlen(pszCodePage), 1, fpCPG); @@ -693,13 +635,13 @@ DBFHandle SHPAPI_CALL DBFCreateLL(const char *pszFilename, } } if (pszCodePage == SHPLIB_NULLPTR || ldid >= 0) { - psHooks->Remove(pszFullname); + psHooks->Remove(pszFullname, psHooks->pvUserData); } free(pszFullname); /* -------------------------------------------------------------------- */ - /* Create the info structure. */ + /* Create the info structure. */ /* -------------------------------------------------------------------- */ DBFHandle psDBF = STATIC_CAST(DBFHandle, calloc(1, sizeof(DBFInfo))); @@ -829,23 +771,22 @@ int SHPAPI_CALL DBFAddNativeFieldType(DBFHandle psDBF, const char *pszFieldName, const int nOldHeaderLength = psDBF->nHeaderLength; /* -------------------------------------------------------------------- */ - /* SfRealloc all the arrays larger to hold the additional field */ + /* realloc all the arrays larger to hold the additional field */ /* information. */ /* -------------------------------------------------------------------- */ psDBF->nFields++; psDBF->panFieldOffset = STATIC_CAST( - int *, SfRealloc(psDBF->panFieldOffset, sizeof(int) * psDBF->nFields)); + int *, realloc(psDBF->panFieldOffset, sizeof(int) * psDBF->nFields)); psDBF->panFieldSize = STATIC_CAST( - int *, SfRealloc(psDBF->panFieldSize, sizeof(int) * psDBF->nFields)); + int *, realloc(psDBF->panFieldSize, sizeof(int) * psDBF->nFields)); - psDBF->panFieldDecimals = - STATIC_CAST(int *, SfRealloc(psDBF->panFieldDecimals, - sizeof(int) * psDBF->nFields)); + psDBF->panFieldDecimals = STATIC_CAST( + int *, realloc(psDBF->panFieldDecimals, sizeof(int) * psDBF->nFields)); psDBF->pachFieldType = STATIC_CAST( - char *, SfRealloc(psDBF->pachFieldType, sizeof(char) * psDBF->nFields)); + char *, realloc(psDBF->pachFieldType, sizeof(char) * psDBF->nFields)); /* -------------------------------------------------------------------- */ /* Assign the new field information fields. */ @@ -863,7 +804,7 @@ int SHPAPI_CALL DBFAddNativeFieldType(DBFHandle psDBF, const char *pszFieldName, psDBF->bUpdated = FALSE; psDBF->pszHeader = STATIC_CAST( - char *, SfRealloc(psDBF->pszHeader, psDBF->nFields * XBASE_FLDHDR_SZ)); + char *, realloc(psDBF->pszHeader, psDBF->nFields * XBASE_FLDHDR_SZ)); char *pszFInfo = psDBF->pszHeader + XBASE_FLDHDR_SZ * (psDBF->nFields - 1); @@ -887,7 +828,7 @@ int SHPAPI_CALL DBFAddNativeFieldType(DBFHandle psDBF, const char *pszFieldName, /* Make the current record buffer appropriately larger. */ /* -------------------------------------------------------------------- */ psDBF->pszCurrentRecord = STATIC_CAST( - char *, SfRealloc(psDBF->pszCurrentRecord, psDBF->nRecordLength)); + char *, realloc(psDBF->pszCurrentRecord, psDBF->nRecordLength)); /* we're done if dealing with new .dbf */ if (psDBF->bNoHeader) @@ -971,13 +912,13 @@ static void *DBFReadAttribute(DBFHandle psDBF, int hEntity, int iField, return SHPLIB_NULLPTR; /* -------------------------------------------------------------------- */ - /* Have we read the record? */ + /* Have we read the record? */ /* -------------------------------------------------------------------- */ if (!DBFLoadRecord(psDBF, hEntity)) return SHPLIB_NULLPTR; - unsigned char *pabyRec = - REINTERPRET_CAST(unsigned char *, psDBF->pszCurrentRecord); + const unsigned char *pabyRec = + REINTERPRET_CAST(const unsigned char *, psDBF->pszCurrentRecord); /* -------------------------------------------------------------------- */ /* Ensure we have room to extract the target field. */ @@ -993,7 +934,7 @@ static void *DBFReadAttribute(DBFHandle psDBF, int hEntity, int iField, } /* -------------------------------------------------------------------- */ - /* Extract the requested field. */ + /* Extract the requested field. */ /* -------------------------------------------------------------------- */ memcpy(psDBF->pszWorkField, REINTERPRET_CAST(const char *, pabyRec) + @@ -1085,7 +1026,6 @@ double SHPAPI_CALL DBFReadDoubleAttribute(DBFHandle psDBF, int iRecord, const char SHPAPI_CALL1(*) DBFReadStringAttribute(DBFHandle psDBF, int iRecord, int iField) - { return STATIC_CAST(const char *, DBFReadAttribute(psDBF, iRecord, iField, 'C')); @@ -1099,12 +1039,40 @@ const char SHPAPI_CALL1(*) const char SHPAPI_CALL1(*) DBFReadLogicalAttribute(DBFHandle psDBF, int iRecord, int iField) - { return STATIC_CAST(const char *, DBFReadAttribute(psDBF, iRecord, iField, 'L')); } +/************************************************************************/ +/* DBFReadDateAttribute() */ +/* */ +/* Read a date attribute. */ +/************************************************************************/ + +SHPDate SHPAPI_CALL DBFReadDateAttribute(DBFHandle psDBF, int iRecord, + int iField) +{ + const char *pdateValue = STATIC_CAST( + const char *, DBFReadAttribute(psDBF, iRecord, iField, 'D')); + + SHPDate date; + + if (pdateValue == SHPLIB_NULLPTR) { + date.year = 0; + date.month = 0; + date.day = 0; + } + else if (3 != sscanf(pdateValue, "%4d%2d%2d", &date.year, &date.month, + &date.day)) { + date.year = 0; + date.month = 0; + date.day = 0; + } + + return date; +} + /************************************************************************/ /* DBFIsValueNULL() */ /* */ @@ -1135,7 +1103,16 @@ static bool DBFIsValueNULL(char chType, const char *pszValue) case 'D': /* NULL date fields have value "00000000" */ - return strncmp(pszValue, "00000000", 8) == 0; + /* Some DBF files have fields filled with spaces */ + /* (trimmed by DBFReadStringAttribute) to indicate null */ + /* values for dates (#4265). */ + /* And others have ' 0': + * https://lists.osgeo.org/pipermail/gdal-dev/2023-November/058010.html + */ + /* And others just empty string: + * https://github.com/OSGeo/gdal/issues/10405 */ + return pszValue[0] == 0 || strncmp(pszValue, "00000000", 8) == 0 || + strcmp(pszValue, " ") == 0 || strcmp(pszValue, "0") == 0; case 'L': /* NULL boolean fields have value "?" */ @@ -1155,7 +1132,8 @@ static bool DBFIsValueNULL(char chType, const char *pszValue) /* Contributed by Jim Matthews. */ /************************************************************************/ -int SHPAPI_CALL DBFIsAttributeNULL(DBFHandle psDBF, int iRecord, int iField) +int SHPAPI_CALL DBFIsAttributeNULL(const DBFHandle psDBF, int iRecord, + int iField) { const char *pszValue = DBFReadStringAttribute(psDBF, iRecord, iField); @@ -1171,8 +1149,7 @@ int SHPAPI_CALL DBFIsAttributeNULL(DBFHandle psDBF, int iRecord, int iField) /* Return the number of fields in this table. */ /************************************************************************/ -int SHPAPI_CALL DBFGetFieldCount(DBFHandle psDBF) - +int SHPAPI_CALL DBFGetFieldCount(const DBFHandle psDBF) { return (psDBF->nFields); } @@ -1183,8 +1160,7 @@ int SHPAPI_CALL DBFGetFieldCount(DBFHandle psDBF) /* Return the number of records in this table. */ /************************************************************************/ -int SHPAPI_CALL DBFGetRecordCount(DBFHandle psDBF) - +int SHPAPI_CALL DBFGetRecordCount(const DBFHandle psDBF) { return (psDBF->nRecords); } @@ -1197,10 +1173,9 @@ int SHPAPI_CALL DBFGetRecordCount(DBFHandle psDBF) /* bytes long. */ /************************************************************************/ -DBFFieldType SHPAPI_CALL DBFGetFieldInfo(DBFHandle psDBF, int iField, +DBFFieldType SHPAPI_CALL DBFGetFieldInfo(const DBFHandle psDBF, int iField, char *pszFieldName, int *pnWidth, int *pnDecimals) - { if (iField < 0 || iField >= psDBF->nFields) return (FTInvalid); @@ -1230,10 +1205,9 @@ DBFFieldType SHPAPI_CALL DBFGetFieldInfo(DBFHandle psDBF, int iField, else if (psDBF->pachFieldType[iField] == 'N' || psDBF->pachFieldType[iField] == 'F') { - if (psDBF->panFieldDecimals[iField] > 0) { - /* || psDBF->panFieldSize[iField] >= 10 ) */ /* GDAL bug #809 */ + if (psDBF->panFieldDecimals[iField] > 0 || + psDBF->panFieldSize[iField] >= 10) return (FTDouble); - } else return (FTInteger); } @@ -1244,15 +1218,15 @@ DBFFieldType SHPAPI_CALL DBFGetFieldInfo(DBFHandle psDBF, int iField, /************************************************************************/ /* DBFWriteAttribute() */ -/* */ -/* Write an attribute record to the file. */ +/* */ +/* Write an attribute record to the file. */ /************************************************************************/ static bool DBFWriteAttribute(DBFHandle psDBF, int hEntity, int iField, void *pValue) { /* -------------------------------------------------------------------- */ - /* Is this a valid record? */ + /* Is this a valid record? */ /* -------------------------------------------------------------------- */ if (hEntity < 0 || hEntity > psDBF->nRecords) return false; @@ -1333,9 +1307,13 @@ static bool DBFWriteAttribute(DBFHandle psDBF, int hEntity, int iField, case 'L': if (psDBF->panFieldSize[iField] >= 1 && (*STATIC_CAST(char *, pValue) == 'F' || - *STATIC_CAST(char *, pValue) == 'T')) + *STATIC_CAST(char *, pValue) == 'T')) { *(pabyRec + psDBF->panFieldOffset[iField]) = *STATIC_CAST(char *, pValue); + } + else { + nRetResult = false; + } break; default: { @@ -1370,10 +1348,10 @@ static bool DBFWriteAttribute(DBFHandle psDBF, int hEntity, int iField, /************************************************************************/ int SHPAPI_CALL DBFWriteAttributeDirectly(DBFHandle psDBF, int hEntity, - int iField, void *pValue) + int iField, const void *pValue) { /* -------------------------------------------------------------------- */ - /* Is this a valid record? */ + /* Is this a valid record? */ /* -------------------------------------------------------------------- */ if (hEntity < 0 || hEntity > psDBF->nRecords) return (FALSE); @@ -1402,24 +1380,29 @@ int SHPAPI_CALL DBFWriteAttributeDirectly(DBFHandle psDBF, int hEntity, if (!DBFLoadRecord(psDBF, hEntity)) return FALSE; - unsigned char *pabyRec = - REINTERPRET_CAST(unsigned char *, psDBF->pszCurrentRecord); + if (iField >= 0) { + unsigned char *pabyRec = + REINTERPRET_CAST(unsigned char *, psDBF->pszCurrentRecord); - /* -------------------------------------------------------------------- */ - /* Assign all the record fields. */ - /* -------------------------------------------------------------------- */ - int j; - if (STATIC_CAST(int, strlen(STATIC_CAST(char *, pValue))) > - psDBF->panFieldSize[iField]) - j = psDBF->panFieldSize[iField]; - else { - memset(pabyRec + psDBF->panFieldOffset[iField], ' ', - psDBF->panFieldSize[iField]); - j = STATIC_CAST(int, strlen(STATIC_CAST(char *, pValue))); - } + /* -------------------------------------------------------------------- + */ + /* Assign all the record fields. */ + /* -------------------------------------------------------------------- + */ + int j; + if (STATIC_CAST(int, strlen(STATIC_CAST(const char *, pValue))) > + psDBF->panFieldSize[iField]) + j = psDBF->panFieldSize[iField]; + else { + memset(pabyRec + psDBF->panFieldOffset[iField], ' ', + psDBF->panFieldSize[iField]); + j = STATIC_CAST(int, strlen(STATIC_CAST(const char *, pValue))); + } - strncpy(REINTERPRET_CAST(char *, pabyRec + psDBF->panFieldOffset[iField]), + memcpy( + REINTERPRET_CAST(char *, pabyRec + psDBF->panFieldOffset[iField]), STATIC_CAST(const char *, pValue), j); + } psDBF->bCurrentRecordModified = TRUE; psDBF->bUpdated = TRUE; @@ -1443,7 +1426,7 @@ int SHPAPI_CALL DBFWriteDoubleAttribute(DBFHandle psDBF, int iRecord, /************************************************************************/ /* DBFWriteIntegerAttribute() */ /* */ -/* Write a integer attribute. */ +/* Write an integer attribute. */ /************************************************************************/ int SHPAPI_CALL DBFWriteIntegerAttribute(DBFHandle psDBF, int iRecord, @@ -1463,7 +1446,6 @@ int SHPAPI_CALL DBFWriteIntegerAttribute(DBFHandle psDBF, int iRecord, int SHPAPI_CALL DBFWriteStringAttribute(DBFHandle psDBF, int iRecord, int iField, const char *pszValue) - { return ( DBFWriteAttribute(psDBF, iRecord, iField, @@ -1473,11 +1455,10 @@ int SHPAPI_CALL DBFWriteStringAttribute(DBFHandle psDBF, int iRecord, /************************************************************************/ /* DBFWriteNULLAttribute() */ /* */ -/* Write a string attribute. */ +/* Write a NULL attribute. */ /************************************************************************/ int SHPAPI_CALL DBFWriteNULLAttribute(DBFHandle psDBF, int iRecord, int iField) - { return (DBFWriteAttribute(psDBF, iRecord, iField, SHPLIB_NULLPTR)); } @@ -1490,23 +1471,47 @@ int SHPAPI_CALL DBFWriteNULLAttribute(DBFHandle psDBF, int iRecord, int iField) int SHPAPI_CALL DBFWriteLogicalAttribute(DBFHandle psDBF, int iRecord, int iField, const char lValue) - { return ( DBFWriteAttribute(psDBF, iRecord, iField, STATIC_CAST(void *, CONST_CAST(char *, &lValue)))); } +/************************************************************************/ +/* DBFWriteDateAttribute() */ +/* */ +/* Write a date attribute. */ +/************************************************************************/ + +int SHPAPI_CALL DBFWriteDateAttribute(DBFHandle psDBF, int iRecord, int iField, + const SHPDate *lValue) +{ + if (SHPLIB_NULLPTR == lValue) + return false; + /* check for supported digit range, but do not check for valid date */ + if (lValue->year < 0 || lValue->year > 9999) + return false; + if (lValue->month < 0 || lValue->month > 99) + return false; + if (lValue->day < 0 || lValue->day > 99) + return false; + char dateValue[9]; /* "yyyyMMdd\0" */ + snprintf(dateValue, sizeof(dateValue), "%04d%02d%02d", lValue->year, + lValue->month, lValue->day); + return (DBFWriteAttributeDirectly(psDBF, iRecord, iField, dateValue)); +} + /************************************************************************/ /* DBFWriteTuple() */ -/* */ -/* Write an attribute record to the file. */ +/* */ +/* Write an attribute record to the file. */ /************************************************************************/ -int SHPAPI_CALL DBFWriteTuple(DBFHandle psDBF, int hEntity, void *pRawTuple) +int SHPAPI_CALL DBFWriteTuple(DBFHandle psDBF, int hEntity, + const void *pRawTuple) { /* -------------------------------------------------------------------- */ - /* Is this a valid record? */ + /* Is this a valid record? */ /* -------------------------------------------------------------------- */ if (hEntity < 0 || hEntity > psDBF->nRecords) return (FALSE); @@ -1554,7 +1559,6 @@ int SHPAPI_CALL DBFWriteTuple(DBFHandle psDBF, int hEntity, void *pRawTuple) /************************************************************************/ const char SHPAPI_CALL1(*) DBFReadTuple(DBFHandle psDBF, int hEntity) - { if (hEntity < 0 || hEntity >= psDBF->nRecords) return SHPLIB_NULLPTR; @@ -1566,14 +1570,17 @@ const char SHPAPI_CALL1(*) DBFReadTuple(DBFHandle psDBF, int hEntity) } /************************************************************************/ -/* DBFCloneEmpty() */ +/* DBFCloneEmpty() */ /* */ -/* Read one of the attribute fields of a record. */ +/* Create a new .dbf file with same code page and field */ +/* definitions as the given handle. */ /************************************************************************/ -DBFHandle SHPAPI_CALL DBFCloneEmpty(DBFHandle psDBF, const char *pszFilename) +DBFHandle SHPAPI_CALL DBFCloneEmpty(const DBFHandle psDBF, + const char *pszFilename) { - DBFHandle newDBF = DBFCreateEx(pszFilename, psDBF->pszCodePage); + DBFHandle newDBF = + DBFCreateLL(pszFilename, psDBF->pszCodePage, &psDBF->sHooks); if (newDBF == SHPLIB_NULLPTR) return SHPLIB_NULLPTR; @@ -1629,8 +1636,7 @@ DBFHandle SHPAPI_CALL DBFCloneEmpty(DBFHandle psDBF, const char *pszFilename) /* 'M' (Memo: 10 digits .DBT block ptr) */ /************************************************************************/ -char SHPAPI_CALL DBFGetNativeFieldType(DBFHandle psDBF, int iField) - +char SHPAPI_CALL DBFGetNativeFieldType(const DBFHandle psDBF, int iField) { if (iField >= 0 && iField < psDBF->nFields) return psDBF->pachFieldType[iField]; @@ -1646,7 +1652,8 @@ char SHPAPI_CALL DBFGetNativeFieldType(DBFHandle psDBF, int iField) /* Contributed by Jim Matthews. */ /************************************************************************/ -int SHPAPI_CALL DBFGetFieldIndex(DBFHandle psDBF, const char *pszFieldName) +int SHPAPI_CALL DBFGetFieldIndex(const DBFHandle psDBF, + const char *pszFieldName) { char name[XBASE_FLDNAME_LEN_READ + 1]; @@ -1665,7 +1672,7 @@ int SHPAPI_CALL DBFGetFieldIndex(DBFHandle psDBF, const char *pszFieldName) /* it returns FALSE. */ /************************************************************************/ -int SHPAPI_CALL DBFIsRecordDeleted(DBFHandle psDBF, int iShape) +int SHPAPI_CALL DBFIsRecordDeleted(const DBFHandle psDBF, int iShape) { /* -------------------------------------------------------------------- */ /* Verify selection. */ @@ -1674,7 +1681,7 @@ int SHPAPI_CALL DBFIsRecordDeleted(DBFHandle psDBF, int iShape) return TRUE; /* -------------------------------------------------------------------- */ - /* Have we read the record? */ + /* Have we read the record? */ /* -------------------------------------------------------------------- */ if (!DBFLoadRecord(psDBF, iShape)) return FALSE; @@ -1727,7 +1734,7 @@ int SHPAPI_CALL DBFMarkRecordDeleted(DBFHandle psDBF, int iShape, /* DBFGetCodePage */ /************************************************************************/ -const char SHPAPI_CALL1(*) DBFGetCodePage(DBFHandle psDBF) +const char SHPAPI_CALL1(*) DBFGetCodePage(const DBFHandle psDBF) { if (psDBF == SHPLIB_NULLPTR) return SHPLIB_NULLPTR; @@ -1768,17 +1775,16 @@ int SHPAPI_CALL DBFDeleteField(DBFHandle psDBF, int iField) psDBF->nFields--; psDBF->panFieldOffset = STATIC_CAST( - int *, SfRealloc(psDBF->panFieldOffset, sizeof(int) * psDBF->nFields)); + int *, realloc(psDBF->panFieldOffset, sizeof(int) * psDBF->nFields)); psDBF->panFieldSize = STATIC_CAST( - int *, SfRealloc(psDBF->panFieldSize, sizeof(int) * psDBF->nFields)); + int *, realloc(psDBF->panFieldSize, sizeof(int) * psDBF->nFields)); - psDBF->panFieldDecimals = - STATIC_CAST(int *, SfRealloc(psDBF->panFieldDecimals, - sizeof(int) * psDBF->nFields)); + psDBF->panFieldDecimals = STATIC_CAST( + int *, realloc(psDBF->panFieldDecimals, sizeof(int) * psDBF->nFields)); psDBF->pachFieldType = STATIC_CAST( - char *, SfRealloc(psDBF->pachFieldType, sizeof(char) * psDBF->nFields)); + char *, realloc(psDBF->pachFieldType, sizeof(char) * psDBF->nFields)); /* update header information */ psDBF->nHeaderLength -= XBASE_FLDHDR_SZ; @@ -1790,11 +1796,11 @@ int SHPAPI_CALL DBFDeleteField(DBFHandle psDBF, int iField) sizeof(char) * (psDBF->nFields - iField) * XBASE_FLDHDR_SZ); psDBF->pszHeader = STATIC_CAST( - char *, SfRealloc(psDBF->pszHeader, psDBF->nFields * XBASE_FLDHDR_SZ)); + char *, realloc(psDBF->pszHeader, psDBF->nFields * XBASE_FLDHDR_SZ)); /* update size of current record appropriately */ psDBF->pszCurrentRecord = STATIC_CAST( - char *, SfRealloc(psDBF->pszCurrentRecord, psDBF->nRecordLength)); + char *, realloc(psDBF->pszCurrentRecord, psDBF->nRecordLength)); /* we're done if we're dealing with not yet created .dbf */ if (psDBF->bNoHeader && psDBF->nRecords == 0) @@ -1866,7 +1872,7 @@ int SHPAPI_CALL DBFDeleteField(DBFHandle psDBF, int iField) /* code of DBFReorderFields. */ /************************************************************************/ -int SHPAPI_CALL DBFReorderFields(DBFHandle psDBF, int *panMap) +int SHPAPI_CALL DBFReorderFields(DBFHandle psDBF, const int *panMap) { if (psDBF->nFields == 0) return TRUE; @@ -1878,13 +1884,13 @@ int SHPAPI_CALL DBFReorderFields(DBFHandle psDBF, int *panMap) /* a simple malloc() would be enough, but calloc() helps clang static * analyzer */ int *panFieldOffsetNew = - STATIC_CAST(int *, calloc(sizeof(int), psDBF->nFields)); + STATIC_CAST(int *, calloc(psDBF->nFields, sizeof(int))); int *panFieldSizeNew = - STATIC_CAST(int *, calloc(sizeof(int), psDBF->nFields)); + STATIC_CAST(int *, calloc(psDBF->nFields, sizeof(int))); int *panFieldDecimalsNew = - STATIC_CAST(int *, calloc(sizeof(int), psDBF->nFields)); + STATIC_CAST(int *, calloc(psDBF->nFields, sizeof(int))); char *pachFieldTypeNew = - STATIC_CAST(char *, calloc(sizeof(char), psDBF->nFields)); + STATIC_CAST(char *, calloc(psDBF->nFields, sizeof(char))); char *pszHeaderNew = STATIC_CAST( char *, malloc(sizeof(char) * XBASE_FLDHDR_SZ * psDBF->nFields)); @@ -2050,7 +2056,7 @@ int SHPAPI_CALL DBFAlterFieldDefn(DBFHandle psDBF, int iField, psDBF->nRecordLength += nWidth - nOldWidth; psDBF->pszCurrentRecord = STATIC_CAST( - char *, SfRealloc(psDBF->pszCurrentRecord, psDBF->nRecordLength)); + char *, realloc(psDBF->pszCurrentRecord, psDBF->nRecordLength)); } /* we're done if we're dealing with not yet created .dbf */ @@ -2069,7 +2075,6 @@ int SHPAPI_CALL DBFAlterFieldDefn(DBFHandle psDBF, int iField, char *pszOldField = STATIC_CAST(char *, malloc(sizeof(char) * (nOldWidth + 1))); - /* cppcheck-suppress uninitdata */ pszOldField[nOldWidth] = 0; /* move records to their new positions */ @@ -2139,7 +2144,6 @@ int SHPAPI_CALL DBFAlterFieldDefn(DBFHandle psDBF, int iField, char *pszOldField = STATIC_CAST(char *, malloc(sizeof(char) * (nOldWidth + 1))); - /* cppcheck-suppress uninitdata */ pszOldField[nOldWidth] = 0; /* move records to their new positions */ diff --git a/lib/external/shapelib/safileio.c b/lib/external/shapelib/safileio.c index 3377642e4d4..e13f1f29acf 100644 --- a/lib/external/shapelib/safileio.c +++ b/lib/external/shapelib/safileio.c @@ -1,5 +1,4 @@ /****************************************************************************** - * $Id: safileio.c,v 1.6 2018-06-15 19:56:32 erouault Exp $ * * Project: Shapelib * Purpose: Default implementation of file io based on stdio. @@ -8,74 +7,19 @@ ****************************************************************************** * Copyright (c) 2007, Frank Warmerdam * - * This software is available under the following "MIT Style" license, - * or at the option of the licensee under the LGPL (see COPYING). This - * option is discussed in more detail in shapelib.html. - * - * -- - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. + * SPDX-License-Identifier: MIT OR LGPL-2.0-or-later ****************************************************************************** * - * $Log: safileio.c,v $ - * Revision 1.6 2018-06-15 19:56:32 erouault - * * safileio.c: remove duplicate test. Patch by Jaroslav Fojtik. - * Fixes http://bugzilla.maptools.org/show_bug.cgi?id=2744 - * - * Revision 1.5 2016-12-05 12:44:05 erouault - * * Major overhaul of Makefile build system to use autoconf/automake. - * - * * Warning fixes in contrib/ - * - * Revision 1.4 2008-01-16 20:05:14 bram - * Add file hooks that accept UTF-8 encoded filenames on some platforms. Use - *SASetupUtf8Hooks tosetup the hooks and check SHPAPI_UTF8_HOOKS for its - *availability. Currently, this is only available on the Windows platform that - *decodes the UTF-8 filenames to wide character strings and feeds them to - *_wfopen and _wremove. - * - * Revision 1.3 2007/12/18 18:28:11 bram - * - create hook for client specific atof (bugzilla ticket 1615) - * - check for NULL handle before closing cpCPG file, and close after reading. - * - * Revision 1.2 2007/12/15 20:25:30 bram - * dbfopen.c now reads the Code Page information from the DBF file, and exports - * this information as a string through the DBFGetCodePage function. This is - * either the number from the LDID header field ("LDID/") or as the - * content of an accompanying .CPG file. When creating a DBF file, the code can - * be set using DBFCreateEx. - * - * Revision 1.1 2007/12/06 06:56:41 fwarmerdam - * new - * */ #include "shapefil.h" +#include #include #include -#include +#include #include #include -#include - -SHP_CVSID("$Id: safileio.c,v 1.6 2018-06-15 19:56:32 erouault Exp $") #ifdef SHPAPI_UTF8_HOOKS #ifdef SHPAPI_WINDOWS @@ -86,102 +30,64 @@ SHP_CVSID("$Id: safileio.c,v 1.6 2018-06-15 19:56:32 erouault Exp $") #endif #endif -/************************************************************************/ -/* SADFOpen() */ -/************************************************************************/ - -SAFile SADFOpen(const char *pszFilename, const char *pszAccess) - +static SAFile SADFOpen(const char *pszFilename, const char *pszAccess, + void *pvUserData) { + (void)pvUserData; return (SAFile)fopen(pszFilename, pszAccess); } -/************************************************************************/ -/* SADFRead() */ -/************************************************************************/ - -SAOffset SADFRead(void *p, SAOffset size, SAOffset nmemb, SAFile file) - +static SAOffset SADFRead(void *p, SAOffset size, SAOffset nmemb, SAFile file) { return (SAOffset)fread(p, (size_t)size, (size_t)nmemb, (FILE *)file); } -/************************************************************************/ -/* SADFWrite() */ -/************************************************************************/ - -SAOffset SADFWrite(void *p, SAOffset size, SAOffset nmemb, SAFile file) - +static SAOffset SADFWrite(const void *p, SAOffset size, SAOffset nmemb, + SAFile file) { return (SAOffset)fwrite(p, (size_t)size, (size_t)nmemb, (FILE *)file); } -/************************************************************************/ -/* SADFSeek() */ -/************************************************************************/ - -SAOffset SADFSeek(SAFile file, SAOffset offset, int whence) - +static SAOffset SADFSeek(SAFile file, SAOffset offset, int whence) { +#if defined(_MSC_VER) && _MSC_VER >= 1400 + return (SAOffset)_fseeki64((FILE *)file, (__int64)offset, whence); +#else return (SAOffset)fseek((FILE *)file, (long)offset, whence); +#endif } -/************************************************************************/ -/* SADFTell() */ -/************************************************************************/ - -SAOffset SADFTell(SAFile file) - +static SAOffset SADFTell(SAFile file) { +#if defined(_MSC_VER) && _MSC_VER >= 1400 + return (SAOffset)_ftelli64((FILE *)file); +#else return (SAOffset)ftell((FILE *)file); +#endif } -/************************************************************************/ -/* SADFFlush() */ -/************************************************************************/ - -int SADFFlush(SAFile file) - +static int SADFFlush(SAFile file) { return fflush((FILE *)file); } -/************************************************************************/ -/* SADFClose() */ -/************************************************************************/ - -int SADFClose(SAFile file) - +static int SADFClose(SAFile file) { return fclose((FILE *)file); } -/************************************************************************/ -/* SADFClose() */ -/************************************************************************/ - -int SADRemove(const char *filename) - +static int SADRemove(const char *filename, void *pvUserData) { + (void)pvUserData; return remove(filename); } -/************************************************************************/ -/* SADError() */ -/************************************************************************/ - -void SADError(const char *message) - +static void SADError(const char *message) { fprintf(stderr, "%s\n", message); } -/************************************************************************/ -/* SASetupDefaultHooks() */ -/************************************************************************/ - void SASetupDefaultHooks(SAHooks *psHooks) - { psHooks->FOpen = SADFOpen; psHooks->FRead = SADFRead; @@ -194,25 +100,20 @@ void SASetupDefaultHooks(SAHooks *psHooks) psHooks->Error = SADError; psHooks->Atof = atof; + psHooks->pvUserData = NULL; } #ifdef SHPAPI_WINDOWS -/************************************************************************/ -/* Utf8ToWideChar */ -/************************************************************************/ - -const wchar_t *Utf8ToWideChar(const char *pszFilename) +static wchar_t *Utf8ToWideChar(const char *pszFilename) { - int nMulti, nWide; - wchar_t *pwszFileName; - - nMulti = strlen(pszFilename) + 1; - nWide = MultiByteToWideChar(CP_UTF8, 0, pszFilename, nMulti, 0, 0); + const int nMulti = (int)strlen(pszFilename) + 1; + const int nWide = + MultiByteToWideChar(CP_UTF8, 0, pszFilename, nMulti, 0, 0); if (nWide == 0) { return NULL; } - pwszFileName = (wchar_t *)malloc(nWide * sizeof(wchar_t)); + wchar_t *pwszFileName = (wchar_t *)malloc(nWide * sizeof(wchar_t)); if (pwszFileName == NULL) { return NULL; } @@ -228,51 +129,44 @@ const wchar_t *Utf8ToWideChar(const char *pszFilename) /* SAUtf8WFOpen */ /************************************************************************/ -SAFile SAUtf8WFOpen(const char *pszFilename, const char *pszAccess) +static SAFile SAUtf8WFOpen(const char *pszFilename, const char *pszAccess, + void *pvUserData) { + (void)pvUserData; SAFile file = NULL; - const wchar_t *pwszFileName, *pwszAccess; - pwszFileName = Utf8ToWideChar(pszFilename); - pwszAccess = Utf8ToWideChar(pszAccess); + wchar_t *pwszFileName = Utf8ToWideChar(pszFilename); + wchar_t *pwszAccess = Utf8ToWideChar(pszAccess); if (pwszFileName != NULL && pwszAccess != NULL) { file = (SAFile)_wfopen(pwszFileName, pwszAccess); } - free((wchar_t *)pwszFileName); - free((wchar_t *)pwszAccess); + free(pwszFileName); + free(pwszAccess); return file; } -/************************************************************************/ -/* SAUtf8WRemove() */ -/************************************************************************/ - -int SAUtf8WRemove(const char *pszFilename) +static int SAUtf8WRemove(const char *pszFilename, void *pvUserData) { - const wchar_t *pwszFileName = Utf8ToWideChar(pszFilename); + (void)pvUserData; + wchar_t *pwszFileName = Utf8ToWideChar(pszFilename); int rc = -1; if (pwszFileName != NULL) { rc = _wremove(pwszFileName); } - free((wchar_t *)pwszFileName); + free(pwszFileName); return rc; } #endif #ifdef SHPAPI_UTF8_HOOKS - -/************************************************************************/ -/* SASetupUtf8Hooks() */ -/************************************************************************/ +#ifndef SHPAPI_WINDOWS +#error "no implementations of UTF-8 hooks available for this platform" +#endif void SASetupUtf8Hooks(SAHooks *psHooks) { -#ifdef SHPAPI_WINDOWS psHooks->FOpen = SAUtf8WFOpen; psHooks->Remove = SAUtf8WRemove; -#else -#error "no implementations of UTF-8 hooks available for this platform" -#endif psHooks->FRead = SADFRead; psHooks->FWrite = SADFWrite; psHooks->FSeek = SADFSeek; @@ -283,5 +177,4 @@ void SASetupUtf8Hooks(SAHooks *psHooks) psHooks->Error = SADError; psHooks->Atof = atof; } - #endif diff --git a/lib/external/shapelib/shapefil.h b/lib/external/shapelib/shapefil.h index cd4ef54aa56..c11632fa6ba 100644 --- a/lib/external/shapelib/shapefil.h +++ b/lib/external/shapelib/shapefil.h @@ -2,7 +2,6 @@ #define SHAPEFILE_H_INCLUDED /****************************************************************************** - * $Id$ * * Project: Shapelib * Purpose: Primary include file for Shapelib. @@ -12,29 +11,7 @@ * Copyright (c) 1999, Frank Warmerdam * Copyright (c) 2012-2016, Even Rouault * - * This software is available under the following "MIT Style" license, - * or at the option of the licensee under the LGPL (see COPYING). This - * option is discussed in more detail in shapelib.html. - * - * -- - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. + * SPDX-License-Identifier: MIT OR LGPL-2.0-or-later ****************************************************************************** * */ @@ -49,6 +26,26 @@ extern "C" { #endif +/************************************************************************/ +/* Version related macros (added in 1.6.0) */ +/************************************************************************/ + +#define SHAPELIB_VERSION_MAJOR 1 +#define SHAPELIB_VERSION_MINOR 6 +#define SHAPELIB_VERSION_MICRO 0 + +#define SHAPELIB_MAKE_VERSION_NUMBER(major, minor, micro) \ + ((major) * 10000 + (minor) * 100 + (micro)) + +#define SHAPELIB_VERSION_NUMBER \ + SHAPELIB_MAKE_VERSION_NUMBER(SHAPELIB_VERSION_MAJOR, \ + SHAPELIB_VERSION_MINOR, \ + SHAPELIB_VERSION_MICRO) + +#define SHAPELIB_AT_LEAST(major, minor, micro) \ + (SHAPELIB_VERSION_NUMBER >= \ + SHAPELIB_MAKE_VERSION_NUMBER(major, minor, micro)) + /************************************************************************/ /* Configuration options. */ /************************************************************************/ @@ -73,7 +70,7 @@ extern "C" { /* various calling conventions on the Shapelib API. */ /* */ /* To force __stdcall conventions (needed to call Shapelib */ -/* from Visual Basic and/or Dephi I believe) the makefile could */ +/* from Visual Basic and/or Delphi I believe) the makefile could */ /* be modified to define: */ /* */ /* /DSHPAPI_CALL=__stdcall */ @@ -113,31 +110,11 @@ extern "C" { #define SHPAPI_CALL1(x) x SHPAPI_CALL #endif -/* -------------------------------------------------------------------- */ -/* Macros for controlling CVSID and ensuring they don't appear */ -/* as unreferenced variables resulting in lots of warnings. */ -/* -------------------------------------------------------------------- */ -#ifndef DISABLE_CVSID -#if defined(__GNUC__) && __GNUC__ >= 4 -#define SHP_CVSID(string) \ - static const char cpl_cvsid[] __attribute__((used)) = string; -#else -#define SHP_CVSID(string) \ - static const char cpl_cvsid[] = string; \ - static const char *cvsid_aw() \ - { \ - return (cvsid_aw() ? NULL : cpl_cvsid); \ - } -#endif -#else -#define SHP_CVSID(string) -#endif - /* -------------------------------------------------------------------- */ /* On some platforms, additional file IO hooks are defined that */ /* UTF-8 encoded filenames Unicode filenames */ /* -------------------------------------------------------------------- */ -#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) +#if defined(_WIN32) #define SHPAPI_WINDOWS #define SHPAPI_UTF8_HOOKS #endif @@ -148,21 +125,27 @@ extern "C" { typedef int *SAFile; #ifndef SAOffset +#if defined(_MSC_VER) && _MSC_VER >= 1400 +typedef unsigned __int64 SAOffset; +#else typedef unsigned long SAOffset; #endif +#endif typedef struct { - SAFile (*FOpen)(const char *filename, const char *access); + SAFile (*FOpen)(const char *filename, const char *access, void *pvUserData); SAOffset (*FRead)(void *p, SAOffset size, SAOffset nmemb, SAFile file); - SAOffset (*FWrite)(void *p, SAOffset size, SAOffset nmemb, SAFile file); + SAOffset (*FWrite)(const void *p, SAOffset size, SAOffset nmemb, + SAFile file); SAOffset (*FSeek)(SAFile file, SAOffset offset, int whence); SAOffset (*FTell)(SAFile file); int (*FFlush)(SAFile file); int (*FClose)(SAFile file); - int (*Remove)(const char *filename); + int (*Remove)(const char *filename, void *pvUserData); void (*Error)(const char *message); double (*Atof)(const char *str); + void *pvUserData; } SAHooks; void SHPAPI_CALL SASetupDefaultHooks(SAHooks *psHooks); @@ -206,6 +189,12 @@ typedef struct { typedef SHPInfo *SHPHandle; +typedef struct { + int year; + int month; + int day; +} SHPDate; + /* -------------------------------------------------------------------- */ /* Shape types (nSHPType) */ /* -------------------------------------------------------------------- */ @@ -277,13 +266,13 @@ struct tagSHPObject { /* will be NULL as it is not necessary to keep the SHX file open */ SHPHandle SHPAPI_CALL SHPOpen(const char *pszShapeFile, const char *pszAccess); SHPHandle SHPAPI_CALL SHPOpenLL(const char *pszShapeFile, const char *pszAccess, - SAHooks *psHooks); + const SAHooks *psHooks); SHPHandle SHPAPI_CALL SHPOpenLLEx(const char *pszShapeFile, - const char *pszAccess, SAHooks *psHooks, + const char *pszAccess, const SAHooks *psHooks, int bRestoreSHX); int SHPAPI_CALL SHPRestoreSHX(const char *pszShapeFile, const char *pszAccess, - SAHooks *psHooks); + const SAHooks *psHooks); /* If setting bFastMode = TRUE, the content of SHPReadObject() is owned by the * SHPHandle. */ @@ -296,12 +285,14 @@ void SHPAPI_CALL SHPSetFastModeReadObject(SHPHandle hSHP, int bFastMode); SHPHandle SHPAPI_CALL SHPCreate(const char *pszShapeFile, int nShapeType); SHPHandle SHPAPI_CALL SHPCreateLL(const char *pszShapeFile, int nShapeType, - SAHooks *psHooks); -void SHPAPI_CALL SHPGetInfo(SHPHandle hSHP, int *pnEntities, int *pnShapeType, - double *padfMinBound, double *padfMaxBound); + const SAHooks *psHooks); +void SHPAPI_CALL SHPGetInfo(const SHPHandle hSHP, int *pnEntities, + int *pnShapeType, double *padfMinBound, + double *padfMaxBound); -SHPObject SHPAPI_CALL1(*) SHPReadObject(SHPHandle hSHP, int iShape); -int SHPAPI_CALL SHPWriteObject(SHPHandle hSHP, int iShape, SHPObject *psObject); +SHPObject SHPAPI_CALL1(*) SHPReadObject(const SHPHandle hSHP, int iShape); +int SHPAPI_CALL SHPWriteObject(SHPHandle hSHP, int iShape, + const SHPObject *psObject); void SHPAPI_CALL SHPDestroyObject(SHPObject *psObject); void SHPAPI_CALL SHPComputeExtents(SHPObject *psObject); @@ -314,7 +305,7 @@ SHPObject SHPAPI_CALL1(*) SHPCreateSimpleObject(int nSHPType, int nVertices, const double *padfX, const double *padfY, const double *padfZ); -int SHPAPI_CALL SHPRewindObject(SHPHandle hSHP, SHPObject *psObject); +int SHPAPI_CALL SHPRewindObject(const SHPHandle hSHP, SHPObject *psObject); void SHPAPI_CALL SHPClose(SHPHandle hSHP); void SHPAPI_CALL SHPWriteHeader(SHPHandle hSHP); @@ -360,21 +351,20 @@ typedef struct { SHPTree SHPAPI_CALL1(*) SHPCreateTree(SHPHandle hSHP, int nDimension, int nMaxDepth, - double *padfBoundsMin, double *padfBoundsMax); + const double *padfBoundsMin, const double *padfBoundsMax); void SHPAPI_CALL SHPDestroyTree(SHPTree *hTree); int SHPAPI_CALL SHPWriteTree(SHPTree *hTree, const char *pszFilename); int SHPAPI_CALL SHPTreeAddShapeId(SHPTree *hTree, SHPObject *psObject); -int SHPAPI_CALL SHPTreeRemoveShapeId(SHPTree *hTree, int nShapeId); void SHPAPI_CALL SHPTreeTrimExtraNodes(SHPTree *hTree); int SHPAPI_CALL1(*) - SHPTreeFindLikelyShapes(SHPTree *hTree, double *padfBoundsMin, + SHPTreeFindLikelyShapes(const SHPTree *hTree, double *padfBoundsMin, double *padfBoundsMax, int *); -int SHPAPI_CALL SHPCheckBoundsOverlap(double *, double *, double *, double *, - int); +int SHPAPI_CALL SHPCheckBoundsOverlap(const double *, const double *, + const double *, const double *, int); int SHPAPI_CALL1(*) SHPSearchDiskTree(FILE *fp, double *padfBoundsMin, double *padfBoundsMax, int *pnShapeCount); @@ -382,16 +372,17 @@ int SHPAPI_CALL1(*) SHPSearchDiskTree(FILE *fp, double *padfBoundsMin, typedef struct SHPDiskTreeInfo *SHPTreeDiskHandle; SHPTreeDiskHandle SHPAPI_CALL SHPOpenDiskTree(const char *pszQIXFilename, - SAHooks *psHooks); + const SAHooks *psHooks); void SHPAPI_CALL SHPCloseDiskTree(SHPTreeDiskHandle hDiskTree); int SHPAPI_CALL1(*) - SHPSearchDiskTreeEx(SHPTreeDiskHandle hDiskTree, double *padfBoundsMin, - double *padfBoundsMax, int *pnShapeCount); + SHPSearchDiskTreeEx(const SHPTreeDiskHandle hDiskTree, + double *padfBoundsMin, double *padfBoundsMax, + int *pnShapeCount); int SHPAPI_CALL SHPWriteTreeLL(SHPTree *hTree, const char *pszFilename, - SAHooks *psHooks); + const SAHooks *psHooks); /* -------------------------------------------------------------------- */ /* SBN Search API */ @@ -400,16 +391,16 @@ int SHPAPI_CALL SHPWriteTreeLL(SHPTree *hTree, const char *pszFilename, typedef struct SBNSearchInfo *SBNSearchHandle; SBNSearchHandle SHPAPI_CALL SBNOpenDiskTree(const char *pszSBNFilename, - SAHooks *psHooks); + const SAHooks *psHooks); void SHPAPI_CALL SBNCloseDiskTree(SBNSearchHandle hSBN); int SHPAPI_CALL1(*) - SBNSearchDiskTree(SBNSearchHandle hSBN, double *padfBoundsMin, - double *padfBoundsMax, int *pnShapeCount); + SBNSearchDiskTree(const SBNSearchHandle hSBN, const double *padfBoundsMin, + const double *padfBoundsMax, int *pnShapeCount); int SHPAPI_CALL1(*) - SBNSearchDiskTreeInteger(SBNSearchHandle hSBN, int bMinX, int bMinY, + SBNSearchDiskTreeInteger(const SBNSearchHandle hSBN, int bMinX, int bMinY, int bMaxX, int bMaxY, int *pnShapeCount); void SHPAPI_CALL SBNSearchFreeIds(int *panShapeId); @@ -485,15 +476,16 @@ typedef enum { DBFHandle SHPAPI_CALL DBFOpen(const char *pszDBFFile, const char *pszAccess); DBFHandle SHPAPI_CALL DBFOpenLL(const char *pszDBFFile, const char *pszAccess, - SAHooks *psHooks); + const SAHooks *psHooks); DBFHandle SHPAPI_CALL DBFCreate(const char *pszDBFFile); DBFHandle SHPAPI_CALL DBFCreateEx(const char *pszDBFFile, const char *pszCodePage); DBFHandle SHPAPI_CALL DBFCreateLL(const char *pszDBFFile, - const char *pszCodePage, SAHooks *psHooks); + const char *pszCodePage, + const SAHooks *psHooks); -int SHPAPI_CALL DBFGetFieldCount(DBFHandle psDBF); -int SHPAPI_CALL DBFGetRecordCount(DBFHandle psDBF); +int SHPAPI_CALL DBFGetFieldCount(const DBFHandle psDBF); +int SHPAPI_CALL DBFGetRecordCount(const DBFHandle psDBF); int SHPAPI_CALL DBFAddField(DBFHandle hDBF, const char *pszFieldName, DBFFieldType eType, int nWidth, int nDecimals); @@ -502,17 +494,18 @@ int SHPAPI_CALL DBFAddNativeFieldType(DBFHandle hDBF, const char *pszFieldName, int SHPAPI_CALL DBFDeleteField(DBFHandle hDBF, int iField); -int SHPAPI_CALL DBFReorderFields(DBFHandle psDBF, int *panMap); +int SHPAPI_CALL DBFReorderFields(DBFHandle psDBF, const int *panMap); int SHPAPI_CALL DBFAlterFieldDefn(DBFHandle psDBF, int iField, const char *pszFieldName, char chType, int nWidth, int nDecimals); -DBFFieldType SHPAPI_CALL DBFGetFieldInfo(DBFHandle psDBF, int iField, +DBFFieldType SHPAPI_CALL DBFGetFieldInfo(const DBFHandle psDBF, int iField, char *pszFieldName, int *pnWidth, int *pnDecimals); -int SHPAPI_CALL DBFGetFieldIndex(DBFHandle psDBF, const char *pszFieldName); +int SHPAPI_CALL DBFGetFieldIndex(const DBFHandle psDBF, + const char *pszFieldName); int SHPAPI_CALL DBFReadIntegerAttribute(DBFHandle hDBF, int iShape, int iField); double SHPAPI_CALL DBFReadDoubleAttribute(DBFHandle hDBF, int iShape, @@ -521,7 +514,10 @@ const char SHPAPI_CALL1(*) DBFReadStringAttribute(DBFHandle hDBF, int iShape, int iField); const char SHPAPI_CALL1(*) DBFReadLogicalAttribute(DBFHandle hDBF, int iShape, int iField); -int SHPAPI_CALL DBFIsAttributeNULL(DBFHandle hDBF, int iShape, int iField); +SHPDate SHPAPI_CALL DBFReadDateAttribute(DBFHandle hDBF, int iShape, + int iField); +int SHPAPI_CALL DBFIsAttributeNULL(const DBFHandle hDBF, int iShape, + int iField); int SHPAPI_CALL DBFWriteIntegerAttribute(DBFHandle hDBF, int iShape, int iField, int nFieldValue); @@ -533,22 +529,26 @@ int SHPAPI_CALL DBFWriteNULLAttribute(DBFHandle hDBF, int iShape, int iField); int SHPAPI_CALL DBFWriteLogicalAttribute(DBFHandle hDBF, int iShape, int iField, const char lFieldValue); +int SHPAPI_CALL DBFWriteDateAttribute(DBFHandle hDBF, int iShape, int iField, + const SHPDate *dateFieldValue); int SHPAPI_CALL DBFWriteAttributeDirectly(DBFHandle psDBF, int hEntity, - int iField, void *pValue); + int iField, const void *pValue); const char SHPAPI_CALL1(*) DBFReadTuple(DBFHandle psDBF, int hEntity); -int SHPAPI_CALL DBFWriteTuple(DBFHandle psDBF, int hEntity, void *pRawTuple); +int SHPAPI_CALL DBFWriteTuple(DBFHandle psDBF, int hEntity, + const void *pRawTuple); -int SHPAPI_CALL DBFIsRecordDeleted(DBFHandle psDBF, int iShape); +int SHPAPI_CALL DBFIsRecordDeleted(const DBFHandle psDBF, int iShape); int SHPAPI_CALL DBFMarkRecordDeleted(DBFHandle psDBF, int iShape, int bIsDeleted); -DBFHandle SHPAPI_CALL DBFCloneEmpty(DBFHandle psDBF, const char *pszFilename); +DBFHandle SHPAPI_CALL DBFCloneEmpty(const DBFHandle psDBF, + const char *pszFilename); void SHPAPI_CALL DBFClose(DBFHandle hDBF); void SHPAPI_CALL DBFUpdateHeader(DBFHandle hDBF); -char SHPAPI_CALL DBFGetNativeFieldType(DBFHandle hDBF, int iField); +char SHPAPI_CALL DBFGetNativeFieldType(const DBFHandle hDBF, int iField); -const char SHPAPI_CALL1(*) DBFGetCodePage(DBFHandle psDBF); +const char SHPAPI_CALL1(*) DBFGetCodePage(const DBFHandle psDBF); void SHPAPI_CALL DBFSetLastModifiedDate(DBFHandle psDBF, int nYYSince1900, int nMM, int nDD); diff --git a/lib/external/shapelib/shapefil_private.h b/lib/external/shapelib/shapefil_private.h new file mode 100644 index 00000000000..1bed49e7cce --- /dev/null +++ b/lib/external/shapelib/shapefil_private.h @@ -0,0 +1,115 @@ +#ifndef SHAPEFILE_PRIVATE_H_INCLUDED +#define SHAPEFILE_PRIVATE_H_INCLUDED + +/****************************************************************************** + * + * Project: Shapelib + * Purpose: Private include file for Shapelib. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2012-2016, Even Rouault + * + * SPDX-License-Identifier: MIT OR LGPL-2.0-or-later + ****************************************************************************** + * + */ + +#ifdef __cplusplus +#define STATIC_CAST(type, x) static_cast(x) +#define REINTERPRET_CAST(type, x) reinterpret_cast(x) +#define CONST_CAST(type, x) const_cast(x) +#define SHPLIB_NULLPTR nullptr +#else +#define STATIC_CAST(type, x) ((type)(x)) +#define REINTERPRET_CAST(type, x) ((type)(x)) +#define CONST_CAST(type, x) ((type)(x)) +#define SHPLIB_NULLPTR NULL +#endif + +#if !defined(SHP_BIG_ENDIAN) +#if defined(CPL_MSB) +#define SHP_BIG_ENDIAN 1 +#elif (defined(__GNUC__) && __GNUC__ >= 5) || \ + (defined(__GNUC__) && defined(__GNUC_MINOR__) && __GNUC__ == 4 && \ + __GNUC_MINOR__ >= 6) +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +#define SHP_BIG_ENDIAN 1 +#endif +#elif defined(__GLIBC__) +#if __BYTE_ORDER == __BIG_ENDIAN +#define SHP_BIG_ENDIAN 1 +#endif +#elif defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN) +#define SHP_BIG_ENDIAN 1 +#elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN) +#elif defined(__sparc) || defined(__sparc__) || defined(_POWER) || \ + defined(__powerpc__) || defined(__ppc__) || defined(__hpux) || \ + defined(_MIPSEB) || defined(_POWER) || defined(__s390__) +#define SHP_BIG_ENDIAN 1 +#endif +#endif + +#include "shapefil.h" +#include +#include + +/************************************************************************/ +/* Little endian <==> big endian byte swap macros. */ +/************************************************************************/ + +#if (defined(__GNUC__) && __GNUC__ >= 5) || \ + (defined(__GNUC__) && defined(__GNUC_MINOR__) && __GNUC__ == 4 && \ + __GNUC_MINOR__ >= 8) +#define _SHP_SWAP32(x) \ + STATIC_CAST(uint32_t, __builtin_bswap32(STATIC_CAST(uint32_t, x))) +#define _SHP_SWAP64(x) \ + STATIC_CAST(uint64_t, __builtin_bswap64(STATIC_CAST(uint64_t, x))) +#elif defined(_MSC_VER) +#define _SHP_SWAP32(x) \ + STATIC_CAST(uint32_t, _byteswap_ulong(STATIC_CAST(uint32_t, x))) +#define _SHP_SWAP64(x) \ + STATIC_CAST(uint64_t, _byteswap_uint64(STATIC_CAST(uint64_t, x))) +#else +#define _SHP_SWAP32(x) \ + STATIC_CAST(uint32_t, \ + ((STATIC_CAST(uint32_t, x) & 0x000000ffU) << 24) | \ + ((STATIC_CAST(uint32_t, x) & 0x0000ff00U) << 8) | \ + ((STATIC_CAST(uint32_t, x) & 0x00ff0000U) >> 8) | \ + ((STATIC_CAST(uint32_t, x) & 0xff000000U) >> 24)) +#define _SHP_SWAP64(x) \ + ((STATIC_CAST(uint64_t, _SHP_SWAP32(STATIC_CAST(uint32_t, x))) << 32) | \ + (STATIC_CAST(uint64_t, _SHP_SWAP32(STATIC_CAST( \ + uint32_t, STATIC_CAST(uint64_t, x) >> 32))))) + +#endif + +/* in-place uint32_t* swap */ +#define SHP_SWAP32(p) \ + *REINTERPRET_CAST(uint32_t *, p) = \ + _SHP_SWAP32(*REINTERPRET_CAST(uint32_t *, p)) +/* in-place uint64_t* swap */ +#define SHP_SWAP64(p) \ + *REINTERPRET_CAST(uint64_t *, p) = \ + _SHP_SWAP64(*REINTERPRET_CAST(uint64_t *, p)) +/* in-place double* swap */ +#define SHP_SWAPDOUBLE(x) \ + do { \ + uint64_t _n64; \ + void *_lx = x; \ + memcpy(&_n64, _lx, 8); \ + _n64 = _SHP_SWAP64(_n64); \ + memcpy(_lx, &_n64, 8); \ + } while (0) +/* copy double* swap*/ +#define SHP_SWAPDOUBLE_CPY(dst, src) \ + do { \ + uint64_t _n64; \ + const void *_ls = src; \ + void *_ld = dst; \ + memcpy(&_n64, _ls, 8); \ + _n64 = _SHP_SWAP64(_n64); \ + memcpy(_ld, &_n64, 8); \ + } while (0) +#endif /* ndef SHAPEFILE_PRIVATE_H_INCLUDED */ diff --git a/lib/external/shapelib/shpopen.c b/lib/external/shapelib/shpopen.c index e63cf768d25..b2eeb3614fb 100644 --- a/lib/external/shapelib/shpopen.c +++ b/lib/external/shapelib/shpopen.c @@ -8,52 +8,21 @@ * Copyright (c) 1999, 2001, Frank Warmerdam * Copyright (c) 2011-2019, Even Rouault * - * This software is available under the following "MIT Style" license, - * or at the option of the licensee under the LGPL (see COPYING). This - * option is discussed in more detail in shapelib.html. - * - * -- - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. + * SPDX-License-Identifier: MIT OR LGPL-2.0-or-later ******************************************************************************/ -#include "shapefil.h" +#include "shapefil_private.h" #include #include #include #include #include +#include #include #include #include -SHP_CVSID("$Id$") - -typedef unsigned char uchar; - -#if UINT_MAX == 65535 -typedef unsigned long int32; -#else -typedef unsigned int int32; -#endif - #ifndef FALSE #define FALSE 0 #define TRUE 1 @@ -70,73 +39,18 @@ typedef unsigned int int32; #if _MSC_VER < 1900 #define snprintf _snprintf #endif -#elif defined(WIN32) || defined(_WIN32) +#elif defined(_WIN32) #ifndef snprintf #define snprintf _snprintf #endif #endif #endif -#ifndef CPL_UNUSED -#if defined(__GNUC__) && __GNUC__ >= 4 -#define CPL_UNUSED __attribute((__unused__)) -#else -#define CPL_UNUSED -#endif -#endif - -#if defined(CPL_LSB) -#define bBigEndian false -#elif defined(CPL_MSB) -#define bBigEndian true -#else -static bool bBigEndian; -#endif - -#ifdef __cplusplus -#define STATIC_CAST(type, x) static_cast(x) -#define SHPLIB_NULLPTR nullptr -#else -#define STATIC_CAST(type, x) ((type)(x)) -#define SHPLIB_NULLPTR NULL -#endif - -/************************************************************************/ -/* SwapWord() */ -/* */ -/* Swap a 2, 4 or 8 byte word. */ -/************************************************************************/ - -static void SwapWord(int length, void *wordP) -{ - for (int i = 0; i < length / 2; i++) { - const uchar temp = STATIC_CAST(uchar *, wordP)[i]; - STATIC_CAST(uchar *, wordP) - [i] = STATIC_CAST(uchar *, wordP)[length - i - 1]; - STATIC_CAST(uchar *, wordP)[length - i - 1] = temp; - } -} - -/************************************************************************/ -/* SfRealloc() */ -/* */ -/* A realloc cover function that will access a NULL pointer as */ -/* a valid input. */ -/************************************************************************/ - -static void *SfRealloc(void *pMem, int nNewSize) -{ - if (pMem == SHPLIB_NULLPTR) - return malloc(nNewSize); - else - return realloc(pMem, nNewSize); -} - /************************************************************************/ /* SHPWriteHeader() */ /* */ -/* Write out a header for the .shp and .shx files as well as the */ -/* contents of the index (.shx) file. */ +/* Write out a header for the .shp and .shx files as well as the */ +/* contents of the index (.shx) file. */ /************************************************************************/ void SHPAPI_CALL SHPWriteHeader(SHPHandle psSHP) @@ -150,64 +64,73 @@ void SHPAPI_CALL SHPWriteHeader(SHPHandle psSHP) /* Prepare header block for .shp file. */ /* -------------------------------------------------------------------- */ - uchar abyHeader[100] = {0}; + unsigned char abyHeader[100] = {0}; abyHeader[2] = 0x27; /* magic cookie */ abyHeader[3] = 0x0a; - int32 i32 = psSHP->nFileSize / 2; /* file size */ + uint32_t i32 = psSHP->nFileSize / 2; /* file size */ ByteCopy(&i32, abyHeader + 24, 4); - if (!bBigEndian) - SwapWord(4, abyHeader + 24); +#if !defined(SHP_BIG_ENDIAN) + SHP_SWAP32(abyHeader + 24); +#endif i32 = 1000; /* version */ ByteCopy(&i32, abyHeader + 28, 4); - if (bBigEndian) - SwapWord(4, abyHeader + 28); +#if defined(SHP_BIG_ENDIAN) + SHP_SWAP32(abyHeader + 28); +#endif i32 = psSHP->nShapeType; /* shape type */ ByteCopy(&i32, abyHeader + 32, 4); - if (bBigEndian) - SwapWord(4, abyHeader + 32); +#if defined(SHP_BIG_ENDIAN) + SHP_SWAP32(abyHeader + 32); +#endif double dValue = psSHP->adBoundsMin[0]; /* set bounds */ ByteCopy(&dValue, abyHeader + 36, 8); - if (bBigEndian) - SwapWord(8, abyHeader + 36); - +#if defined(SHP_BIG_ENDIAN) + SHP_SWAP64(abyHeader + 36); +#endif dValue = psSHP->adBoundsMin[1]; ByteCopy(&dValue, abyHeader + 44, 8); - if (bBigEndian) - SwapWord(8, abyHeader + 44); - +#if defined(SHP_BIG_ENDIAN) + SHP_SWAP64(abyHeader + 44); +#endif dValue = psSHP->adBoundsMax[0]; ByteCopy(&dValue, abyHeader + 52, 8); - if (bBigEndian) - SwapWord(8, abyHeader + 52); +#if defined(SHP_BIG_ENDIAN) + SHP_SWAP64(abyHeader + 52); +#endif dValue = psSHP->adBoundsMax[1]; ByteCopy(&dValue, abyHeader + 60, 8); - if (bBigEndian) - SwapWord(8, abyHeader + 60); +#if defined(SHP_BIG_ENDIAN) + SHP_SWAP64(abyHeader + 60); +#endif dValue = psSHP->adBoundsMin[2]; /* z */ ByteCopy(&dValue, abyHeader + 68, 8); - if (bBigEndian) - SwapWord(8, abyHeader + 68); +#if defined(SHP_BIG_ENDIAN) + SHP_SWAP64(abyHeader + 68); +#endif dValue = psSHP->adBoundsMax[2]; ByteCopy(&dValue, abyHeader + 76, 8); - if (bBigEndian) - SwapWord(8, abyHeader + 76); +#if defined(SHP_BIG_ENDIAN) + SHP_SWAP64(abyHeader + 76); +#endif dValue = psSHP->adBoundsMin[3]; /* m */ ByteCopy(&dValue, abyHeader + 84, 8); - if (bBigEndian) - SwapWord(8, abyHeader + 84); +#if defined(SHP_BIG_ENDIAN) + SHP_SWAP64(abyHeader + 84); +#endif dValue = psSHP->adBoundsMax[3]; ByteCopy(&dValue, abyHeader + 92, 8); - if (bBigEndian) - SwapWord(8, abyHeader + 92); +#if defined(SHP_BIG_ENDIAN) + SHP_SWAP64(abyHeader + 92); +#endif /* -------------------------------------------------------------------- */ /* Write .shp file header. */ @@ -226,10 +149,11 @@ void SHPAPI_CALL SHPWriteHeader(SHPHandle psSHP) /* -------------------------------------------------------------------- */ /* Prepare, and write .shx file header. */ /* -------------------------------------------------------------------- */ - i32 = (psSHP->nRecords * 2 * sizeof(int32) + 100) / 2; /* file size */ + i32 = (psSHP->nRecords * 2 * sizeof(uint32_t) + 100) / 2; /* file size */ ByteCopy(&i32, abyHeader + 24, 4); - if (!bBigEndian) - SwapWord(4, abyHeader + 24); +#if !defined(SHP_BIG_ENDIAN) + SHP_SWAP32(abyHeader + 24); +#endif if (psSHP->sHooks.FSeek(psSHP->fpSHX, 0, 0) != 0 || psSHP->sHooks.FWrite(abyHeader, 100, 1, psSHP->fpSHX) != 1) { @@ -246,8 +170,8 @@ void SHPAPI_CALL SHPWriteHeader(SHPHandle psSHP) /* -------------------------------------------------------------------- */ /* Write out the .shx contents. */ /* -------------------------------------------------------------------- */ - int32 *panSHX = - STATIC_CAST(int32 *, malloc(sizeof(int32) * 2 * psSHP->nRecords)); + uint32_t *panSHX = + STATIC_CAST(uint32_t *, malloc(sizeof(uint32_t) * 2 * psSHP->nRecords)); if (panSHX == SHPLIB_NULLPTR) { psSHP->sHooks.Error("Failure allocatin panSHX"); return; @@ -256,13 +180,13 @@ void SHPAPI_CALL SHPWriteHeader(SHPHandle psSHP) for (int i = 0; i < psSHP->nRecords; i++) { panSHX[i * 2] = psSHP->panRecOffset[i] / 2; panSHX[i * 2 + 1] = psSHP->panRecSize[i] / 2; - if (!bBigEndian) - SwapWord(4, panSHX + i * 2); - if (!bBigEndian) - SwapWord(4, panSHX + i * 2 + 1); +#if !defined(SHP_BIG_ENDIAN) + SHP_SWAP32(panSHX + i * 2); + SHP_SWAP32(panSHX + i * 2 + 1); +#endif } - if (STATIC_CAST(int, psSHP->sHooks.FWrite(panSHX, sizeof(int32) * 2, + if (STATIC_CAST(int, psSHP->sHooks.FWrite(panSHX, sizeof(uint32_t) * 2, psSHP->nRecords, psSHP->fpSHX)) != psSHP->nRecords) { char szErrorMsg[200]; @@ -319,7 +243,7 @@ static int SHPGetLenWithoutExtension(const char *pszBasename) /************************************************************************/ SHPHandle SHPAPI_CALL SHPOpenLL(const char *pszLayer, const char *pszAccess, - SAHooks *psHooks) + const SAHooks *psHooks) { /* -------------------------------------------------------------------- */ /* Ensure the access string is one of the legal ones. We */ @@ -336,23 +260,10 @@ SHPHandle SHPAPI_CALL SHPOpenLL(const char *pszLayer, const char *pszAccess, pszAccess = "rb"; } -/* -------------------------------------------------------------------- */ -/* Establish the byte order on this machine. */ -/* -------------------------------------------------------------------- */ -#if !defined(bBigEndian) - { - int i = 1; - if (*((uchar *)&i) == 1) - bBigEndian = false; - else - bBigEndian = true; - } -#endif - /* -------------------------------------------------------------------- */ /* Initialize the info structure. */ /* -------------------------------------------------------------------- */ - SHPHandle psSHP = STATIC_CAST(SHPHandle, calloc(sizeof(SHPInfo), 1)); + SHPHandle psSHP = STATIC_CAST(SHPHandle, calloc(1, sizeof(SHPInfo))); psSHP->bUpdated = FALSE; memcpy(&(psSHP->sHooks), psHooks, sizeof(SAHooks)); @@ -365,18 +276,21 @@ SHPHandle SHPAPI_CALL SHPOpenLL(const char *pszLayer, const char *pszAccess, char *pszFullname = STATIC_CAST(char *, malloc(nLenWithoutExtension + 5)); memcpy(pszFullname, pszLayer, nLenWithoutExtension); memcpy(pszFullname + nLenWithoutExtension, ".shp", 5); - psSHP->fpSHP = psSHP->sHooks.FOpen(pszFullname, pszAccess); + psSHP->fpSHP = + psSHP->sHooks.FOpen(pszFullname, pszAccess, psSHP->sHooks.pvUserData); if (psSHP->fpSHP == SHPLIB_NULLPTR) { memcpy(pszFullname + nLenWithoutExtension, ".SHP", 5); - psSHP->fpSHP = psSHP->sHooks.FOpen(pszFullname, pszAccess); + psSHP->fpSHP = psSHP->sHooks.FOpen(pszFullname, pszAccess, + psSHP->sHooks.pvUserData); } if (psSHP->fpSHP == SHPLIB_NULLPTR) { const size_t nMessageLen = strlen(pszFullname) * 2 + 256; char *pszMessage = STATIC_CAST(char *, malloc(nMessageLen)); pszFullname[nLenWithoutExtension] = 0; - snprintf(pszMessage, nMessageLen, "Unable to open %s.shp or %s.SHP.", - pszFullname, pszFullname); + snprintf(pszMessage, nMessageLen, + "Unable to open %s.shp or %s.SHP in %s mode.", pszFullname, + pszFullname, pszAccess); psHooks->Error(pszMessage); free(pszMessage); @@ -387,10 +301,12 @@ SHPHandle SHPAPI_CALL SHPOpenLL(const char *pszLayer, const char *pszAccess, } memcpy(pszFullname + nLenWithoutExtension, ".shx", 5); - psSHP->fpSHX = psSHP->sHooks.FOpen(pszFullname, pszAccess); + psSHP->fpSHX = + psSHP->sHooks.FOpen(pszFullname, pszAccess, psSHP->sHooks.pvUserData); if (psSHP->fpSHX == SHPLIB_NULLPTR) { memcpy(pszFullname + nLenWithoutExtension, ".SHX", 5); - psSHP->fpSHX = psSHP->sHooks.FOpen(pszFullname, pszAccess); + psSHP->fpSHX = psSHP->sHooks.FOpen(pszFullname, pszAccess, + psSHP->sHooks.pvUserData); } if (psSHP->fpSHX == SHPLIB_NULLPTR) { @@ -416,7 +332,7 @@ SHPHandle SHPAPI_CALL SHPOpenLL(const char *pszLayer, const char *pszAccess, /* -------------------------------------------------------------------- */ /* Read the file size from the SHP file. */ /* -------------------------------------------------------------------- */ - uchar *pabyBuf = STATIC_CAST(uchar *, malloc(100)); + unsigned char *pabyBuf = STATIC_CAST(unsigned char *, malloc(100)); if (psSHP->sHooks.FRead(pabyBuf, 100, 1, psSHP->fpSHP) != 1) { psSHP->sHooks.Error(".shp file is unreadable, or corrupt."); psSHP->sHooks.FClose(psSHP->fpSHP); @@ -489,43 +405,51 @@ SHPHandle SHPAPI_CALL SHPOpenLL(const char *pszLayer, const char *pszAccess, /* -------------------------------------------------------------------- */ double dValue; - if (bBigEndian) - SwapWord(8, pabyBuf + 36); +#if defined(SHP_BIG_ENDIAN) + SHP_SWAP64(pabyBuf + 36); +#endif memcpy(&dValue, pabyBuf + 36, 8); psSHP->adBoundsMin[0] = dValue; - if (bBigEndian) - SwapWord(8, pabyBuf + 44); +#if defined(SHP_BIG_ENDIAN) + SHP_SWAP64(pabyBuf + 44); +#endif memcpy(&dValue, pabyBuf + 44, 8); psSHP->adBoundsMin[1] = dValue; - if (bBigEndian) - SwapWord(8, pabyBuf + 52); +#if defined(SHP_BIG_ENDIAN) + SHP_SWAP64(pabyBuf + 52); +#endif memcpy(&dValue, pabyBuf + 52, 8); psSHP->adBoundsMax[0] = dValue; - if (bBigEndian) - SwapWord(8, pabyBuf + 60); +#if defined(SHP_BIG_ENDIAN) + SHP_SWAP64(pabyBuf + 60); +#endif memcpy(&dValue, pabyBuf + 60, 8); psSHP->adBoundsMax[1] = dValue; - if (bBigEndian) - SwapWord(8, pabyBuf + 68); /* z */ +#if defined(SHP_BIG_ENDIAN) + SHP_SWAP64(pabyBuf + 68); /* z */ +#endif memcpy(&dValue, pabyBuf + 68, 8); psSHP->adBoundsMin[2] = dValue; - if (bBigEndian) - SwapWord(8, pabyBuf + 76); +#if defined(SHP_BIG_ENDIAN) + SHP_SWAP64(pabyBuf + 76); +#endif memcpy(&dValue, pabyBuf + 76, 8); psSHP->adBoundsMax[2] = dValue; - if (bBigEndian) - SwapWord(8, pabyBuf + 84); /* z */ +#if defined(SHP_BIG_ENDIAN) + SHP_SWAP64(pabyBuf + 84); /* z */ +#endif memcpy(&dValue, pabyBuf + 84, 8); psSHP->adBoundsMin[3] = dValue; - if (bBigEndian) - SwapWord(8, pabyBuf + 92); +#if defined(SHP_BIG_ENDIAN) + SHP_SWAP64(pabyBuf + 92); +#endif memcpy(&dValue, pabyBuf + 92, 8); psSHP->adBoundsMax[3] = dValue; @@ -546,7 +470,8 @@ SHPHandle SHPAPI_CALL SHPOpenLL(const char *pszLayer, const char *pszAccess, if (bLazySHXLoading) pabyBuf = SHPLIB_NULLPTR; else - pabyBuf = STATIC_CAST(uchar *, malloc(8 * MAX(1, psSHP->nRecords))); + pabyBuf = + STATIC_CAST(unsigned char *, malloc(8 * MAX(1, psSHP->nRecords))); if (psSHP->panRecOffset == SHPLIB_NULLPTR || psSHP->panRecSize == SHPLIB_NULLPTR || @@ -612,13 +537,15 @@ SHPHandle SHPAPI_CALL SHPOpenLL(const char *pszLayer, const char *pszAccess, for (int i = 0; i < psSHP->nRecords; i++) { unsigned int nOffset; memcpy(&nOffset, pabyBuf + i * 8, 4); - if (!bBigEndian) - SwapWord(4, &nOffset); +#if !defined(SHP_BIG_ENDIAN) + SHP_SWAP32(&nOffset); +#endif unsigned int nLength; memcpy(&nLength, pabyBuf + i * 8 + 4, 4); - if (!bBigEndian) - SwapWord(4, &nLength); +#if !defined(SHP_BIG_ENDIAN) + SHP_SWAP32(&nLength); +#endif if (nOffset > STATIC_CAST(unsigned int, INT_MAX)) { char str[128]; @@ -657,7 +584,7 @@ SHPHandle SHPAPI_CALL SHPOpenLL(const char *pszLayer, const char *pszAccess, /************************************************************************/ SHPHandle SHPAPI_CALL SHPOpenLLEx(const char *pszLayer, const char *pszAccess, - SAHooks *psHooks, int bRestoreSHX) + const SAHooks *psHooks, int bRestoreSHX) { if (!bRestoreSHX) return SHPOpenLL(pszLayer, pszAccess, psHooks); @@ -678,7 +605,7 @@ SHPHandle SHPAPI_CALL SHPOpenLLEx(const char *pszLayer, const char *pszAccess, /************************************************************************/ int SHPAPI_CALL SHPRestoreSHX(const char *pszLayer, const char *pszAccess, - SAHooks *psHooks) + const SAHooks *psHooks) { /* -------------------------------------------------------------------- */ /* Ensure the access string is one of the legal ones. We */ @@ -693,19 +620,6 @@ int SHPAPI_CALL SHPRestoreSHX(const char *pszLayer, const char *pszAccess, pszAccess = "rb"; } -/* -------------------------------------------------------------------- */ -/* Establish the byte order on this machine. */ -/* -------------------------------------------------------------------- */ -#if !defined(bBigEndian) - { - int i = 1; - if (*((uchar *)&i) == 1) - bBigEndian = false; - else - bBigEndian = true; - } -#endif - /* -------------------------------------------------------------------- */ /* Open the .shp file. Note that files pulled from */ /* a PC to Unix with upper case filenames won't work! */ @@ -714,10 +628,10 @@ int SHPAPI_CALL SHPRestoreSHX(const char *pszLayer, const char *pszAccess, char *pszFullname = STATIC_CAST(char *, malloc(nLenWithoutExtension + 5)); memcpy(pszFullname, pszLayer, nLenWithoutExtension); memcpy(pszFullname + nLenWithoutExtension, ".shp", 5); - SAFile fpSHP = psHooks->FOpen(pszFullname, pszAccess); + SAFile fpSHP = psHooks->FOpen(pszFullname, pszAccess, psHooks->pvUserData); if (fpSHP == SHPLIB_NULLPTR) { memcpy(pszFullname + nLenWithoutExtension, ".SHP", 5); - fpSHP = psHooks->FOpen(pszFullname, pszAccess); + fpSHP = psHooks->FOpen(pszFullname, pszAccess, psHooks->pvUserData); } if (fpSHP == SHPLIB_NULLPTR) { @@ -738,7 +652,7 @@ int SHPAPI_CALL SHPRestoreSHX(const char *pszLayer, const char *pszAccess, /* -------------------------------------------------------------------- */ /* Read the file size from the SHP file. */ /* -------------------------------------------------------------------- */ - uchar *pabyBuf = STATIC_CAST(uchar *, malloc(100)); + unsigned char *pabyBuf = STATIC_CAST(unsigned char *, malloc(100)); if (psHooks->FRead(pabyBuf, 100, 1, fpSHP) != 1) { psHooks->Error(".shp file is unreadable, or corrupt."); psHooks->FClose(fpSHP); @@ -759,7 +673,8 @@ int SHPAPI_CALL SHPRestoreSHX(const char *pszLayer, const char *pszAccess, memcpy(pszFullname + nLenWithoutExtension, ".shx", 5); const char pszSHXAccess[] = "w+b"; - SAFile fpSHX = psHooks->FOpen(pszFullname, pszSHXAccess); + SAFile fpSHX = + psHooks->FOpen(pszFullname, pszSHXAccess, psHooks->pvUserData); if (fpSHX == SHPLIB_NULLPTR) { size_t nMessageLen = strlen(pszFullname) * 2 + 256; char *pszMessage = STATIC_CAST(char *, malloc(nMessageLen)); @@ -789,25 +704,70 @@ int SHPAPI_CALL SHPRestoreSHX(const char *pszLayer, const char *pszAccess, // unsigned int nCurrentRecordOffset = 0; unsigned int nCurrentSHPOffset = 100; unsigned int nRealSHXContentSize = 100; - unsigned int niRecord = 0; - unsigned int nRecordLength = 0; + int nRetCode = TRUE; unsigned int nRecordOffset = 50; - char abyReadRecord[8]; while (nCurrentSHPOffset < nSHPFilesize) { + unsigned int niRecord = 0; + unsigned int nRecordLength = 0; + int nSHPType; + if (psHooks->FRead(&niRecord, 4, 1, fpSHP) == 1 && - psHooks->FRead(&nRecordLength, 4, 1, fpSHP) == 1) { - if (!bBigEndian) - SwapWord(4, &nRecordOffset); - memcpy(abyReadRecord, &nRecordOffset, 4); + psHooks->FRead(&nRecordLength, 4, 1, fpSHP) == 1 && + psHooks->FRead(&nSHPType, 4, 1, fpSHP) == 1) { + char abyReadRecord[8]; + unsigned int nRecordOffsetBE = nRecordOffset; + +#if !defined(SHP_BIG_ENDIAN) + SHP_SWAP32(&nRecordOffsetBE); +#endif + memcpy(abyReadRecord, &nRecordOffsetBE, 4); memcpy(abyReadRecord + 4, &nRecordLength, 4); +#if !defined(SHP_BIG_ENDIAN) + SHP_SWAP32(&nRecordLength); +#endif +#if defined(SHP_BIG_ENDIAN) + SHP_SWAP32(&nSHPType); +#endif + + // Sanity check on record length + if (nRecordLength < 1 || + nRecordLength > (nSHPFilesize - (nCurrentSHPOffset + 8)) / 2) { + char szErrorMsg[200]; + snprintf(szErrorMsg, sizeof(szErrorMsg), + "Error parsing .shp to restore .shx. " + "Invalid record length = %u at record starting at " + "offset %u", + nRecordLength, nCurrentSHPOffset); + psHooks->Error(szErrorMsg); + + nRetCode = FALSE; + break; + } + + // Sanity check on record type + if (nSHPType != SHPT_NULL && nSHPType != SHPT_POINT && + nSHPType != SHPT_ARC && nSHPType != SHPT_POLYGON && + nSHPType != SHPT_MULTIPOINT && nSHPType != SHPT_POINTZ && + nSHPType != SHPT_ARCZ && nSHPType != SHPT_POLYGONZ && + nSHPType != SHPT_MULTIPOINTZ && nSHPType != SHPT_POINTM && + nSHPType != SHPT_ARCM && nSHPType != SHPT_POLYGONM && + nSHPType != SHPT_MULTIPOINTM && nSHPType != SHPT_MULTIPATCH) { + char szErrorMsg[200]; + snprintf(szErrorMsg, sizeof(szErrorMsg), + "Error parsing .shp to restore .shx. " + "Invalid shape type = %d at record starting at " + "offset %u", + nSHPType, nCurrentSHPOffset); + psHooks->Error(szErrorMsg); + + nRetCode = FALSE; + break; + } + psHooks->FWrite(abyReadRecord, 8, 1, fpSHX); - if (!bBigEndian) - SwapWord(4, &nRecordOffset); - if (!bBigEndian) - SwapWord(4, &nRecordLength); nRecordOffset += nRecordLength + 4; // nCurrentRecordOffset += 8; nCurrentSHPOffset += 8 + nRecordLength * 2; @@ -816,21 +776,30 @@ int SHPAPI_CALL SHPRestoreSHX(const char *pszLayer, const char *pszAccess, nRealSHXContentSize += 8; } else { - psHooks->Error("Error parsing .shp to restore .shx"); - - psHooks->FClose(fpSHX); - psHooks->FClose(fpSHP); - - free(pabySHXHeader); - free(pszFullname); + char szErrorMsg[200]; + snprintf(szErrorMsg, sizeof(szErrorMsg), + "Error parsing .shp to restore .shx. " + "Cannot read first bytes of record starting at " + "offset %u", + nCurrentSHPOffset); + psHooks->Error(szErrorMsg); - return (0); + nRetCode = FALSE; + break; } } + if (nRetCode && nCurrentSHPOffset != nSHPFilesize) { + psHooks->Error("Error parsing .shp to restore .shx. " + "Not expected number of bytes"); + + nRetCode = FALSE; + } nRealSHXContentSize /= 2; // Bytes counted -> WORDs - if (!bBigEndian) - SwapWord(4, &nRealSHXContentSize); +#if !defined(SHP_BIG_ENDIAN) + SHP_SWAP32(&nRealSHXContentSize); +#endif + psHooks->FSeek(fpSHX, 24, 0); psHooks->FWrite(&nRealSHXContentSize, 4, 1, fpSHX); @@ -840,13 +809,13 @@ int SHPAPI_CALL SHPRestoreSHX(const char *pszLayer, const char *pszAccess, free(pszFullname); free(pabySHXHeader); - return (1); + return nRetCode; } /************************************************************************/ /* SHPClose() */ -/* */ -/* Close the .shp and .shx files. */ +/* */ +/* Close the .shp and .shx files. */ /************************************************************************/ void SHPAPI_CALL SHPClose(SHPHandle psSHP) @@ -855,7 +824,7 @@ void SHPAPI_CALL SHPClose(SHPHandle psSHP) return; /* -------------------------------------------------------------------- */ - /* Update the header if we have modified anything. */ + /* Update the header if we have modified anything. */ /* -------------------------------------------------------------------- */ if (psSHP->bUpdated) SHPWriteHeader(psSHP); @@ -914,8 +883,9 @@ void SHPAPI_CALL SHPSetFastModeReadObject(SHPHandle hSHP, int bFastMode) /* Fetch general information about the shape file. */ /************************************************************************/ -void SHPAPI_CALL SHPGetInfo(SHPHandle psSHP, int *pnEntities, int *pnShapeType, - double *padfMinBound, double *padfMaxBound) +void SHPAPI_CALL SHPGetInfo(const SHPHandle psSHP, int *pnEntities, + int *pnShapeType, double *padfMinBound, + double *padfMaxBound) { if (psSHP == SHPLIB_NULLPTR) return; @@ -958,21 +928,8 @@ SHPHandle SHPAPI_CALL SHPCreate(const char *pszLayer, int nShapeType) /************************************************************************/ SHPHandle SHPAPI_CALL SHPCreateLL(const char *pszLayer, int nShapeType, - SAHooks *psHooks) + const SAHooks *psHooks) { -/* -------------------------------------------------------------------- */ -/* Establish the byte order on this system. */ -/* -------------------------------------------------------------------- */ -#if !defined(bBigEndian) - { - int i = 1; - if (*((uchar *)&i) == 1) - bBigEndian = false; - else - bBigEndian = true; - } -#endif - /* -------------------------------------------------------------------- */ /* Open the two files so we can write their headers. */ /* -------------------------------------------------------------------- */ @@ -980,7 +937,7 @@ SHPHandle SHPAPI_CALL SHPCreateLL(const char *pszLayer, int nShapeType, char *pszFullname = STATIC_CAST(char *, malloc(nLenWithoutExtension + 5)); memcpy(pszFullname, pszLayer, nLenWithoutExtension); memcpy(pszFullname + nLenWithoutExtension, ".shp", 5); - SAFile fpSHP = psHooks->FOpen(pszFullname, "wb"); + SAFile fpSHP = psHooks->FOpen(pszFullname, "w+b", psHooks->pvUserData); if (fpSHP == SHPLIB_NULLPTR) { char szErrorMsg[200]; snprintf(szErrorMsg, sizeof(szErrorMsg), "Failed to create file %s: %s", @@ -988,11 +945,11 @@ SHPHandle SHPAPI_CALL SHPCreateLL(const char *pszLayer, int nShapeType, psHooks->Error(szErrorMsg); free(pszFullname); - return NULL; + return SHPLIB_NULLPTR; } memcpy(pszFullname + nLenWithoutExtension, ".shx", 5); - SAFile fpSHX = psHooks->FOpen(pszFullname, "wb"); + SAFile fpSHX = psHooks->FOpen(pszFullname, "w+b", psHooks->pvUserData); if (fpSHX == SHPLIB_NULLPTR) { char szErrorMsg[200]; snprintf(szErrorMsg, sizeof(szErrorMsg), "Failed to create file %s: %s", @@ -1001,7 +958,7 @@ SHPHandle SHPAPI_CALL SHPCreateLL(const char *pszLayer, int nShapeType, free(pszFullname); psHooks->FClose(fpSHP); - return NULL; + return SHPLIB_NULLPTR; } free(pszFullname); @@ -1010,26 +967,29 @@ SHPHandle SHPAPI_CALL SHPCreateLL(const char *pszLayer, int nShapeType, /* -------------------------------------------------------------------- */ /* Prepare header block for .shp file. */ /* -------------------------------------------------------------------- */ - uchar abyHeader[100]; + unsigned char abyHeader[100]; memset(abyHeader, 0, sizeof(abyHeader)); abyHeader[2] = 0x27; /* magic cookie */ abyHeader[3] = 0x0a; - int32 i32 = 50; /* file size */ + uint32_t i32 = 50; /* file size */ ByteCopy(&i32, abyHeader + 24, 4); - if (!bBigEndian) - SwapWord(4, abyHeader + 24); +#if !defined(SHP_BIG_ENDIAN) + SHP_SWAP32(abyHeader + 24); +#endif i32 = 1000; /* version */ ByteCopy(&i32, abyHeader + 28, 4); - if (bBigEndian) - SwapWord(4, abyHeader + 28); +#if defined(SHP_BIG_ENDIAN) + SHP_SWAP32(abyHeader + 28); +#endif i32 = nShapeType; /* shape type */ ByteCopy(&i32, abyHeader + 32, 4); - if (bBigEndian) - SwapWord(4, abyHeader + 32); +#if defined(SHP_BIG_ENDIAN) + SHP_SWAP32(abyHeader + 32); +#endif double dValue = 0.0; /* set bounds */ ByteCopy(&dValue, abyHeader + 36, 8); @@ -1051,7 +1011,7 @@ SHPHandle SHPAPI_CALL SHPCreateLL(const char *pszLayer, int nShapeType, free(pszFullname); psHooks->FClose(fpSHP); psHooks->FClose(fpSHX); - return NULL; + return SHPLIB_NULLPTR; } /* -------------------------------------------------------------------- */ @@ -1059,8 +1019,9 @@ SHPHandle SHPAPI_CALL SHPCreateLL(const char *pszLayer, int nShapeType, /* -------------------------------------------------------------------- */ i32 = 50; /* file size */ ByteCopy(&i32, abyHeader + 24, 4); - if (!bBigEndian) - SwapWord(4, abyHeader + 24); +#if !defined(SHP_BIG_ENDIAN) + SHP_SWAP32(abyHeader + 24); +#endif if (psHooks->FWrite(abyHeader, 100, 1, fpSHX) != 1) { char szErrorMsg[200]; @@ -1073,16 +1034,37 @@ SHPHandle SHPAPI_CALL SHPCreateLL(const char *pszLayer, int nShapeType, free(pszFullname); psHooks->FClose(fpSHP); psHooks->FClose(fpSHX); - return NULL; + return SHPLIB_NULLPTR; } - /* -------------------------------------------------------------------- */ - /* Close the files, and then open them as regular existing files. */ - /* -------------------------------------------------------------------- */ - psHooks->FClose(fpSHP); - psHooks->FClose(fpSHX); + SHPHandle psSHP = STATIC_CAST(SHPHandle, calloc(1, sizeof(SHPInfo))); - return (SHPOpenLL(pszLayer, "r+b", psHooks)); + psSHP->bUpdated = FALSE; + memcpy(&(psSHP->sHooks), psHooks, sizeof(SAHooks)); + + psSHP->fpSHP = fpSHP; + psSHP->fpSHX = fpSHX; + psSHP->nShapeType = nShapeType; + psSHP->nFileSize = 100; + psSHP->panRecOffset = + STATIC_CAST(unsigned int *, malloc(sizeof(unsigned int))); + psSHP->panRecSize = + STATIC_CAST(unsigned int *, malloc(sizeof(unsigned int))); + + if (psSHP->panRecOffset == SHPLIB_NULLPTR || + psSHP->panRecSize == SHPLIB_NULLPTR) { + psSHP->sHooks.Error("Not enough memory to allocate requested memory"); + psSHP->sHooks.FClose(psSHP->fpSHP); + psSHP->sHooks.FClose(psSHP->fpSHX); + if (psSHP->panRecOffset) + free(psSHP->panRecOffset); + if (psSHP->panRecSize) + free(psSHP->panRecSize); + free(psSHP); + return SHPLIB_NULLPTR; + } + + return psSHP; } /************************************************************************/ @@ -1092,19 +1074,19 @@ SHPHandle SHPAPI_CALL SHPCreateLL(const char *pszLayer, int nShapeType, /* indicated location in the record. */ /************************************************************************/ -static void _SHPSetBounds(uchar *pabyRec, SHPObject *psShape) +static void _SHPSetBounds(unsigned char *pabyRec, const SHPObject *psShape) { ByteCopy(&(psShape->dfXMin), pabyRec + 0, 8); ByteCopy(&(psShape->dfYMin), pabyRec + 8, 8); ByteCopy(&(psShape->dfXMax), pabyRec + 16, 8); ByteCopy(&(psShape->dfYMax), pabyRec + 24, 8); - if (bBigEndian) { - SwapWord(8, pabyRec + 0); - SwapWord(8, pabyRec + 8); - SwapWord(8, pabyRec + 16); - SwapWord(8, pabyRec + 24); - } +#if defined(SHP_BIG_ENDIAN) + SHP_SWAP64(pabyRec + 0); + SHP_SWAP64(pabyRec + 8); + SHP_SWAP64(pabyRec + 16); + SHP_SWAP64(pabyRec + 24); +#endif } /************************************************************************/ @@ -1159,7 +1141,7 @@ SHPObject SHPAPI_CALL1(*) psObject->bMeasureIsUsed = FALSE; /* -------------------------------------------------------------------- */ - /* Establish whether this shape type has M, and Z values. */ + /* Establish whether this shape type has M, and Z values. */ /* -------------------------------------------------------------------- */ bool bHasM; bool bHasZ; @@ -1191,7 +1173,7 @@ SHPObject SHPAPI_CALL1(*) psObject->nParts = MAX(1, nParts); psObject->panPartStart = - STATIC_CAST(int *, calloc(sizeof(int), psObject->nParts)); + STATIC_CAST(int *, calloc(psObject->nParts, sizeof(int))); psObject->panPartType = STATIC_CAST(int *, malloc(sizeof(int) * psObject->nParts)); @@ -1208,8 +1190,7 @@ SHPObject SHPAPI_CALL1(*) psObject->panPartType[i] = SHPP_RING; } - if (psObject->panPartStart[0] != 0) - psObject->panPartStart[0] = 0; + psObject->panPartStart[0] = 0; } /* -------------------------------------------------------------------- */ @@ -1219,16 +1200,16 @@ SHPObject SHPAPI_CALL1(*) const size_t nSize = sizeof(double) * nVertices; psObject->padfX = STATIC_CAST(double *, padfX ? malloc(nSize) - : calloc(sizeof(double), nVertices)); + : calloc(nVertices, sizeof(double))); psObject->padfY = STATIC_CAST(double *, padfY ? malloc(nSize) - : calloc(sizeof(double), nVertices)); + : calloc(nVertices, sizeof(double))); psObject->padfZ = STATIC_CAST( double *, - padfZ &&bHasZ ? malloc(nSize) : calloc(sizeof(double), nVertices)); + padfZ &&bHasZ ? malloc(nSize) : calloc(nVertices, sizeof(double))); psObject->padfM = STATIC_CAST( double *, - padfM &&bHasM ? malloc(nSize) : calloc(sizeof(double), nVertices)); + padfM &&bHasM ? malloc(nSize) : calloc(nVertices, sizeof(double))); if (padfX != SHPLIB_NULLPTR) memcpy(psObject->padfX, padfX, nSize); if (padfY != SHPLIB_NULLPTR) @@ -1273,7 +1254,7 @@ SHPObject SHPAPI_CALL1(*) /************************************************************************/ int SHPAPI_CALL SHPWriteObject(SHPHandle psSHP, int nShapeId, - SHPObject *psObject) + const SHPObject *psObject) { psSHP->bUpdated = TRUE; @@ -1298,22 +1279,32 @@ int SHPAPI_CALL SHPWriteObject(SHPHandle psSHP, int nShapeId, /* Add the new entity to the in memory index. */ /* -------------------------------------------------------------------- */ if (nShapeId == -1 && psSHP->nRecords + 1 > psSHP->nMaxRecords) { + /* This cannot overflow given that we check that the file size does + * not grow over 4 GB, and the minimum size of a record is 12 bytes, + * hence the maximm value for nMaxRecords is 357,913,941 + */ int nNewMaxRecords = psSHP->nMaxRecords + psSHP->nMaxRecords / 3 + 100; unsigned int *panRecOffsetNew; unsigned int *panRecSizeNew; panRecOffsetNew = STATIC_CAST( - unsigned int *, SfRealloc(psSHP->panRecOffset, - sizeof(unsigned int) * nNewMaxRecords)); - if (panRecOffsetNew == SHPLIB_NULLPTR) + unsigned int *, realloc(psSHP->panRecOffset, + sizeof(unsigned int) * nNewMaxRecords)); + if (panRecOffsetNew == SHPLIB_NULLPTR) { + psSHP->sHooks.Error("Failed to write shape object. " + "Memory allocation error."); return -1; + } psSHP->panRecOffset = panRecOffsetNew; panRecSizeNew = STATIC_CAST( - unsigned int *, SfRealloc(psSHP->panRecSize, - sizeof(unsigned int) * nNewMaxRecords)); - if (panRecSizeNew == SHPLIB_NULLPTR) + unsigned int *, + realloc(psSHP->panRecSize, sizeof(unsigned int) * nNewMaxRecords)); + if (panRecSizeNew == SHPLIB_NULLPTR) { + psSHP->sHooks.Error("Failed to write shape object. " + "Memory allocation error."); return -1; + } psSHP->panRecSize = panRecSizeNew; psSHP->nMaxRecords = nNewMaxRecords; @@ -1322,14 +1313,28 @@ int SHPAPI_CALL SHPWriteObject(SHPHandle psSHP, int nShapeId, /* -------------------------------------------------------------------- */ /* Initialize record. */ /* -------------------------------------------------------------------- */ - uchar *pabyRec = - STATIC_CAST(uchar *, malloc(psObject->nVertices * 4 * sizeof(double) + - psObject->nParts * 8 + 128)); - if (pabyRec == SHPLIB_NULLPTR) + + /* The following computation cannot overflow on 32-bit platforms given that + * the user had to allocate arrays of at least that size. */ + size_t nRecMaxSize = + psObject->nVertices * 4 * sizeof(double) + psObject->nParts * 8; + /* But the following test could trigger on 64-bit platforms on huge + * geometries. */ + const unsigned nExtraSpaceForGeomHeader = 128; + if (nRecMaxSize > UINT_MAX - nExtraSpaceForGeomHeader) { + psSHP->sHooks.Error("Failed to write shape object. Too big geometry."); return -1; + } + nRecMaxSize += nExtraSpaceForGeomHeader; + unsigned char *pabyRec = STATIC_CAST(unsigned char *, malloc(nRecMaxSize)); + if (pabyRec == SHPLIB_NULLPTR) { + psSHP->sHooks.Error("Failed to write shape object. " + "Memory allocation error."); + return -1; + } /* -------------------------------------------------------------------- */ - /* Extract vertices for a Polygon or Arc. */ + /* Extract vertices for a Polygon or Arc. */ /* -------------------------------------------------------------------- */ unsigned int nRecordSize = 0; const bool bFirstFeature = psSHP->nRecords == 0; @@ -1339,15 +1344,15 @@ int SHPAPI_CALL SHPWriteObject(SHPHandle psSHP, int nShapeId, psObject->nSHPType == SHPT_POLYGONM || psObject->nSHPType == SHPT_ARC || psObject->nSHPType == SHPT_ARCZ || psObject->nSHPType == SHPT_ARCM || psObject->nSHPType == SHPT_MULTIPATCH) { - int32 nPoints = psObject->nVertices; - int32 nParts = psObject->nParts; + uint32_t nPoints = psObject->nVertices; + uint32_t nParts = psObject->nParts; _SHPSetBounds(pabyRec + 12, psObject); - if (bBigEndian) - SwapWord(4, &nPoints); - if (bBigEndian) - SwapWord(4, &nParts); +#if defined(SHP_BIG_ENDIAN) + SHP_SWAP32(&nPoints); + SHP_SWAP32(&nParts); +#endif ByteCopy(&nPoints, pabyRec + 40 + 8, 4); ByteCopy(&nParts, pabyRec + 36 + 8, 4); @@ -1360,8 +1365,9 @@ int SHPAPI_CALL SHPWriteObject(SHPHandle psSHP, int nShapeId, ByteCopy(psObject->panPartStart, pabyRec + 44 + 8, 4 * psObject->nParts); for (int i = 0; i < psObject->nParts; i++) { - if (bBigEndian) - SwapWord(4, pabyRec + 44 + 8 + 4 * i); +#if defined(SHP_BIG_ENDIAN) + SHP_SWAP32(pabyRec + 44 + 8 + 4 * i); +#endif nRecordSize += 4; } @@ -1372,8 +1378,9 @@ int SHPAPI_CALL SHPWriteObject(SHPHandle psSHP, int nShapeId, memcpy(pabyRec + nRecordSize, psObject->panPartType, 4 * psObject->nParts); for (int i = 0; i < psObject->nParts; i++) { - if (bBigEndian) - SwapWord(4, pabyRec + nRecordSize); +#if defined(SHP_BIG_ENDIAN) + SHP_SWAP32(pabyRec + nRecordSize); +#endif nRecordSize += 4; } } @@ -1385,11 +1392,10 @@ int SHPAPI_CALL SHPWriteObject(SHPHandle psSHP, int nShapeId, ByteCopy(psObject->padfX + i, pabyRec + nRecordSize, 8); ByteCopy(psObject->padfY + i, pabyRec + nRecordSize + 8, 8); - if (bBigEndian) - SwapWord(8, pabyRec + nRecordSize); - - if (bBigEndian) - SwapWord(8, pabyRec + nRecordSize + 8); +#if defined(SHP_BIG_ENDIAN) + SHP_SWAP64(pabyRec + nRecordSize); + SHP_SWAP64(pabyRec + nRecordSize + 8); +#endif nRecordSize += 2 * 8; } @@ -1401,19 +1407,22 @@ int SHPAPI_CALL SHPWriteObject(SHPHandle psSHP, int nShapeId, psObject->nSHPType == SHPT_ARCZ || psObject->nSHPType == SHPT_MULTIPATCH) { ByteCopy(&(psObject->dfZMin), pabyRec + nRecordSize, 8); - if (bBigEndian) - SwapWord(8, pabyRec + nRecordSize); +#if defined(SHP_BIG_ENDIAN) + SHP_SWAP64(pabyRec + nRecordSize); +#endif nRecordSize += 8; ByteCopy(&(psObject->dfZMax), pabyRec + nRecordSize, 8); - if (bBigEndian) - SwapWord(8, pabyRec + nRecordSize); +#if defined(SHP_BIG_ENDIAN) + SHP_SWAP64(pabyRec + nRecordSize); +#endif nRecordSize += 8; for (int i = 0; i < psObject->nVertices; i++) { ByteCopy(psObject->padfZ + i, pabyRec + nRecordSize, 8); - if (bBigEndian) - SwapWord(8, pabyRec + nRecordSize); +#if defined(SHP_BIG_ENDIAN) + SHP_SWAP64(pabyRec + nRecordSize); +#endif nRecordSize += 8; } } @@ -1430,65 +1439,72 @@ int SHPAPI_CALL SHPWriteObject(SHPHandle psSHP, int nShapeId, || psObject->nSHPType == SHPT_POLYGONZ || psObject->nSHPType == SHPT_ARCZ)) { ByteCopy(&(psObject->dfMMin), pabyRec + nRecordSize, 8); - if (bBigEndian) - SwapWord(8, pabyRec + nRecordSize); +#if defined(SHP_BIG_ENDIAN) + SHP_SWAP64(pabyRec + nRecordSize); +#endif nRecordSize += 8; ByteCopy(&(psObject->dfMMax), pabyRec + nRecordSize, 8); - if (bBigEndian) - SwapWord(8, pabyRec + nRecordSize); +#if defined(SHP_BIG_ENDIAN) + SHP_SWAP64(pabyRec + nRecordSize); +#endif nRecordSize += 8; for (int i = 0; i < psObject->nVertices; i++) { ByteCopy(psObject->padfM + i, pabyRec + nRecordSize, 8); - if (bBigEndian) - SwapWord(8, pabyRec + nRecordSize); +#if defined(SHP_BIG_ENDIAN) + SHP_SWAP64(pabyRec + nRecordSize); +#endif nRecordSize += 8; } } } /* -------------------------------------------------------------------- */ - /* Extract vertices for a MultiPoint. */ + /* Extract vertices for a MultiPoint. */ /* -------------------------------------------------------------------- */ else if (psObject->nSHPType == SHPT_MULTIPOINT || psObject->nSHPType == SHPT_MULTIPOINTZ || psObject->nSHPType == SHPT_MULTIPOINTM) { - int32 nPoints = psObject->nVertices; + uint32_t nPoints = psObject->nVertices; _SHPSetBounds(pabyRec + 12, psObject); - if (bBigEndian) - SwapWord(4, &nPoints); +#if defined(SHP_BIG_ENDIAN) + SHP_SWAP32(&nPoints); +#endif ByteCopy(&nPoints, pabyRec + 44, 4); for (int i = 0; i < psObject->nVertices; i++) { ByteCopy(psObject->padfX + i, pabyRec + 48 + i * 16, 8); ByteCopy(psObject->padfY + i, pabyRec + 48 + i * 16 + 8, 8); - if (bBigEndian) - SwapWord(8, pabyRec + 48 + i * 16); - if (bBigEndian) - SwapWord(8, pabyRec + 48 + i * 16 + 8); +#if defined(SHP_BIG_ENDIAN) + SHP_SWAP64(pabyRec + 48 + i * 16); + SHP_SWAP64(pabyRec + 48 + i * 16 + 8); +#endif } nRecordSize = 48 + 16 * psObject->nVertices; if (psObject->nSHPType == SHPT_MULTIPOINTZ) { ByteCopy(&(psObject->dfZMin), pabyRec + nRecordSize, 8); - if (bBigEndian) - SwapWord(8, pabyRec + nRecordSize); +#if defined(SHP_BIG_ENDIAN) + SHP_SWAP64(pabyRec + nRecordSize); +#endif nRecordSize += 8; ByteCopy(&(psObject->dfZMax), pabyRec + nRecordSize, 8); - if (bBigEndian) - SwapWord(8, pabyRec + nRecordSize); +#if defined(SHP_BIG_ENDIAN) + SHP_SWAP64(pabyRec + nRecordSize); +#endif nRecordSize += 8; for (int i = 0; i < psObject->nVertices; i++) { ByteCopy(psObject->padfZ + i, pabyRec + nRecordSize, 8); - if (bBigEndian) - SwapWord(8, pabyRec + nRecordSize); +#if defined(SHP_BIG_ENDIAN) + SHP_SWAP64(pabyRec + nRecordSize); +#endif nRecordSize += 8; } } @@ -1497,26 +1513,29 @@ int SHPAPI_CALL SHPWriteObject(SHPHandle psSHP, int nShapeId, (psObject->nSHPType == SHPT_MULTIPOINTZ || psObject->nSHPType == SHPT_MULTIPOINTM)) { ByteCopy(&(psObject->dfMMin), pabyRec + nRecordSize, 8); - if (bBigEndian) - SwapWord(8, pabyRec + nRecordSize); +#if defined(SHP_BIG_ENDIAN) + SHP_SWAP64(pabyRec + nRecordSize); +#endif nRecordSize += 8; ByteCopy(&(psObject->dfMMax), pabyRec + nRecordSize, 8); - if (bBigEndian) - SwapWord(8, pabyRec + nRecordSize); +#if defined(SHP_BIG_ENDIAN) + SHP_SWAP64(pabyRec + nRecordSize); +#endif nRecordSize += 8; for (int i = 0; i < psObject->nVertices; i++) { ByteCopy(psObject->padfM + i, pabyRec + nRecordSize, 8); - if (bBigEndian) - SwapWord(8, pabyRec + nRecordSize); +#if defined(SHP_BIG_ENDIAN) + SHP_SWAP64(pabyRec + nRecordSize); +#endif nRecordSize += 8; } } } /* -------------------------------------------------------------------- */ - /* Write point. */ + /* Write point. */ /* -------------------------------------------------------------------- */ else if (psObject->nSHPType == SHPT_POINT || psObject->nSHPType == SHPT_POINTZ || @@ -1524,25 +1543,27 @@ int SHPAPI_CALL SHPWriteObject(SHPHandle psSHP, int nShapeId, ByteCopy(psObject->padfX, pabyRec + 12, 8); ByteCopy(psObject->padfY, pabyRec + 20, 8); - if (bBigEndian) - SwapWord(8, pabyRec + 12); - if (bBigEndian) - SwapWord(8, pabyRec + 20); +#if defined(SHP_BIG_ENDIAN) + SHP_SWAP64(pabyRec + 12); + SHP_SWAP64(pabyRec + 20); +#endif nRecordSize = 28; if (psObject->nSHPType == SHPT_POINTZ) { ByteCopy(psObject->padfZ, pabyRec + nRecordSize, 8); - if (bBigEndian) - SwapWord(8, pabyRec + nRecordSize); +#if defined(SHP_BIG_ENDIAN) + SHP_SWAP64(pabyRec + nRecordSize); +#endif nRecordSize += 8; } if (psObject->bMeasureIsUsed && (psObject->nSHPType == SHPT_POINTZ || psObject->nSHPType == SHPT_POINTM)) { ByteCopy(psObject->padfM, pabyRec + nRecordSize, 8); - if (bBigEndian) - SwapWord(8, pabyRec + nRecordSize); +#if defined(SHP_BIG_ENDIAN) + SHP_SWAP64(pabyRec + nRecordSize); +#endif nRecordSize += 8; } } @@ -1598,20 +1619,23 @@ int SHPAPI_CALL SHPWriteObject(SHPHandle psSHP, int nShapeId, /* -------------------------------------------------------------------- */ /* Set the shape type, record number, and record size. */ /* -------------------------------------------------------------------- */ - int32 i32 = + uint32_t i32 = (nShapeId < 0) ? psSHP->nRecords + 1 : nShapeId + 1; /* record # */ - if (!bBigEndian) - SwapWord(4, &i32); +#if !defined(SHP_BIG_ENDIAN) + SHP_SWAP32(&i32); +#endif ByteCopy(&i32, pabyRec, 4); i32 = (nRecordSize - 8) / 2; /* record size */ - if (!bBigEndian) - SwapWord(4, &i32); +#if !defined(SHP_BIG_ENDIAN) + SHP_SWAP32(&i32); +#endif ByteCopy(&i32, pabyRec + 4, 4); i32 = psObject->nSHPType; /* shape type */ - if (bBigEndian) - SwapWord(4, &i32); +#if defined(SHP_BIG_ENDIAN) + SHP_SWAP32(&i32); +#endif ByteCopy(&i32, pabyRec + 8, 4); /* -------------------------------------------------------------------- */ @@ -1666,7 +1690,7 @@ int SHPAPI_CALL SHPWriteObject(SHPHandle psSHP, int nShapeId, psSHP->panRecSize[nShapeId] = nRecordSize - 8; /* -------------------------------------------------------------------- */ - /* Expand file wide bounds based on this shape. */ + /* Expand file wide bounds based on this shape. */ /* -------------------------------------------------------------------- */ if (bFirstFeature) { if (psObject->nSHPType == SHPT_NULL || psObject->nVertices == 0) { @@ -1754,11 +1778,11 @@ static unsigned char *SHPReallocObjectBufIfNecessary(SHPHandle psSHP, /************************************************************************/ /* SHPReadObject() */ /* */ -/* Read the vertices, parts, and other non-attribute information */ -/* for one shape. */ +/* Read the vertices, parts, and other non-attribute information */ +/* for one shape. */ /************************************************************************/ -SHPObject SHPAPI_CALL1(*) SHPReadObject(SHPHandle psSHP, int hEntity) +SHPObject SHPAPI_CALL1(*) SHPReadObject(const SHPHandle psSHP, int hEntity) { /* -------------------------------------------------------------------- */ /* Validate the record/entity number. */ @@ -1786,10 +1810,10 @@ SHPObject SHPAPI_CALL1(*) SHPReadObject(SHPHandle psSHP, int hEntity) psSHP->sHooks.Error(str); return SHPLIB_NULLPTR; } - if (!bBigEndian) - SwapWord(4, &nOffset); - if (!bBigEndian) - SwapWord(4, &nLength); +#if !defined(SHP_BIG_ENDIAN) + SHP_SWAP32(&nOffset); + SHP_SWAP32(&nLength); +#endif if (nOffset > STATIC_CAST(unsigned int, INT_MAX)) { char str[128]; @@ -1859,8 +1883,8 @@ SHPObject SHPAPI_CALL1(*) SHPReadObject(SHPHandle psSHP, int hEntity) } } - uchar *pabyRecNew = - STATIC_CAST(uchar *, SfRealloc(psSHP->pabyRec, nNewBufSize)); + unsigned char *pabyRecNew = + STATIC_CAST(unsigned char *, realloc(psSHP->pabyRec, nNewBufSize)); if (pabyRecNew == SHPLIB_NULLPTR) { char szErrorMsg[160]; snprintf(szErrorMsg, sizeof(szErrorMsg), @@ -1919,8 +1943,9 @@ SHPObject SHPAPI_CALL1(*) SHPReadObject(SHPHandle psSHP, int hEntity) /* Do a sanity check */ int nSHPContentLength; memcpy(&nSHPContentLength, psSHP->pabyRec + 4, 4); - if (!bBigEndian) - SwapWord(4, &(nSHPContentLength)); +#if !defined(SHP_BIG_ENDIAN) + SHP_SWAP32(&(nSHPContentLength)); +#endif if (nSHPContentLength < 0 || nSHPContentLength > INT_MAX / 2 - 4 || 2 * nSHPContentLength + 8 != nBytesRead) { char str[128]; @@ -1962,11 +1987,12 @@ SHPObject SHPAPI_CALL1(*) SHPReadObject(SHPHandle psSHP, int hEntity) int nSHPType; memcpy(&nSHPType, psSHP->pabyRec + 8, 4); - if (bBigEndian) - SwapWord(4, &(nSHPType)); +#if defined(SHP_BIG_ENDIAN) + SHP_SWAP32(&(nSHPType)); +#endif /* -------------------------------------------------------------------- */ - /* Allocate and minimally initialize the object. */ + /* Allocate and minimally initialize the object. */ /* -------------------------------------------------------------------- */ SHPObject *psShape; if (psSHP->bFastModeReadObject) { @@ -1988,7 +2014,7 @@ SHPObject SHPAPI_CALL1(*) SHPReadObject(SHPHandle psSHP, int hEntity) psShape->bFastModeReadObject = psSHP->bFastModeReadObject; /* ==================================================================== */ - /* Extract vertices for a Polygon or Arc. */ + /* Extract vertices for a Polygon or Arc. */ /* ==================================================================== */ if (psShape->nSHPType == SHPT_POLYGON || psShape->nSHPType == SHPT_ARC || psShape->nSHPType == SHPT_POLYGONZ || @@ -2007,22 +2033,20 @@ SHPObject SHPAPI_CALL1(*) SHPReadObject(SHPHandle psSHP, int hEntity) } /* -------------------------------------------------------------------- */ - /* Get the X/Y bounds. */ + /* Get the X/Y bounds. */ /* -------------------------------------------------------------------- */ - memcpy(&(psShape->dfXMin), psSHP->pabyRec + 8 + 4, 8); - memcpy(&(psShape->dfYMin), psSHP->pabyRec + 8 + 12, 8); - memcpy(&(psShape->dfXMax), psSHP->pabyRec + 8 + 20, 8); - memcpy(&(psShape->dfYMax), psSHP->pabyRec + 8 + 28, 8); - - if (bBigEndian) - SwapWord(8, &(psShape->dfXMin)); - if (bBigEndian) - SwapWord(8, &(psShape->dfYMin)); - if (bBigEndian) - SwapWord(8, &(psShape->dfXMax)); - if (bBigEndian) - SwapWord(8, &(psShape->dfYMax)); +#if defined(SHP_BIG_ENDIAN) + SHP_SWAPDOUBLE_CPY(&psShape->dfXMin, psSHP->pabyRec + 8 + 4); + SHP_SWAPDOUBLE_CPY(&psShape->dfYMin, psSHP->pabyRec + 8 + 12); + SHP_SWAPDOUBLE_CPY(&psShape->dfXMax, psSHP->pabyRec + 8 + 20); + SHP_SWAPDOUBLE_CPY(&psShape->dfYMax, psSHP->pabyRec + 8 + 28); +#else + memcpy(&psShape->dfXMin, psSHP->pabyRec + 8 + 4, 8); + memcpy(&psShape->dfYMin, psSHP->pabyRec + 8 + 12, 8); + memcpy(&psShape->dfXMax, psSHP->pabyRec + 8 + 20, 8); + memcpy(&psShape->dfYMax, psSHP->pabyRec + 8 + 28, 8); +#endif /* -------------------------------------------------------------------- */ @@ -2030,15 +2054,15 @@ SHPObject SHPAPI_CALL1(*) SHPReadObject(SHPHandle psSHP, int hEntity) /* to proper size. */ /* -------------------------------------------------------------------- */ - int32 nPoints; + uint32_t nPoints; memcpy(&nPoints, psSHP->pabyRec + 40 + 8, 4); - int32 nParts; + uint32_t nParts; memcpy(&nParts, psSHP->pabyRec + 36 + 8, 4); - if (bBigEndian) - SwapWord(4, &nPoints); - if (bBigEndian) - SwapWord(4, &nParts); +#if defined(SHP_BIG_ENDIAN) + SHP_SWAP32(&nPoints); + SHP_SWAP32(&nParts); +#endif /* nPoints and nParts are unsigned */ if (/* nPoints < 0 || nParts < 0 || */ @@ -2121,7 +2145,7 @@ SHPObject SHPAPI_CALL1(*) SHPReadObject(SHPHandle psSHP, int hEntity) return SHPLIB_NULLPTR; } - for (int i = 0; STATIC_CAST(int32, i) < nParts; i++) + for (int i = 0; STATIC_CAST(uint32_t, i) < nParts; i++) psShape->panPartType[i] = SHPP_RING; /* -------------------------------------------------------------------- @@ -2130,9 +2154,10 @@ SHPObject SHPAPI_CALL1(*) SHPReadObject(SHPHandle psSHP, int hEntity) /* -------------------------------------------------------------------- */ memcpy(psShape->panPartStart, psSHP->pabyRec + 44 + 8, 4 * nParts); - for (int i = 0; STATIC_CAST(int32, i) < nParts; i++) { - if (bBigEndian) - SwapWord(4, psShape->panPartStart + i); + for (int i = 0; STATIC_CAST(uint32_t, i) < nParts; i++) { +#if defined(SHP_BIG_ENDIAN) + SHP_SWAP32(psShape->panPartStart + i); +#endif /* We check that the offset is inside the vertex array */ if (psShape->panPartStart[i] < 0 || @@ -2174,9 +2199,10 @@ SHPObject SHPAPI_CALL1(*) SHPReadObject(SHPHandle psSHP, int hEntity) */ if (psShape->nSHPType == SHPT_MULTIPATCH) { memcpy(psShape->panPartType, psSHP->pabyRec + nOffset, 4 * nParts); - for (int i = 0; STATIC_CAST(int32, i) < nParts; i++) { - if (bBigEndian) - SwapWord(4, psShape->panPartType + i); + for (int i = 0; STATIC_CAST(uint32_t, i) < nParts; i++) { +#if defined(SHP_BIG_ENDIAN) + SHP_SWAP32(psShape->panPartType + i); +#endif } nOffset += 4 * nParts; @@ -2187,16 +2213,17 @@ SHPObject SHPAPI_CALL1(*) SHPReadObject(SHPHandle psSHP, int hEntity) /* Copy out the vertices from the record. */ /* -------------------------------------------------------------------- */ - for (int i = 0; STATIC_CAST(int32, i) < nPoints; i++) { + for (int i = 0; STATIC_CAST(uint32_t, i) < nPoints; i++) { +#if defined(SHP_BIG_ENDIAN) + SHP_SWAPDOUBLE_CPY(psShape->padfX + i, + psSHP->pabyRec + nOffset + i * 16); + SHP_SWAPDOUBLE_CPY(psShape->padfY + i, + psSHP->pabyRec + nOffset + i * 16 + 8); +#else memcpy(psShape->padfX + i, psSHP->pabyRec + nOffset + i * 16, 8); - memcpy(psShape->padfY + i, psSHP->pabyRec + nOffset + i * 16 + 8, 8); - - if (bBigEndian) - SwapWord(8, psShape->padfX + i); - if (bBigEndian) - SwapWord(8, psShape->padfY + i); +#endif } nOffset += 16 * nPoints; @@ -2209,19 +2236,23 @@ SHPObject SHPAPI_CALL1(*) SHPReadObject(SHPHandle psSHP, int hEntity) if (psShape->nSHPType == SHPT_POLYGONZ || psShape->nSHPType == SHPT_ARCZ || psShape->nSHPType == SHPT_MULTIPATCH) { - memcpy(&(psShape->dfZMin), psSHP->pabyRec + nOffset, 8); - memcpy(&(psShape->dfZMax), psSHP->pabyRec + nOffset + 8, 8); +#if defined(SHP_BIG_ENDIAN) + SHP_SWAPDOUBLE_CPY(&psShape->dfZMin, psSHP->pabyRec + nOffset); + SHP_SWAPDOUBLE_CPY(&psShape->dfZMax, psSHP->pabyRec + nOffset + 8); +#else + memcpy(&psShape->dfZMin, psSHP->pabyRec + nOffset, 8); + memcpy(&psShape->dfZMax, psSHP->pabyRec + nOffset + 8, 8); - if (bBigEndian) - SwapWord(8, &(psShape->dfZMin)); - if (bBigEndian) - SwapWord(8, &(psShape->dfZMax)); +#endif - for (int i = 0; STATIC_CAST(int32, i) < nPoints; i++) { + for (int i = 0; STATIC_CAST(uint32_t, i) < nPoints; i++) { +#if defined(SHP_BIG_ENDIAN) + SHP_SWAPDOUBLE_CPY(psShape->padfZ + i, + psSHP->pabyRec + nOffset + 16 + i * 8); +#else memcpy(psShape->padfZ + i, psSHP->pabyRec + nOffset + 16 + i * 8, 8); - if (bBigEndian) - SwapWord(8, psShape->padfZ + i); +#endif } nOffset += 16 + 8 * nPoints; @@ -2239,19 +2270,22 @@ SHPObject SHPAPI_CALL1(*) SHPReadObject(SHPHandle psSHP, int hEntity) /* -------------------------------------------------------------------- */ if (nEntitySize >= STATIC_CAST(int, nOffset + 16 + 8 * nPoints)) { - memcpy(&(psShape->dfMMin), psSHP->pabyRec + nOffset, 8); - memcpy(&(psShape->dfMMax), psSHP->pabyRec + nOffset + 8, 8); - - if (bBigEndian) - SwapWord(8, &(psShape->dfMMin)); - if (bBigEndian) - SwapWord(8, &(psShape->dfMMax)); +#if defined(SHP_BIG_ENDIAN) + SHP_SWAPDOUBLE_CPY(&psShape->dfMMin, psSHP->pabyRec + nOffset); + SHP_SWAPDOUBLE_CPY(&psShape->dfMMax, psSHP->pabyRec + nOffset + 8); +#else + memcpy(&psShape->dfMMin, psSHP->pabyRec + nOffset, 8); + memcpy(&psShape->dfMMax, psSHP->pabyRec + nOffset + 8, 8); +#endif - for (int i = 0; STATIC_CAST(int32, i) < nPoints; i++) { + for (int i = 0; STATIC_CAST(uint32_t, i) < nPoints; i++) { +#if defined(SHP_BIG_ENDIAN) + SHP_SWAPDOUBLE_CPY(psShape->padfM + i, + psSHP->pabyRec + nOffset + 16 + i * 8); +#else memcpy(psShape->padfM + i, psSHP->pabyRec + nOffset + 16 + i * 8, 8); - if (bBigEndian) - SwapWord(8, psShape->padfM + i); +#endif } psShape->bMeasureIsUsed = TRUE; } @@ -2261,7 +2295,7 @@ SHPObject SHPAPI_CALL1(*) SHPReadObject(SHPHandle psSHP, int hEntity) } /* ==================================================================== */ - /* Extract vertices for a MultiPoint. */ + /* Extract vertices for a MultiPoint. */ /* ==================================================================== */ else if (psShape->nSHPType == SHPT_MULTIPOINT || psShape->nSHPType == SHPT_MULTIPOINTM || @@ -2276,11 +2310,12 @@ SHPObject SHPAPI_CALL1(*) SHPReadObject(SHPHandle psSHP, int hEntity) SHPDestroyObject(psShape); return SHPLIB_NULLPTR; } - int32 nPoints; + uint32_t nPoints; memcpy(&nPoints, psSHP->pabyRec + 44, 4); - if (bBigEndian) - SwapWord(4, &nPoints); +#if defined(SHP_BIG_ENDIAN) + SHP_SWAP32(&nPoints); +#endif /* nPoints is unsigned */ if (/* nPoints < 0 || */ nPoints > 50 * 1000 * 1000) { @@ -2346,36 +2381,36 @@ SHPObject SHPAPI_CALL1(*) SHPReadObject(SHPHandle psSHP, int hEntity) return SHPLIB_NULLPTR; } - for (int i = 0; STATIC_CAST(int32, i) < nPoints; i++) { + for (int i = 0; STATIC_CAST(uint32_t, i) < nPoints; i++) { +#if defined(SHP_BIG_ENDIAN) + SHP_SWAPDOUBLE_CPY(psShape->padfX + i, + psSHP->pabyRec + 48 + 16 * i); + SHP_SWAPDOUBLE_CPY(psShape->padfY + i, + psSHP->pabyRec + 48 + 16 * i + 8); +#else memcpy(psShape->padfX + i, psSHP->pabyRec + 48 + 16 * i, 8); memcpy(psShape->padfY + i, psSHP->pabyRec + 48 + 16 * i + 8, 8); - - if (bBigEndian) - SwapWord(8, psShape->padfX + i); - if (bBigEndian) - SwapWord(8, psShape->padfY + i); +#endif } int nOffset = 48 + 16 * nPoints; /* -------------------------------------------------------------------- */ - /* Get the X/Y bounds. */ + /* Get the X/Y bounds. */ /* -------------------------------------------------------------------- */ - memcpy(&(psShape->dfXMin), psSHP->pabyRec + 8 + 4, 8); - memcpy(&(psShape->dfYMin), psSHP->pabyRec + 8 + 12, 8); - memcpy(&(psShape->dfXMax), psSHP->pabyRec + 8 + 20, 8); - memcpy(&(psShape->dfYMax), psSHP->pabyRec + 8 + 28, 8); - - if (bBigEndian) - SwapWord(8, &(psShape->dfXMin)); - if (bBigEndian) - SwapWord(8, &(psShape->dfYMin)); - if (bBigEndian) - SwapWord(8, &(psShape->dfXMax)); - if (bBigEndian) - SwapWord(8, &(psShape->dfYMax)); +#if defined(SHP_BIG_ENDIAN) + SHP_SWAPDOUBLE_CPY(&psShape->dfXMin, psSHP->pabyRec + 8 + 4); + SHP_SWAPDOUBLE_CPY(&psShape->dfYMin, psSHP->pabyRec + 8 + 12); + SHP_SWAPDOUBLE_CPY(&psShape->dfXMax, psSHP->pabyRec + 8 + 20); + SHP_SWAPDOUBLE_CPY(&psShape->dfYMax, psSHP->pabyRec + 8 + 28); +#else + memcpy(&psShape->dfXMin, psSHP->pabyRec + 8 + 4, 8); + memcpy(&psShape->dfYMin, psSHP->pabyRec + 8 + 12, 8); + memcpy(&psShape->dfXMax, psSHP->pabyRec + 8 + 20, 8); + memcpy(&psShape->dfYMax, psSHP->pabyRec + 8 + 28, 8); +#endif /* -------------------------------------------------------------------- */ @@ -2383,19 +2418,22 @@ SHPObject SHPAPI_CALL1(*) SHPReadObject(SHPHandle psSHP, int hEntity) /* -------------------------------------------------------------------- */ if (psShape->nSHPType == SHPT_MULTIPOINTZ) { - memcpy(&(psShape->dfZMin), psSHP->pabyRec + nOffset, 8); - memcpy(&(psShape->dfZMax), psSHP->pabyRec + nOffset + 8, 8); - - if (bBigEndian) - SwapWord(8, &(psShape->dfZMin)); - if (bBigEndian) - SwapWord(8, &(psShape->dfZMax)); +#if defined(SHP_BIG_ENDIAN) + SHP_SWAPDOUBLE_CPY(&psShape->dfZMin, psSHP->pabyRec + nOffset); + SHP_SWAPDOUBLE_CPY(&psShape->dfZMax, psSHP->pabyRec + nOffset + 8); +#else + memcpy(&psShape->dfZMin, psSHP->pabyRec + nOffset, 8); + memcpy(&psShape->dfZMax, psSHP->pabyRec + nOffset + 8, 8); +#endif - for (int i = 0; STATIC_CAST(int32, i) < nPoints; i++) { + for (int i = 0; STATIC_CAST(uint32_t, i) < nPoints; i++) { +#if defined(SHP_BIG_ENDIAN) + SHP_SWAPDOUBLE_CPY(psShape->padfZ + i, + psSHP->pabyRec + nOffset + 16 + i * 8); +#else memcpy(psShape->padfZ + i, psSHP->pabyRec + nOffset + 16 + i * 8, 8); - if (bBigEndian) - SwapWord(8, psShape->padfZ + i); +#endif } nOffset += 16 + 8 * nPoints; @@ -2412,19 +2450,22 @@ SHPObject SHPAPI_CALL1(*) SHPReadObject(SHPHandle psSHP, int hEntity) /* -------------------------------------------------------------------- */ if (nEntitySize >= STATIC_CAST(int, nOffset + 16 + 8 * nPoints)) { - memcpy(&(psShape->dfMMin), psSHP->pabyRec + nOffset, 8); - memcpy(&(psShape->dfMMax), psSHP->pabyRec + nOffset + 8, 8); - - if (bBigEndian) - SwapWord(8, &(psShape->dfMMin)); - if (bBigEndian) - SwapWord(8, &(psShape->dfMMax)); +#if defined(SHP_BIG_ENDIAN) + SHP_SWAPDOUBLE_CPY(&psShape->dfMMin, psSHP->pabyRec + nOffset); + SHP_SWAPDOUBLE_CPY(&psShape->dfMMax, psSHP->pabyRec + nOffset + 8); +#else + memcpy(&psShape->dfMMin, psSHP->pabyRec + nOffset, 8); + memcpy(&psShape->dfMMax, psSHP->pabyRec + nOffset + 8, 8); +#endif - for (int i = 0; STATIC_CAST(int32, i) < nPoints; i++) { + for (int i = 0; STATIC_CAST(uint32_t, i) < nPoints; i++) { +#if defined(SHP_BIG_ENDIAN) + SHP_SWAPDOUBLE_CPY(psShape->padfM + i, + psSHP->pabyRec + nOffset + 16 + i * 8); +#else memcpy(psShape->padfM + i, psSHP->pabyRec + nOffset + 16 + i * 8, 8); - if (bBigEndian) - SwapWord(8, psShape->padfM + i); +#endif } psShape->bMeasureIsUsed = TRUE; } @@ -2465,13 +2506,13 @@ SHPObject SHPAPI_CALL1(*) SHPReadObject(SHPHandle psSHP, int hEntity) SHPDestroyObject(psShape); return SHPLIB_NULLPTR; } +#if defined(SHP_BIG_ENDIAN) + SHP_SWAPDOUBLE_CPY(psShape->padfX, psSHP->pabyRec + 12); + SHP_SWAPDOUBLE_CPY(psShape->padfY, psSHP->pabyRec + 20); +#else memcpy(psShape->padfX, psSHP->pabyRec + 12, 8); memcpy(psShape->padfY, psSHP->pabyRec + 20, 8); - - if (bBigEndian) - SwapWord(8, psShape->padfX); - if (bBigEndian) - SwapWord(8, psShape->padfY); +#endif int nOffset = 20 + 8; @@ -2481,10 +2522,11 @@ SHPObject SHPAPI_CALL1(*) SHPReadObject(SHPHandle psSHP, int hEntity) /* -------------------------------------------------------------------- */ if (psShape->nSHPType == SHPT_POINTZ) { +#if defined(SHP_BIG_ENDIAN) + SHP_SWAPDOUBLE_CPY(psShape->padfZ, psSHP->pabyRec + nOffset); +#else memcpy(psShape->padfZ, psSHP->pabyRec + nOffset, 8); - - if (bBigEndian) - SwapWord(8, psShape->padfZ); +#endif nOffset += 8; } @@ -2498,10 +2540,11 @@ SHPObject SHPAPI_CALL1(*) SHPReadObject(SHPHandle psSHP, int hEntity) /* -------------------------------------------------------------------- */ if (nEntitySize >= nOffset + 8) { +#if defined(SHP_BIG_ENDIAN) + SHP_SWAPDOUBLE_CPY(psShape->padfM, psSHP->pabyRec + nOffset); +#else memcpy(psShape->padfM, psSHP->pabyRec + nOffset, 8); - - if (bBigEndian) - SwapWord(8, psShape->padfM); +#endif psShape->bMeasureIsUsed = TRUE; } @@ -2739,8 +2782,9 @@ static int SHPRewindIsInnerRing(const SHPObject *psObject, int iOpRing, /* specification. */ /************************************************************************/ -int SHPAPI_CALL SHPRewindObject(CPL_UNUSED SHPHandle hSHP, SHPObject *psObject) +int SHPAPI_CALL SHPRewindObject(const SHPHandle hSHP, SHPObject *psObject) { + (void)hSHP; /* -------------------------------------------------------------------- */ /* Do nothing if this is not a polygon object. */ /* -------------------------------------------------------------------- */ From e82663c25232be28cc768789ed7a1a586f20354c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Sun, 29 Sep 2024 05:44:15 -0400 Subject: [PATCH 308/471] style: Use `any` or `all` instead of `for` loop (SIM110) (#4404) --- gui/wxpython/psmap/instructions.py | 5 +---- man/build_class_graphical.py | 5 +---- man/build_manual_gallery.py | 5 +---- pyproject.toml | 2 -- python/grass/pygrass/gis/region.py | 5 +---- python/grass/pygrass/raster/history.py | 5 +---- python/grass/pygrass/vector/table.py | 5 +---- python/grass/script/task.py | 6 +----- scripts/g.search.modules/g.search.modules.py | 5 +---- 9 files changed, 8 insertions(+), 35 deletions(-) diff --git a/gui/wxpython/psmap/instructions.py b/gui/wxpython/psmap/instructions.py index 8a535b96704..693e8024b57 100644 --- a/gui/wxpython/psmap/instructions.py +++ b/gui/wxpython/psmap/instructions.py @@ -86,10 +86,7 @@ def __getitem__(self, id): def __contains__(self, id): """Test if instruction is included""" - for each in self.instruction: - if each.id == id: - return True - return False + return any(each.id == id for each in self.instruction) def __delitem__(self, id): """Delete instruction""" diff --git a/man/build_class_graphical.py b/man/build_class_graphical.py index 35b2a731cfe..554c950d3a8 100644 --- a/man/build_class_graphical.py +++ b/man/build_class_graphical.py @@ -91,10 +91,7 @@ def file_matches(filename, patterns): - for pattern in patterns: - if fnmatch.fnmatch(filename, pattern): - return True - return False + return any(fnmatch.fnmatch(filename, pattern) for pattern in patterns) def starts_with_module(string, module) -> bool: diff --git a/man/build_manual_gallery.py b/man/build_manual_gallery.py index 3d381e27877..51a6fbcd312 100755 --- a/man/build_manual_gallery.py +++ b/man/build_manual_gallery.py @@ -102,10 +102,7 @@ def img_in_html(filename, imagename) -> bool: def file_matches(filename, patterns): - for pattern in patterns: - if fnmatch.fnmatch(filename, pattern): - return True - return False + return any(fnmatch.fnmatch(filename, pattern) for pattern in patterns) def get_files(directory, patterns, exclude_patterns): diff --git a/pyproject.toml b/pyproject.toml index 3a6d449ec1d..9e430071afc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -246,8 +246,6 @@ ignore = [ "SIM102", # collapsible-if "SIM105", # suppressible-exception "SIM108", # if-else-block-instead-of-if-exp - "SIM109", # compare-with-tuple - "SIM110", # reimplemented-builtin "SIM113", # enumerate-for-loop "SIM116", # if-else-block-instead-of-dict-lookup "SIM118", # in-dict-keys diff --git a/python/grass/pygrass/gis/region.py b/python/grass/pygrass/gis/region.py index 98c75a15631..892a3664b49 100644 --- a/python/grass/pygrass/gis/region.py +++ b/python/grass/pygrass/gis/region.py @@ -331,10 +331,7 @@ def __eq__(self, reg): "zone", "proj", ] - for attr in attrs: - if getattr(self, attr) != getattr(reg, attr): - return False - return True + return all(getattr(self, attr) == getattr(reg, attr) for attr in attrs) def __ne__(self, other): return not self == other diff --git a/python/grass/pygrass/raster/history.py b/python/grass/pygrass/raster/history.py index 5ec855cf1bb..8f227affabc 100644 --- a/python/grass/pygrass/raster/history.py +++ b/python/grass/pygrass/raster/history.py @@ -59,10 +59,7 @@ def __del__(self): """Rast_free_history""" def __eq__(self, hist): - for attr in self.attrs: - if getattr(self, attr) != getattr(hist, attr): - return False - return True + return all(getattr(self, attr) == getattr(hist, attr) for attr in self.attrs) def __len__(self): return self.length() diff --git a/python/grass/pygrass/vector/table.py b/python/grass/pygrass/vector/table.py index 4b7e99c0a83..634c440a583 100644 --- a/python/grass/pygrass/vector/table.py +++ b/python/grass/pygrass/vector/table.py @@ -791,10 +791,7 @@ def __eq__(self, link): False """ attrs = ["layer", "name", "table_name", "key", "driver"] - for attr in attrs: - if getattr(self, attr) != getattr(link, attr): - return False - return True + return all(getattr(self, attr) == getattr(link, attr) for attr in attrs) def __ne__(self, other): return not self == other diff --git a/python/grass/script/task.py b/python/grass/script/task.py index 35234ba65e6..8e1f0a12844 100644 --- a/python/grass/script/task.py +++ b/python/grass/script/task.py @@ -247,11 +247,7 @@ def get_options(self): def has_required(self): """Check if command has at least one required parameter""" - for p in self.params: - if p.get("required", False): - return True - - return False + return any(p.get("required", False) for p in self.params) def set_param(self, aParam, aValue, element="value"): """Set param value/values.""" diff --git a/scripts/g.search.modules/g.search.modules.py b/scripts/g.search.modules/g.search.modules.py index a3bab4325c2..574b8133a9e 100755 --- a/scripts/g.search.modules/g.search.modules.py +++ b/scripts/g.search.modules/g.search.modules.py @@ -313,10 +313,7 @@ def _exact_search(keyword, module_keywords): :param module_keywords: comma separated list of keywords """ module_keywords = module_keywords.split(",") - for current in module_keywords: - if keyword == current: - return True - return False + return keyword in module_keywords def _manpage_search(pattern, name): From 108e5e4fa06ab8b2d8178597145b6abcdab871b5 Mon Sep 17 00:00:00 2001 From: Vaclav Petras Date: Mon, 30 Sep 2024 10:23:19 -0400 Subject: [PATCH 309/471] raster: Add library function to ask for mask presence (#4402) To avoid asking about presence of the MASK raster, add a library function which checks for presence of the raster hiding its name in the library. Motivation for this is a greater mask abstraction. Some code needs to link to newly raster library (which was previously used transitively). --- include/grass/defs/raster.h | 1 + lib/raster/mask_info.c | 10 ++++++++++ raster/r.mfilter/main.c | 4 ++-- raster/r.neighbors/main.c | 4 ++-- raster/r.patch/main.c | 4 ++-- raster/r.resamp.filter/main.c | 4 ++-- raster/r.resamp.interp/main.c | 4 ++-- raster/r.series/main.c | 4 ++-- raster/r.sim/r.sim.sediment/Makefile | 4 ++-- raster/r.sim/r.sim.sediment/main.c | 5 +++-- raster/r.sim/r.sim.water/Makefile | 4 ++-- raster/r.sim/r.sim.water/main.c | 5 +++-- raster/r.slope.aspect/main.c | 4 ++-- raster/r.sun/main.c | 4 ++-- raster/r.univar/r.univar_main.c | 4 ++-- vector/v.surf.rst/Makefile | 4 ++-- vector/v.surf.rst/main.c | 5 +++-- 17 files changed, 44 insertions(+), 30 deletions(-) diff --git a/include/grass/defs/raster.h b/include/grass/defs/raster.h index 7244da2a354..c2d26ccdccc 100644 --- a/include/grass/defs/raster.h +++ b/include/grass/defs/raster.h @@ -393,6 +393,7 @@ int Rast_option_to_interp_type(const struct Option *); /* mask_info.c */ char *Rast_mask_info(void); int Rast__mask_info(char *, char *); +bool Rast_mask_is_present(void); /* maskfd.c */ int Rast_maskfd(void); diff --git a/lib/raster/mask_info.c b/lib/raster/mask_info.c index 792510879dc..1a11da972f8 100644 --- a/lib/raster/mask_info.c +++ b/lib/raster/mask_info.c @@ -70,3 +70,13 @@ int Rast__mask_info(char *name, char *mapset) return 1; } + +/** + * @brief Check presence of 2D raster mask + * + * @return true if mask is present, false otherwise + */ +bool Rast_mask_is_present(void) +{ + return G_find_raster("MASK", G_mapset()) != NULL; +} diff --git a/raster/r.mfilter/main.c b/raster/r.mfilter/main.c index 17ecd2182be..ad0a9dd35ae 100644 --- a/raster/r.mfilter/main.c +++ b/raster/r.mfilter/main.c @@ -132,8 +132,8 @@ int main(int argc, char **argv) "threads setting.")); nprocs = 1; #endif - if (nprocs > 1 && G_find_raster("MASK", G_mapset()) != NULL) { - G_warning(_("Parallel processing disabled due to active MASK.")); + if (nprocs > 1 && Rast_mask_is_present()) { + G_warning(_("Parallel processing disabled due to active mask.")); nprocs = 1; } out_name = opt2->answer; diff --git a/raster/r.neighbors/main.c b/raster/r.neighbors/main.c index 93cbaa9e3da..8885460a564 100644 --- a/raster/r.neighbors/main.c +++ b/raster/r.neighbors/main.c @@ -310,8 +310,8 @@ int main(int argc, char *argv[]) "threads setting.")); ncb.threads = 1; #endif - if (ncb.threads > 1 && G_find_raster("MASK", G_mapset()) != NULL) { - G_warning(_("Parallel processing disabled due to active MASK.")); + if (ncb.threads > 1 && Rast_mask_is_present()) { + G_warning(_("Parallel processing disabled due to active mask.")); ncb.threads = 1; } if (strcmp(parm.weighting_function->answer, "none") && flag.circle->answer) diff --git a/raster/r.patch/main.c b/raster/r.patch/main.c index 3835e287440..c54482db13a 100644 --- a/raster/r.patch/main.c +++ b/raster/r.patch/main.c @@ -113,8 +113,8 @@ int main(int argc, char *argv[]) "threads setting.")); nprocs = 1; #endif - if (nprocs > 1 && G_find_raster("MASK", G_mapset()) != NULL) { - G_warning(_("Parallel processing disabled due to active MASK.")); + if (nprocs > 1 && Rast_mask_is_present()) { + G_warning(_("Parallel processing disabled due to active mask.")); nprocs = 1; } diff --git a/raster/r.resamp.filter/main.c b/raster/r.resamp.filter/main.c index b1f6ed3e530..e74f3598629 100644 --- a/raster/r.resamp.filter/main.c +++ b/raster/r.resamp.filter/main.c @@ -494,8 +494,8 @@ int main(int argc, char *argv[]) "threads setting.")); nprocs = 1; #endif - if (nprocs > 1 && G_find_raster("MASK", G_mapset()) != NULL) { - G_warning(_("Parallel processing disabled due to active MASK.")); + if (nprocs > 1 && Rast_mask_is_present()) { + G_warning(_("Parallel processing disabled due to active make.")); nprocs = 1; } if (parm.radius->answer) { diff --git a/raster/r.resamp.interp/main.c b/raster/r.resamp.interp/main.c index 734f3306bf8..2319400b0fc 100644 --- a/raster/r.resamp.interp/main.c +++ b/raster/r.resamp.interp/main.c @@ -132,8 +132,8 @@ int main(int argc, char *argv[]) "threads setting.")); threads = 1; #endif - if (threads > 1 && G_find_raster("MASK", G_mapset()) != NULL) { - G_warning(_("Parallel processing disabled due to active MASK.")); + if (threads > 1 && Rast_mask_is_present()) { + G_warning(_("Parallel processing disabled due to active mask.")); threads = 1; } bufrows = atoi(memory->answer) * (((1 << 20) / sizeof(DCELL)) / dst_w.cols); diff --git a/raster/r.series/main.c b/raster/r.series/main.c index bfecbf117c0..ade93d8f185 100644 --- a/raster/r.series/main.c +++ b/raster/r.series/main.c @@ -227,8 +227,8 @@ int main(int argc, char *argv[]) "threads setting.")); nprocs = 1; #endif - if (nprocs > 1 && G_find_raster("MASK", G_mapset()) != NULL) { - G_warning(_("Parallel processing disabled due to active MASK.")); + if (nprocs > 1 && Rast_mask_is_present()) { + G_warning(_("Parallel processing disabled due to active mask.")); nprocs = 1; } lo = -INFINITY; diff --git a/raster/r.sim/r.sim.sediment/Makefile b/raster/r.sim/r.sim.sediment/Makefile index 4bc07478ec9..13ca19d09dc 100644 --- a/raster/r.sim/r.sim.sediment/Makefile +++ b/raster/r.sim/r.sim.sediment/Makefile @@ -4,8 +4,8 @@ PGM=r.sim.sediment EXTRA_CLEAN_DIRS=doxygenhtml -LIBES = $(SIMLIB) $(GMATHLIB) $(GISLIB) $(OPENMP_LIBPATH) $(OPENMP_LIB) -DEPENDENCIES = $(SIMDEP) $(GMATHDEP) $(GISDEP) +LIBES = $(SIMLIB) $(GMATHLIB) $(GISLIB) $(RASTERLIB) $(OPENMP_LIBPATH) $(OPENMP_LIB) +DEPENDENCIES = $(SIMDEP) $(GMATHDEP) $(GISDEP) $(RASTERDEP) EXTRA_INC = $(OPENMP_INCPATH) $(VECT_INC) EXTRA_CFLAGS = -I ../simlib $(VECT_CFLAGS) $(OPENMP_CFLAGS) diff --git a/raster/r.sim/r.sim.sediment/main.c b/raster/r.sim/r.sim.sediment/main.c index fcdd9e98f89..dd082fe0f81 100644 --- a/raster/r.sim/r.sim.sediment/main.c +++ b/raster/r.sim/r.sim.sediment/main.c @@ -70,6 +70,7 @@ #endif #include #include +#include #include #include #include @@ -380,8 +381,8 @@ int main(int argc, char *argv[]) #else threads = 1; #endif - if (threads > 1 && G_find_raster("MASK", G_mapset()) != NULL) { - G_warning(_("Parallel processing disabled due to active MASK.")); + if (threads > 1 && Rast_mask_is_present()) { + G_warning(_("Parallel processing disabled due to active mask.")); threads = 1; } G_message(_("Number of threads: %d"), threads); diff --git a/raster/r.sim/r.sim.water/Makefile b/raster/r.sim/r.sim.water/Makefile index 36121ebf82e..47037823fae 100644 --- a/raster/r.sim/r.sim.water/Makefile +++ b/raster/r.sim/r.sim.water/Makefile @@ -4,8 +4,8 @@ PGM=r.sim.water EXTRA_CLEAN_DIRS=doxygenhtml -LIBES = $(SIMLIB) $(GMATHLIB) $(GISLIB) $(OPENMP_LIBPATH) $(OPENMP_LIB) -DEPENDENCIES = $(SIMDEP) $(GMATHDEP) $(GISDEP) +LIBES = $(SIMLIB) $(GMATHLIB) $(GISLIB) $(RASTERLIB) $(OPENMP_LIBPATH) $(OPENMP_LIB) +DEPENDENCIES = $(SIMDEP) $(GMATHDEP) $(GISDEP) $(RASTERDEP) EXTRA_INC = $(VECT_INC) $(OPENMP_INCPATH) EXTRA_CFLAGS = -I ../simlib $(VECT_CFLAGS) $(OPENMP_CFLAGS) diff --git a/raster/r.sim/r.sim.water/main.c b/raster/r.sim/r.sim.water/main.c index 3998e5524ac..61f128f5504 100644 --- a/raster/r.sim/r.sim.water/main.c +++ b/raster/r.sim/r.sim.water/main.c @@ -77,6 +77,7 @@ #endif #include #include +#include #include #include #include @@ -407,8 +408,8 @@ int main(int argc, char *argv[]) #else threads = 1; #endif - if (threads > 1 && G_find_raster("MASK", G_mapset()) != NULL) { - G_warning(_("Parallel processing disabled due to active MASK.")); + if (threads > 1 && Rast_mask_is_present()) { + G_warning(_("Parallel processing disabled due to active mask.")); threads = 1; } G_message(_("Number of threads: %d"), threads); diff --git a/raster/r.slope.aspect/main.c b/raster/r.slope.aspect/main.c index 86b4d5f5607..d302e02b22c 100644 --- a/raster/r.slope.aspect/main.c +++ b/raster/r.slope.aspect/main.c @@ -305,8 +305,8 @@ int main(int argc, char *argv[]) "threads setting.")); nprocs = 1; #endif - if (nprocs > 1 && G_find_raster("MASK", G_mapset()) != NULL) { - G_warning(_("Parallel processing disabled due to active MASK.")); + if (nprocs > 1 && Rast_mask_is_present()) { + G_warning(_("Parallel processing disabled due to active mask.")); nprocs = 1; } radians_to_degrees = 180.0 / M_PI; diff --git a/raster/r.sun/main.c b/raster/r.sun/main.c index 9e45e184dac..e325cb637f0 100644 --- a/raster/r.sun/main.c +++ b/raster/r.sun/main.c @@ -591,8 +591,8 @@ int main(int argc, char *argv[]) #else threads = 1; #endif - if (threads > 1 && G_find_raster("MASK", G_mapset()) != NULL) { - G_warning(_("Parallel processing disabled due to active MASK.")); + if (threads > 1 && Rast_mask_is_present()) { + G_warning(_("Parallel processing disabled due to active mask.")); threads = 1; } G_message(_("Number of threads <%d>"), threads); diff --git a/raster/r.univar/r.univar_main.c b/raster/r.univar/r.univar_main.c index 805acfff27b..e56301a1364 100644 --- a/raster/r.univar/r.univar_main.c +++ b/raster/r.univar/r.univar_main.c @@ -192,8 +192,8 @@ int main(int argc, char *argv[]) sscanf(param.nprocs->answer, "%d", &nprocs); if (nprocs < 1) G_fatal_error(_("<%d> is not valid number of nprocs."), nprocs); - if (nprocs > 1 && G_find_raster("MASK", G_mapset()) != NULL) { - G_warning(_("Parallel processing disabled due to active MASK.")); + if (nprocs > 1 && Rast_mask_is_present()) { + G_warning(_("Parallel processing disabled due to active mask.")); nprocs = 1; } #if defined(_OPENMP) diff --git a/vector/v.surf.rst/Makefile b/vector/v.surf.rst/Makefile index 3b5d8eca836..414ebbd85a0 100644 --- a/vector/v.surf.rst/Makefile +++ b/vector/v.surf.rst/Makefile @@ -4,9 +4,9 @@ PGM=v.surf.rst EXTRA_CLEAN_DIRS=doxygenhtml -LIBES = $(INTERPFLLIB) $(QTREELIB) $(INTERPDATALIB) $(GMATHLIB) $(VECTORLIB) $(DBMILIB) $(GISLIB) $(MATHLIB) +LIBES = $(INTERPFLLIB) $(QTREELIB) $(INTERPDATALIB) $(GMATHLIB) $(RASTERLIB) $(VECTORLIB) $(DBMILIB) $(GISLIB) $(MATHLIB) EXTRA_LIBS = $(OPENMP_LIBPATH) $(OPENMP_LIB) -DEPENDENCIES = $(INTERPFLDEP) $(QTREEDEP) $(INTERPDATADEP) $(GMATHDEP) $(VECTORDEP) $(DBMIDEP) $(GISDEP) +DEPENDENCIES = $(INTERPFLDEP) $(QTREEDEP) $(INTERPDATADEP) $(GMATHDEP) $(RASTERDEP) $(VECTORDEP) $(DBMIDEP) $(GISDEP) EXTRA_INC = $(VECT_INC) $(OPENMP_INCPATH) EXTRA_CFLAGS = $(VECT_CFLAGS) $(OPENMP_CFLAGS) diff --git a/vector/v.surf.rst/main.c b/vector/v.surf.rst/main.c index c4581737eed..7b42e269d0f 100644 --- a/vector/v.surf.rst/main.c +++ b/vector/v.surf.rst/main.c @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -419,8 +420,8 @@ int main(int argc, char *argv[]) G_warning(_("GRASS GIS is not compiled with OpenMP support, parallel " "computation is disabled.")); #endif - if (threads > 1 && G_find_raster("MASK", G_mapset()) != NULL) { - G_warning(_("Parallel processing disabled due to active MASK.")); + if (threads > 1 && Rast_mask_is_present()) { + G_warning(_("Parallel processing disabled due to active mask.")); threads = 1; } if (devi) { From 808acd35c85c8dfbf21081e141e002f89103a9ae Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 30 Sep 2024 13:25:14 -0400 Subject: [PATCH 310/471] CI(deps): Update docker/build-push-action action to v6.9.0 (#4420) --- .github/workflows/docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 824fbe18e1e..f8440e8e71f 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -76,7 +76,7 @@ jobs: password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build and push id: docker_build - uses: docker/build-push-action@32945a339266b759abcbdc89316275140b0fc960 # v6.8.0 + uses: docker/build-push-action@4f58ea79222b3b9dc2c8bbdd6debcef730109a75 # v6.9.0 with: push: true pull: true From e02b921e45649c2473370b083aded4dd5a5545d4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 30 Sep 2024 13:25:51 -0400 Subject: [PATCH 311/471] CI(deps): Update github/codeql-action action to v3.26.10 (#4419) --- .github/workflows/codeql-analysis.yml | 4 ++-- .github/workflows/python-code-quality.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 14017253fd5..84d857a3505 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -56,7 +56,7 @@ jobs: if: ${{ matrix.language == 'c-cpp' }} - name: Initialize CodeQL - uses: github/codeql-action/init@461ef6c76dfe95d5c364de2f431ddbd31a417628 # v3.26.9 + uses: github/codeql-action/init@e2b3eafc8d227b0241d48be5f425d47c2d750a13 # v3.26.10 with: languages: ${{ matrix.language }} config-file: ./.github/codeql/codeql-config.yml @@ -81,6 +81,6 @@ jobs: run: .github/workflows/build_ubuntu-22.04.sh "${HOME}/install" - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@461ef6c76dfe95d5c364de2f431ddbd31a417628 # v3.26.9 + uses: github/codeql-action/analyze@e2b3eafc8d227b0241d48be5f425d47c2d750a13 # v3.26.10 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index 9febdd990e3..2e156b08417 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -135,7 +135,7 @@ jobs: path: bandit.sarif - name: Upload SARIF File into Security Tab - uses: github/codeql-action/upload-sarif@461ef6c76dfe95d5c364de2f431ddbd31a417628 # v3.26.9 + uses: github/codeql-action/upload-sarif@e2b3eafc8d227b0241d48be5f425d47c2d750a13 # v3.26.10 with: sarif_file: bandit.sarif From 33d0d15dee425df2062cd0a28399272a290174e1 Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Mon, 30 Sep 2024 13:34:30 -0400 Subject: [PATCH 312/471] v.transform: Fix Copy into fix Buffer size issue (#4415) --- vector/v.transform/main.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/vector/v.transform/main.c b/vector/v.transform/main.c index 400cd3ded91..06a7d105881 100644 --- a/vector/v.transform/main.c +++ b/vector/v.transform/main.c @@ -188,8 +188,17 @@ int main(int argc, char *argv[]) if (G_parser(argc, argv)) exit(EXIT_FAILURE); - strcpy(Current.name, vold->answer); - strcpy(Trans.name, vnew->answer); + if (G_strlcpy(Current.name, vold->answer, sizeof(Current.name)) >= + sizeof(Current.name)) { + G_fatal_error(_("Input vector map name <%s> is too long"), + vold->answer); + } + + if (G_strlcpy(Trans.name, vnew->answer, sizeof(Trans.name)) >= + sizeof(Trans.name)) { + G_fatal_error(_("Output vector map name <%s> is too long"), + vnew->answer); + } Vect_check_input_output_name(vold->answer, vnew->answer, G_FATAL_EXIT); From 480e050b206f6239f9a6f67fd9d176f1c83e3ff6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Mon, 30 Sep 2024 18:50:15 -0400 Subject: [PATCH 313/471] style: Fix redundant-open-modes (UP015) (#4410) --- display/d.mon/render_cmd.py | 2 +- gui/wxpython/core/gconsole.py | 2 +- gui/wxpython/core/render.py | 2 +- gui/wxpython/core/settings.py | 4 ++-- gui/wxpython/core/watchdog.py | 2 +- gui/wxpython/core/workspace.py | 2 +- gui/wxpython/gcp/manager.py | 4 ++-- gui/wxpython/gmodeler/model.py | 2 +- gui/wxpython/gui_core/forms.py | 2 +- gui/wxpython/gui_core/ghelp.py | 4 ++-- gui/wxpython/gui_core/widgets.py | 2 +- gui/wxpython/image2target/ii2t_gis_set.py | 2 +- gui/wxpython/image2target/ii2t_manager.py | 4 ++-- gui/wxpython/location_wizard/wizard.py | 12 +++++------- gui/wxpython/mapdisp/main.py | 4 ++-- gui/wxpython/modules/colorrules.py | 2 +- gui/wxpython/modules/mcalc_builder.py | 2 +- gui/wxpython/photo2image/ip2i_manager.py | 8 ++++---- gui/wxpython/psmap/instructions.py | 2 +- gui/wxpython/vnet/vnet_data.py | 2 +- imagery/i.atcorr/create_iwave.py | 2 +- lib/init/grass.py | 8 ++++---- man/build_html.py | 4 ++-- man/build_rest.py | 4 ++-- pyproject.toml | 1 - python/grass/grassdb/history.py | 6 ++---- python/grass/gunittest/checkers.py | 2 +- python/grass/gunittest/reporters.py | 2 +- python/grass/jupyter/interactivemap.py | 2 +- python/grass/pygrass/gis/__init__.py | 2 +- python/grass/pygrass/modules/grid/grid.py | 2 +- python/grass/pygrass/modules/interface/env.py | 2 +- python/grass/pygrass/raster/category.py | 2 +- python/grass/pygrass/tests/set_mapset.py | 2 +- python/grass/script/core.py | 4 ++-- python/grass/script/utils.py | 4 ++-- python/grass/temporal/register.py | 2 +- python/grass/temporal/stds_import.py | 4 ++-- raster/r.contour/testsuite/test_r_contour.py | 4 ++-- raster/r.in.gdal/testsuite/test_r_in_gdal.py | 2 +- raster/r.what/testsuite/test_r_what.py | 2 +- scripts/d.correlate/d.correlate.py | 4 ++-- scripts/d.frame/d.frame.py | 2 +- scripts/db.univar/db.univar.py | 2 +- scripts/g.extension.all/g.extension.all.py | 2 +- scripts/g.search.modules/g.search.modules.py | 6 +++--- scripts/r.in.wms/wms_gdal_drv.py | 2 +- scripts/r.pack/r.pack.py | 2 +- scripts/v.unpack/v.unpack.py | 2 +- temporal/t.rast.what/t.rast.what.py | 6 +++--- temporal/t.remove/t.remove.py | 2 +- temporal/t.unregister/t.unregister.py | 2 +- 52 files changed, 78 insertions(+), 83 deletions(-) diff --git a/display/d.mon/render_cmd.py b/display/d.mon/render_cmd.py index 7678a1442e4..cf153c0b017 100644 --- a/display/d.mon/render_cmd.py +++ b/display/d.mon/render_cmd.py @@ -39,7 +39,7 @@ def remove_mapfile(mapfile): # read environment variables from file def read_env_file(env_file): width = height = legfile = None - fd = open(env_file, "r") + fd = open(env_file) if fd is None: grass.fatal("Unable to open file '{0}'".format(env_file)) lines = fd.readlines() diff --git a/gui/wxpython/core/gconsole.py b/gui/wxpython/core/gconsole.py index 7575c401c98..092f9bc704a 100644 --- a/gui/wxpython/core/gconsole.py +++ b/gui/wxpython/core/gconsole.py @@ -678,7 +678,7 @@ def load_source(modname, filename): skipInterface = True if os.path.splitext(command[0])[1] in {".py", ".sh"}: try: - with open(command[0], "r") as sfile: + with open(command[0]) as sfile: for line in sfile: if len(line) < 3: continue diff --git a/gui/wxpython/core/render.py b/gui/wxpython/core/render.py index 5b3e3096043..1d4ed6f5386 100644 --- a/gui/wxpython/core/render.py +++ b/gui/wxpython/core/render.py @@ -896,7 +896,7 @@ def GetWindow(self): env["GISDBASE"], env["LOCATION_NAME"], env["MAPSET"], "WIND" ) try: - windfile = open(filename, "r") + windfile = open(filename) except OSError as e: sys.exit( _("Error: Unable to open '%(file)s'. Reason: %(ret)s. wxGUI exited.\n") diff --git a/gui/wxpython/core/settings.py b/gui/wxpython/core/settings.py index 2b2d3b28526..ef3a3031cee 100644 --- a/gui/wxpython/core/settings.py +++ b/gui/wxpython/core/settings.py @@ -922,7 +922,7 @@ def update_nested_dict_by_dict(dictionary, update): return dictionary try: - with open(self.filePath, "r") as f: + with open(self.filePath) as f: update = json.load(f, object_hook=settings_JSON_decode_hook) update_nested_dict_by_dict(settings, update) except json.JSONDecodeError as e: @@ -942,7 +942,7 @@ def _readLegacyFile(self, settings=None): settings = self.userSettings try: - fd = open(self.legacyFilePath, "r") + fd = open(self.legacyFilePath) except OSError: sys.stderr.write( _("Unable to read settings file <%s>\n") % self.legacyFilePath diff --git a/gui/wxpython/core/watchdog.py b/gui/wxpython/core/watchdog.py index 591bb36b477..76302aeea0a 100644 --- a/gui/wxpython/core/watchdog.py +++ b/gui/wxpython/core/watchdog.py @@ -68,7 +68,7 @@ def on_modified(self, event): self.modified_time = timestamp # wait to make sure file writing is done time.sleep(0.1) - with open(event.src_path, "r") as f: + with open(event.src_path) as f: gisrc = {} for line in f: key, val = line.split(":") diff --git a/gui/wxpython/core/workspace.py b/gui/wxpython/core/workspace.py index 9550aa73488..be4eb1fda37 100644 --- a/gui/wxpython/core/workspace.py +++ b/gui/wxpython/core/workspace.py @@ -1742,7 +1742,7 @@ def read(self, parent): :return: list of map layers """ try: - file = open(self.filename, "r") + file = open(self.filename) except OSError: wx.MessageBox( parent=parent, diff --git a/gui/wxpython/gcp/manager.py b/gui/wxpython/gcp/manager.py index a96555fe271..528d710f72c 100644 --- a/gui/wxpython/gcp/manager.py +++ b/gui/wxpython/gcp/manager.py @@ -119,7 +119,7 @@ def __init__(self, parent, giface): self.target_gisrc = os.environ["GISRC"] self.gisrc_dict = {} try: - f = open(self.target_gisrc, "r") + f = open(self.target_gisrc) for line in f: line = line.replace("\n", "").strip() if len(line) < 1: @@ -1603,7 +1603,7 @@ def ReadGCPs(self): GError(parent=self, message=_("target mapwin not defined")) try: - f = open(self.file["points"], "r") + f = open(self.file["points"]) GCPcnt = 0 for line in f: diff --git a/gui/wxpython/gmodeler/model.py b/gui/wxpython/gmodeler/model.py index 59ea791192c..98a7204abbe 100644 --- a/gui/wxpython/gmodeler/model.py +++ b/gui/wxpython/gmodeler/model.py @@ -546,7 +546,7 @@ def _substituteFile(self, item, params=None, checkOnly=False): for finput in self.fileInput: # read lines - fd = open(finput, "r") + fd = open(finput) try: data = self.fileInput[finput] = fd.read() finally: diff --git a/gui/wxpython/gui_core/forms.py b/gui/wxpython/gui_core/forms.py index 74bcc00f85a..1ef623feaf7 100644 --- a/gui/wxpython/gui_core/forms.py +++ b/gui/wxpython/gui_core/forms.py @@ -2588,7 +2588,7 @@ def OnFileLoad(self, event): data = "" try: - f = open(path, "r") + f = open(path) except OSError as e: gcmd.GError( parent=self, diff --git a/gui/wxpython/gui_core/ghelp.py b/gui/wxpython/gui_core/ghelp.py index 72e2564b74d..6713dbcc0a3 100644 --- a/gui/wxpython/gui_core/ghelp.py +++ b/gui/wxpython/gui_core/ghelp.py @@ -274,7 +274,7 @@ def _pageCopyright(self): """Copyright information""" copyfile = os.path.join(os.getenv("GISBASE"), "COPYING") if os.path.exists(copyfile): - copyrightFile = open(copyfile, "r") + copyrightFile = open(copyfile) copytext = copyrightFile.read() copyrightFile.close() else: @@ -303,7 +303,7 @@ def _pageLicense(self): """Licence about""" licfile = os.path.join(os.getenv("GISBASE"), "GPL.TXT") if os.path.exists(licfile): - licenceFile = open(licfile, "r") + licenceFile = open(licfile) license = "".join(licenceFile.readlines()) licenceFile.close() else: diff --git a/gui/wxpython/gui_core/widgets.py b/gui/wxpython/gui_core/widgets.py index c959ebbbf50..5f2582f9b1e 100644 --- a/gui/wxpython/gui_core/widgets.py +++ b/gui/wxpython/gui_core/widgets.py @@ -1566,7 +1566,7 @@ def _loadSettings(self): return data try: - fd = open(self.settingsFile, "r") + fd = open(self.settingsFile) except OSError: return data diff --git a/gui/wxpython/image2target/ii2t_gis_set.py b/gui/wxpython/image2target/ii2t_gis_set.py index 187b01d1740..e63e39ba365 100644 --- a/gui/wxpython/image2target/ii2t_gis_set.py +++ b/gui/wxpython/image2target/ii2t_gis_set.py @@ -543,7 +543,7 @@ def _readGisRC(self): if gisrc and os.path.isfile(gisrc): try: - rc = open(gisrc, "r") + rc = open(gisrc) for line in rc: try: key, val = line.split(":", 1) diff --git a/gui/wxpython/image2target/ii2t_manager.py b/gui/wxpython/image2target/ii2t_manager.py index 1c9958c9cef..06693926ec3 100644 --- a/gui/wxpython/image2target/ii2t_manager.py +++ b/gui/wxpython/image2target/ii2t_manager.py @@ -159,7 +159,7 @@ def __init__(self, parent, giface): self.target_gisrc = os.environ["GISRC"] self.gisrc_dict = {} try: - f = open(self.target_gisrc, "r") + f = open(self.target_gisrc) for line in f: line = line.replace("\n", "").strip() if len(line) < 1: @@ -1627,7 +1627,7 @@ def ReadGCPs(self): GError(parent=self, message=_("target mapwin not defined")) try: - f = open(self.file["control_points"], "r") + f = open(self.file["control_points"]) GCPcnt = 0 for line in f: diff --git a/gui/wxpython/location_wizard/wizard.py b/gui/wxpython/location_wizard/wizard.py index 2cccdf5e301..28ce94fbc48 100644 --- a/gui/wxpython/location_wizard/wizard.py +++ b/gui/wxpython/location_wizard/wizard.py @@ -2543,7 +2543,7 @@ def __readData(self): """Get georeferencing information from tables in $GISBASE/etc/proj""" # read projection and parameters - f = open(os.path.join(globalvar.ETCDIR, "proj", "parms.table"), "r") + f = open(os.path.join(globalvar.ETCDIR, "proj", "parms.table")) self.projections = {} self.projdesc = {} for line in f: @@ -2566,7 +2566,7 @@ def __readData(self): f.close() # read datum definitions - f = open(os.path.join(globalvar.ETCDIR, "proj", "datum.table"), "r") + f = open(os.path.join(globalvar.ETCDIR, "proj", "datum.table")) self.datums = {} paramslist = [] for line in f: @@ -2584,7 +2584,7 @@ def __readData(self): f.close() # read Earth-based ellipsiod definitions - f = open(os.path.join(globalvar.ETCDIR, "proj", "ellipse.table"), "r") + f = open(os.path.join(globalvar.ETCDIR, "proj", "ellipse.table")) self.ellipsoids = {} for line in f: line = line.expandtabs(1) @@ -2600,9 +2600,7 @@ def __readData(self): f.close() # read Planetary ellipsiod definitions - f = open( - os.path.join(globalvar.ETCDIR, "proj", "ellipse.table.solar.system"), "r" - ) + f = open(os.path.join(globalvar.ETCDIR, "proj", "ellipse.table.solar.system")) self.planetary_ellipsoids = {} for line in f: line = line.expandtabs(1) @@ -2618,7 +2616,7 @@ def __readData(self): f.close() # read projection parameter description and parsing table - f = open(os.path.join(globalvar.ETCDIR, "proj", "desc.table"), "r") + f = open(os.path.join(globalvar.ETCDIR, "proj", "desc.table")) self.paramdesc = {} for line in f: line = line.strip() diff --git a/gui/wxpython/mapdisp/main.py b/gui/wxpython/mapdisp/main.py index 1e94d3d8ba2..016261d248d 100644 --- a/gui/wxpython/mapdisp/main.py +++ b/gui/wxpython/mapdisp/main.py @@ -108,7 +108,7 @@ def __init__(self, giface, cmdfile=None, mapfile=None): self.renderMgr = RenderMapMgr(self) # update legend file variable with the one d.mon uses - with open(monFile["env"], "r") as f: + with open(monFile["env"]) as f: lines = f.readlines() for line in lines: if "GRASS_LEGEND_FILE" in line: @@ -123,7 +123,7 @@ def GetLayersFromCmdFile(self): nlayers = 0 try: - fd = open(self.cmdfile, "r") + fd = open(self.cmdfile) lines = fd.readlines() fd.close() # detect d.out.file, delete the line from the cmd file and export diff --git a/gui/wxpython/modules/colorrules.py b/gui/wxpython/modules/colorrules.py index 5093cd7cf25..cad74f38c4b 100644 --- a/gui/wxpython/modules/colorrules.py +++ b/gui/wxpython/modules/colorrules.py @@ -700,7 +700,7 @@ def OnLoadRulesFile(self, event): self.rulesPanel.Clear() - fd = open(path, "r") + fd = open(path) self.ReadColorTable(ctable=fd.read()) fd.close() diff --git a/gui/wxpython/modules/mcalc_builder.py b/gui/wxpython/modules/mcalc_builder.py index e157375b4a4..b0f2f280409 100644 --- a/gui/wxpython/modules/mcalc_builder.py +++ b/gui/wxpython/modules/mcalc_builder.py @@ -759,7 +759,7 @@ def OnLoadExpression(self, event): return try: - fobj = open(path, "r") + fobj = open(path) mctxt = fobj.read() finally: fobj.close() diff --git a/gui/wxpython/photo2image/ip2i_manager.py b/gui/wxpython/photo2image/ip2i_manager.py index e27e763fb2a..5a2a1f14f21 100644 --- a/gui/wxpython/photo2image/ip2i_manager.py +++ b/gui/wxpython/photo2image/ip2i_manager.py @@ -118,7 +118,7 @@ def __init__( self.source_gisrc = os.environ["GISRC"] self.gisrc_dict = {} try: - f = open(self.target_gisrc, "r") + f = open(self.target_gisrc) for line in f: line = line.replace("\n", "").strip() if len(line) < 1: @@ -429,7 +429,7 @@ def __init__( import re try: - fc = open(self.file["camera"], mode="r") + fc = open(self.file["camera"]) fc_count = 0 for line in fc: fc_count += 1 @@ -438,7 +438,7 @@ def __init__( numberOfFiducial = int(line.split()[-1]) dataFiducialX = [] dataFiducialY = [] - fc = open(self.file["camera"], mode="r") + fc = open(self.file["camera"]) fc_count = 0 for line in fc: fc_count += 1 @@ -955,7 +955,7 @@ def ReadGCPs(self): GError(parent=self, message=_("target mapwin not defined")) try: - f = open(self.file["points"], "r") + f = open(self.file["points"]) GCPcnt = 0 for line in f: diff --git a/gui/wxpython/psmap/instructions.py b/gui/wxpython/psmap/instructions.py index 693e8024b57..3c1b2e6d7bb 100644 --- a/gui/wxpython/psmap/instructions.py +++ b/gui/wxpython/psmap/instructions.py @@ -1208,7 +1208,7 @@ def GetImageOrigSize(self, imagePath): # if eps, read info from header if os.path.splitext(fileName)[1].lower() == ".eps": bbInfo = "%%BoundingBox" - file = open(imagePath, "r") + file = open(imagePath) w = h = 0 while file: line = file.readline() diff --git a/gui/wxpython/vnet/vnet_data.py b/gui/wxpython/vnet/vnet_data.py index 6404fcac5c0..9c5b592e043 100644 --- a/gui/wxpython/vnet/vnet_data.py +++ b/gui/wxpython/vnet/vnet_data.py @@ -1070,7 +1070,7 @@ def GetLastModified(self): "head", ) try: - head = open(headPath, "r") + head = open(headPath) for line in head: i = line.find( "MAP DATE:", diff --git a/imagery/i.atcorr/create_iwave.py b/imagery/i.atcorr/create_iwave.py index 8afc6bebd36..a575d8089cd 100644 --- a/imagery/i.atcorr/create_iwave.py +++ b/imagery/i.atcorr/create_iwave.py @@ -59,7 +59,7 @@ def read_input(csvfile): first column is wavelength values are those of the discrete band filter functions """ - infile = open(csvfile, "r") + infile = open(csvfile) # get number of bands and band names bands = infile.readline().split(",") diff --git a/lib/init/grass.py b/lib/init/grass.py index 25486d9426c..233bcf53460 100755 --- a/lib/init/grass.py +++ b/lib/init/grass.py @@ -496,7 +496,7 @@ def create_gisrc(tmpdir, gisrcrc): def read_gisrc(filename): kv = {} try: - f = open(filename, "r") + f = open(filename) except OSError: return kv @@ -522,7 +522,7 @@ def write_gisrcrc(gisrcrc, gisrc, skip_variable=None): """Reads gisrc file and write to gisrcrc""" debug("Reading %s" % gisrc) number = 0 - with open(gisrc, "r") as f: + with open(gisrc) as f: lines = f.readlines() for line in lines: if skip_variable in line: @@ -535,7 +535,7 @@ def write_gisrcrc(gisrcrc, gisrc, skip_variable=None): def read_env_file(path): kv = {} - f = open(path, "r") + f = open(path) for line in f: k, v = line.split(":", 1) kv[k.strip()] = v.strip() @@ -1098,7 +1098,7 @@ def set_language(grass_config_dir): # Override value is stored in wxGUI preferences file. try: - with open(os.path.join(grass_config_dir, "wx.json"), "r") as json_file: + with open(os.path.join(grass_config_dir, "wx.json")) as json_file: try: language = json.load(json_file)["language"]["locale"]["lc_all"] except KeyError: diff --git a/man/build_html.py b/man/build_html.py index daa38b557f4..c89df29e290 100644 --- a/man/build_html.py +++ b/man/build_html.py @@ -393,7 +393,7 @@ def check_for_desc_override(basename): def read_file(name): - f = open(name, "r") + f = open(name) s = f.read() f.close() return s @@ -476,7 +476,7 @@ def write_html_footer(f, index_url, year=None): def get_desc(cmd): - f = open(cmd, "r") + f = open(cmd) while True: line = f.readline() if not line: diff --git a/man/build_rest.py b/man/build_rest.py index ea1dd7692d2..473827fcc86 100644 --- a/man/build_rest.py +++ b/man/build_rest.py @@ -271,7 +271,7 @@ def check_for_desc_override(basename): def read_file(name): - f = open(name, "r") + f = open(name) s = f.read() f.close() return s @@ -337,7 +337,7 @@ def write_rest_footer(f, index_url): def get_desc(cmd): - f = open(cmd, "r") + f = open(cmd) while True: line = f.readline() if not line: diff --git a/pyproject.toml b/pyproject.toml index 9e430071afc..c6cfac4185b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -258,7 +258,6 @@ ignore = [ "TRY201", # verbose-raise "TRY300", # try-consider-else "TRY301", # raise-within-try - "UP015", # redundant-open-modes "UP030", # format-literals "UP031", # printf-string-formatting "UP032", # f-string diff --git a/python/grass/grassdb/history.py b/python/grass/grassdb/history.py index 60b5459700d..d7c22eb36fc 100644 --- a/python/grass/grassdb/history.py +++ b/python/grass/grassdb/history.py @@ -81,9 +81,7 @@ def _read_from_plain_text(history_path): stores only executed commands.""" content_list = [] try: - with open( - history_path, encoding="utf-8", mode="r", errors="replace" - ) as file_history: + with open(history_path, encoding="utf-8", errors="replace") as file_history: content_list = [ {"command": line.strip(), "command_info": None} for line in file_history ] @@ -287,7 +285,7 @@ def _add_entry_to_JSON(history_path, entry): :param dict entry: entry consisting of 'command' and 'command_info' keys """ try: - with open(history_path, encoding="utf-8", mode="r") as file_history: + with open(history_path, encoding="utf-8") as file_history: existing_data = json.load(file_history) except (OSError, ValueError): existing_data = [] diff --git a/python/grass/gunittest/checkers.py b/python/grass/gunittest/checkers.py index fb46a1deb66..c63f6d08ff0 100644 --- a/python/grass/gunittest/checkers.py +++ b/python/grass/gunittest/checkers.py @@ -638,7 +638,7 @@ def text_file_md5( if prepend_lines: for line in prepend_lines: hasher.update(encode(line)) - with open(filename, "r") as f: + with open(filename) as f: for line in f: # replace platform newlines by standard newline if os.linesep != "\n": diff --git a/python/grass/gunittest/reporters.py b/python/grass/gunittest/reporters.py index 650480a234c..9535e07f871 100644 --- a/python/grass/gunittest/reporters.py +++ b/python/grass/gunittest/reporters.py @@ -51,7 +51,7 @@ def replace_in_file(file_path, pattern, repl): """ # using tmp file to store the replaced content tmp_file_path = file_path + ".tmp" - with open(file_path, "r") as old_file, open(tmp_file_path, "w") as new_file: + with open(file_path) as old_file, open(tmp_file_path, "w") as new_file: for line in old_file: new_file.write(re.sub(pattern=pattern, string=line, repl=repl)) # remove old file since it must not exist for rename/move diff --git a/python/grass/jupyter/interactivemap.py b/python/grass/jupyter/interactivemap.py index 085c2843fe7..d843ac3b6b6 100644 --- a/python/grass/jupyter/interactivemap.py +++ b/python/grass/jupyter/interactivemap.py @@ -178,7 +178,7 @@ def add_to(self, interactive_map): else: import ipyleaflet # pylint: disable=import-outside-toplevel - with open(self._filename, "r", encoding="utf-8") as file: + with open(self._filename, encoding="utf-8") as file: data = json.load(file) # allow using opacity directly to keep interface # consistent for both backends diff --git a/python/grass/pygrass/gis/__init__.py b/python/grass/pygrass/gis/__init__.py index 3f380368d28..d31bc31b972 100644 --- a/python/grass/pygrass/gis/__init__.py +++ b/python/grass/pygrass/gis/__init__.py @@ -430,7 +430,7 @@ def __iter__(self): def read(self): """Return the mapsets in the search path""" try: - with open(self.spath, "r") as f: + with open(self.spath) as f: lines = f.readlines() if lines: return [line.strip() for line in lines] diff --git a/python/grass/pygrass/modules/grid/grid.py b/python/grass/pygrass/modules/grid/grid.py index fb338ca973a..e0dbd309656 100644 --- a/python/grass/pygrass/modules/grid/grid.py +++ b/python/grass/pygrass/modules/grid/grid.py @@ -121,7 +121,7 @@ def read_gisrc(gisrc): ... genv['GISDBASE'])) True """ - with open(gisrc, "r") as gfile: + with open(gisrc) as gfile: gis = dict( [(k.strip(), v.strip()) for k, v in [row.split(":", 1) for row in gfile]] ) diff --git a/python/grass/pygrass/modules/interface/env.py b/python/grass/pygrass/modules/interface/env.py index ad6e4c4800e..1f0519d23c5 100644 --- a/python/grass/pygrass/modules/interface/env.py +++ b/python/grass/pygrass/modules/interface/env.py @@ -13,7 +13,7 @@ def get_env(): gisrc = os.environ.get("GISRC") if gisrc is None: raise RuntimeError("You are not in a GRASS session, GISRC not found.") - with open(gisrc, mode="r") as grc: + with open(gisrc) as grc: return dict( [ (k.strip(), v.strip()) diff --git a/python/grass/pygrass/raster/category.py b/python/grass/pygrass/raster/category.py index cfeb84ddde2..f439d51dcdf 100644 --- a/python/grass/pygrass/raster/category.py +++ b/python/grass/pygrass/raster/category.py @@ -299,7 +299,7 @@ def read_rules(self, filename, sep=":"): """ self.reset() - with open(filename, "r") as f: + with open(filename) as f: for row in f: cat = row.strip().split(sep) if len(cat) == 2: diff --git a/python/grass/pygrass/tests/set_mapset.py b/python/grass/pygrass/tests/set_mapset.py index f9d4a96fabf..105735efa5a 100644 --- a/python/grass/pygrass/tests/set_mapset.py +++ b/python/grass/pygrass/tests/set_mapset.py @@ -13,7 +13,7 @@ def read_gisrc(gisrcpath): - gisrc = open(gisrcpath, "r") + gisrc = open(gisrcpath) diz = {} for row in gisrc: key, val = row.split(":") diff --git a/python/grass/script/core.py b/python/grass/script/core.py index 40709a11e80..ee90a41d4ef 100644 --- a/python/grass/script/core.py +++ b/python/grass/script/core.py @@ -1066,7 +1066,7 @@ def _text_to_key_value_dict( {'a': ['Hello'], 'c': [1, 2, 3, 4, 5], 'b': [1.0], 'd': ['hello', 8, 0.1]} """ - text = open(filename, "r").readlines() + text = open(filename).readlines() kvdict = KeyValue() for line in text: @@ -1276,7 +1276,7 @@ def region_env(region3d=False, flags=None, env=None, **kwargs): windfile = os.path.join( gis_env["GISDBASE"], gis_env["LOCATION_NAME"], gis_env["MAPSET"], "WIND" ) - with open(windfile, "r") as fd: + with open(windfile) as fd: grass_region = "" for line in fd: key, value = (x.strip() for x in line.split(":", 1)) diff --git a/python/grass/script/utils.py b/python/grass/script/utils.py index 2868e3111e6..aa7d53375dd 100644 --- a/python/grass/script/utils.py +++ b/python/grass/script/utils.py @@ -91,8 +91,8 @@ def diff_files(filename_a, filename_b): import difflib differ = difflib.Differ() - fh_a = open(filename_a, "r") - fh_b = open(filename_b, "r") + fh_a = open(filename_a) + fh_b = open(filename_b) return list(differ.compare(fh_a.readlines(), fh_b.readlines())) diff --git a/python/grass/temporal/register.py b/python/grass/temporal/register.py index 0cd339946b9..8b7852ab557 100644 --- a/python/grass/temporal/register.py +++ b/python/grass/temporal/register.py @@ -161,7 +161,7 @@ def register_maps_in_space_time_dataset( if hasattr(file, "readline"): fd = file else: - fd = open(file, "r") + fd = open(file) line = True while True: diff --git a/python/grass/temporal/stds_import.py b/python/grass/temporal/stds_import.py index b8223c8ee83..006ee6a387e 100644 --- a/python/grass/temporal/stds_import.py +++ b/python/grass/temporal/stds_import.py @@ -372,7 +372,7 @@ def import_stds( fs = "|" maplist = [] mapset = get_current_mapset() - list_file = open(list_file_name, "r") + list_file = open(list_file_name) new_list_file = open(new_list_file_name, "w") # get number of lines to correctly form the suffix @@ -426,7 +426,7 @@ def import_stds( # Read the init file fs = "=" init = {} - init_file = open(init_file_name, "r") + init_file = open(init_file_name) while True: line = init_file.readline() if not line: diff --git a/raster/r.contour/testsuite/test_r_contour.py b/raster/r.contour/testsuite/test_r_contour.py index 9fc0fc17469..c9ee4c9876a 100644 --- a/raster/r.contour/testsuite/test_r_contour.py +++ b/raster/r.contour/testsuite/test_r_contour.py @@ -62,7 +62,7 @@ def test_raster_contour(self): self.assertModule("v.db.select", map=self.output, file="testReport") self.assertFileExists("testReport", msg="testReport file was not created") if os.path.isfile("testReport"): - file = open("testReport", "r") + file = open("testReport") fileData = file.read() self.assertMultiLineEqual(fileData, self.test_ref_str) file.close() @@ -87,7 +87,7 @@ def test_raster_contour_cut(self): self.assertModule("v.db.select", map=self.output + "_cut", file="testReportCut") self.assertFileExists("testReportCut", msg="testReportCut file was not created") if os.path.isfile("testReportCut"): - file = open("testReportCut", "r") + file = open("testReportCut") fileData = file.read() self.assertMultiLineEqual(fileData, self.test_ref_str) file.close() diff --git a/raster/r.in.gdal/testsuite/test_r_in_gdal.py b/raster/r.in.gdal/testsuite/test_r_in_gdal.py index 2390e9838cd..477ffca89c8 100644 --- a/raster/r.in.gdal/testsuite/test_r_in_gdal.py +++ b/raster/r.in.gdal/testsuite/test_r_in_gdal.py @@ -307,7 +307,7 @@ def test_netCDF_3d_5(self): test_gdal_import_map.0000000105 """ - text_from_file = open("map_names_file.txt", "r").read() + text_from_file = open("map_names_file.txt").read() self.assertLooksLike(map_list, text_from_file) diff --git a/raster/r.what/testsuite/test_r_what.py b/raster/r.what/testsuite/test_r_what.py index fcc312e6284..68bfdeea376 100644 --- a/raster/r.what/testsuite/test_r_what.py +++ b/raster/r.what/testsuite/test_r_what.py @@ -510,7 +510,7 @@ def test_raster_what_csv(self): ) self.assertFileExists(filename="result.csv", msg="CSV file was not created") if os.path.isfile("result.csv"): - file = open("result.csv", "r") + file = open("result.csv") fileData = file.read() self.assertLooksLike( actual=fileData, diff --git a/scripts/d.correlate/d.correlate.py b/scripts/d.correlate/d.correlate.py index af681cce545..1068733a11b 100755 --- a/scripts/d.correlate/d.correlate.py +++ b/scripts/d.correlate/d.correlate.py @@ -73,7 +73,7 @@ def main(): gcore.run_command("r.stats", flags="cnA", input=(i, j), stdout=ofile) ofile.close() - ifile = open(tmpfile, "r") + ifile = open(tmpfile) first = True for line in ifile: f = line.rstrip("\r\n").split(" ") @@ -95,7 +95,7 @@ def main(): p = gcore.feed_command("d.graph", color=color) ofile = p.stdin - ifile = open(tmpfile, "r") + ifile = open(tmpfile) for line in ifile: f = line.rstrip("\r\n").split(" ") x = float(f[0]) diff --git a/scripts/d.frame/d.frame.py b/scripts/d.frame/d.frame.py index e9b44b21650..ff77316f0ec 100755 --- a/scripts/d.frame/d.frame.py +++ b/scripts/d.frame/d.frame.py @@ -91,7 +91,7 @@ def check_monitor(): def read_monitor_file(monitor, ftype="env"): mfile = check_monitor_file(monitor, ftype) try: - fd = open(mfile, "r") + fd = open(mfile) except OSError as e: fatal(_("Unable to get monitor info. %s"), e) diff --git a/scripts/db.univar/db.univar.py b/scripts/db.univar/db.univar.py index f941be42140..97dfbdb7dbe 100755 --- a/scripts/db.univar/db.univar.py +++ b/scripts/db.univar/db.univar.py @@ -77,7 +77,7 @@ def cleanup(): def sortfile(infile, outfile): - inf = open(infile, "r") + inf = open(infile) outf = open(outfile, "w") if gs.find_program("sort", "--help"): diff --git a/scripts/g.extension.all/g.extension.all.py b/scripts/g.extension.all/g.extension.all.py index 43d50073178..1b0a2a88108 100644 --- a/scripts/g.extension.all/g.extension.all.py +++ b/scripts/g.extension.all/g.extension.all.py @@ -64,7 +64,7 @@ def get_extensions(): return [] # read XML file - fo = open(fXML, "r") + fo = open(fXML) try: tree = ET.fromstring(fo.read()) except Exception as e: diff --git a/scripts/g.search.modules/g.search.modules.py b/scripts/g.search.modules/g.search.modules.py index 574b8133a9e..cb40cbdced5 100755 --- a/scripts/g.search.modules/g.search.modules.py +++ b/scripts/g.search.modules/g.search.modules.py @@ -201,7 +201,7 @@ def _search_module( WXGUIDIR = os.path.join(os.getenv("GISBASE"), "gui", "wxpython") filename = os.path.join(WXGUIDIR, "xml", "module_items.xml") - menudata_file = open(filename, "r") + menudata_file = open(filename) menudata = ET.parse(menudata_file) menudata_file.close() @@ -212,7 +212,7 @@ def _search_module( if os.getenv("GRASS_ADDON_BASE"): filename_addons = os.path.join(os.getenv("GRASS_ADDON_BASE"), "modules.xml") if os.path.isfile(filename_addons): - addon_menudata_file = open(filename_addons, "r") + addon_menudata_file = open(filename_addons) addon_menudata = ET.parse(addon_menudata_file) addon_menudata_file.close() addon_items = addon_menudata.findall("task") @@ -221,7 +221,7 @@ def _search_module( # add system-wide installed addons to modules list filename_addons_s = os.path.join(os.getenv("GISBASE"), "modules.xml") if os.path.isfile(filename_addons_s): - addon_menudata_file_s = open(filename_addons_s, "r") + addon_menudata_file_s = open(filename_addons_s) addon_menudata_s = ET.parse(addon_menudata_file_s) addon_menudata_file_s.close() addon_items_s = addon_menudata_s.findall("task") diff --git a/scripts/r.in.wms/wms_gdal_drv.py b/scripts/r.in.wms/wms_gdal_drv.py index 69a49ca5429..830be2b593e 100644 --- a/scripts/r.in.wms/wms_gdal_drv.py +++ b/scripts/r.in.wms/wms_gdal_drv.py @@ -155,7 +155,7 @@ def _download(self): xml_file = self._createXML() # print xml file content for debug level 1 - file = open(xml_file, "r") + file = open(xml_file) gs.debug("WMS request XML:\n%s" % file.read(), 1) file.close() diff --git a/scripts/r.pack/r.pack.py b/scripts/r.pack/r.pack.py index bb3332101ee..5029f93c6d2 100644 --- a/scripts/r.pack/r.pack.py +++ b/scripts/r.pack/r.pack.py @@ -95,7 +95,7 @@ def main(): if map_file["file"]: vrt = os.path.join(map_file["file"], "vrt") if os.path.exists(vrt): - with open(vrt, "r") as f: + with open(vrt) as f: for r in f: map, mapset = r.split("@") map_basedir = os.path.sep.join( diff --git a/scripts/v.unpack/v.unpack.py b/scripts/v.unpack/v.unpack.py index 419e133b195..ea75d443b57 100644 --- a/scripts/v.unpack/v.unpack.py +++ b/scripts/v.unpack/v.unpack.py @@ -218,7 +218,7 @@ def main(): todb = dbconn["database"] # return the list of old connection for extract layer number and key - dbln = open(os.path.join(new_dir, "dbln"), "r") + dbln = open(os.path.join(new_dir, "dbln")) dbnlist = dbln.readlines() dbln.close() # check if dbf or sqlite directory exists diff --git a/temporal/t.rast.what/t.rast.what.py b/temporal/t.rast.what/t.rast.what.py index 39e63180abf..b3cec73a538 100755 --- a/temporal/t.rast.what/t.rast.what.py +++ b/temporal/t.rast.what/t.rast.what.py @@ -389,7 +389,7 @@ def one_point_per_row_output( file_name = output_files[count] gs.verbose(_("Transforming r.what output file %s") % (file_name)) map_list = output_time_list[count] - in_file = open(file_name, "r") + in_file = open(file_name) for line in in_file: line = line.split(separator) if vcat: @@ -466,7 +466,7 @@ def one_point_per_col_output( for count in range(len(output_files)): file_name = output_files[count] gs.verbose(_("Transforming r.what output file %s") % (file_name)) - in_file = open(file_name, "r") + in_file = open(file_name) lines = in_file.readlines() matrix = [] @@ -563,7 +563,7 @@ def one_point_per_timerow_output( file_name = output_files[count] gs.verbose("Transforming r.what output file %s" % (file_name)) map_list = output_time_list[count] - in_file = open(file_name, "r") + in_file = open(file_name) if write_header: if first is True: diff --git a/temporal/t.remove/t.remove.py b/temporal/t.remove/t.remove.py index b5c1164cef4..91fa58dde7a 100755 --- a/temporal/t.remove/t.remove.py +++ b/temporal/t.remove/t.remove.py @@ -102,7 +102,7 @@ def main(): # Read the dataset list from file if file: - fd = open(file, "r") + fd = open(file) line = True while True: diff --git a/temporal/t.unregister/t.unregister.py b/temporal/t.unregister/t.unregister.py index 7aef44ed12b..b39b7b9819f 100755 --- a/temporal/t.unregister/t.unregister.py +++ b/temporal/t.unregister/t.unregister.py @@ -108,7 +108,7 @@ def main(): # Read the map list from file if file: - fd = open(file, "r") + fd = open(file) line = True while True: From afbc3ae7b441575faa633ac7f409e3bda3c9dd9a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 30 Sep 2024 23:38:10 +0000 Subject: [PATCH 314/471] CI(deps): Lock file maintenance (#4416) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index fcda6ed01dd..51f61fb86f9 100644 --- a/flake.lock +++ b/flake.lock @@ -19,11 +19,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1726871744, - "narHash": "sha256-V5LpfdHyQkUF7RfOaDPrZDP+oqz88lTJrMT1+stXNwo=", + "lastModified": 1727648392, + "narHash": "sha256-VTlVv1nSxImFxY6RPQpNZxvEOQ0u5s1wBFDgixySNDo=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "a1d92660c6b3b7c26fb883500a80ea9d33321be2", + "rev": "4e0c36e4dd53f35d5a6385bdae88895ec5832f70", "type": "github" }, "original": { From e427da09ba2e56d6d75bdd1d8dc5235fc31d6384 Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Mon, 30 Sep 2024 19:44:38 -0400 Subject: [PATCH 315/471] wxGUI: Fixed flake8 E722: bare 'except' in wxpython/gcp/ (#4392) --- .flake8 | 1 - gui/wxpython/gcp/manager.py | 14 -------------- 2 files changed, 15 deletions(-) diff --git a/.flake8 b/.flake8 index c3256ec22bf..ceee4512ca2 100644 --- a/.flake8 +++ b/.flake8 @@ -25,7 +25,6 @@ per-file-ignores = doc/python/m.distance.py: E501 doc/gui/wxpython/example/dialogs.py: F401 gui/scripts/d.wms.py: E501 - gui/wxpython/gcp/manager.py: E722 gui/wxpython/gui_core/*: E722 gui/wxpython/gui_core/dialogs.py: E722 gui/wxpython/gui_core/forms.py: E722 diff --git a/gui/wxpython/gcp/manager.py b/gui/wxpython/gcp/manager.py index 528d710f72c..3b6dd398b51 100644 --- a/gui/wxpython/gcp/manager.py +++ b/gui/wxpython/gcp/manager.py @@ -880,20 +880,6 @@ def OnSrcSelection(self, event): else: wx.FindWindowById(wx.ID_FORWARD).Enable(True) - try: - # set computational region to match selected map and zoom display - # to region - if maptype == "raster": - p = RunCommand("g.region", "raster=src_map") - elif maptype == "vector": - p = RunCommand("g.region", "vector=src_map") - - if p.returncode == 0: - print("returncode = ", str(p.returncode)) - self.parent.Map.region = self.parent.Map.GetRegion() - except: - pass - def OnTgtRastSelection(self, event): """Source map to display selected""" global tgt_map From 3e53e34a922c51fd84b39e2cb6569070cf8761eb Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Mon, 30 Sep 2024 23:58:29 -0400 Subject: [PATCH 316/471] wxGUI: Fixed F841 in ii2t_manager.py (#4418) * fixed e722 in ii2t_manager * revert * fixed F841 in iit2t_manager.py * removed point from ii2t_mapdisplay * updated BusyInfo to use context manager --- gui/wxpython/image2target/ii2t_manager.py | 62 +++++++++----------- gui/wxpython/image2target/ii2t_mapdisplay.py | 2 - 2 files changed, 27 insertions(+), 37 deletions(-) diff --git a/gui/wxpython/image2target/ii2t_manager.py b/gui/wxpython/image2target/ii2t_manager.py index 06693926ec3..9284c3a2b2a 100644 --- a/gui/wxpython/image2target/ii2t_manager.py +++ b/gui/wxpython/image2target/ii2t_manager.py @@ -1781,22 +1781,20 @@ def OnGeorect(self, event): else: flags = "a" - busy = wx.BusyInfo(_("Rectifying images, please wait..."), parent=self) - wx.GetApp().Yield() - - ret, msg = RunCommand( - "i.ortho.rectify", - parent=self, - getErrorMsg=True, - quiet=True, - group=self.xygroup, - extension=self.extension, - method=self.gr_method, - angle=self.grwiz.cam_angle, - flags=flags, - ) + with wx.BusyInfo(_("Rectifying images, please wait..."), parent=self): + wx.GetApp().Yield() - del busy + ret, msg = RunCommand( + "i.ortho.rectify", + parent=self, + getErrorMsg=True, + quiet=True, + group=self.xygroup, + extension=self.extension, + method=self.gr_method, + angle=self.grwiz.cam_angle, + flags=flags, + ) # provide feedback on failure if ret != 0: @@ -1828,23 +1826,21 @@ def OnGeorect(self, event): ) ret = msg = "" - busy = wx.BusyInfo( + with wx.BusyInfo( _("Rectifying vector map <%s>, please wait...") % vect, parent=self - ) - wx.GetApp().Yield() - - ret, msg = RunCommand( - "v.rectify", - parent=self, - getErrorMsg=True, - quiet=True, - input=vect, - output=self.outname, - group=self.xygroup, - order=self.gr_order, - ) - - del busy + ): + wx.GetApp().Yield() + + ret, msg = RunCommand( + "v.rectify", + parent=self, + getErrorMsg=True, + quiet=True, + input=vect, + output=self.outname, + group=self.xygroup, + order=self.gr_order, + ) # provide feedback on failure if ret != 0: @@ -1971,7 +1967,6 @@ def OnGROrder(self, event): elif self.gr_order == 2: minNumOfItems = 6 - diff = 6 - numOfItems # self.SetStatusText(_( # "Insufficient points, 6+ points needed for 2nd order")) @@ -2270,7 +2265,6 @@ def OnZoomToTarget(self, event): def OnZoomMenuGCP(self, event): """Popup Zoom menu""" - point = wx.GetMousePosition() zoommenu = Menu() # Add items to the menu @@ -3376,7 +3370,6 @@ def UpdateSettings(self): srcrenderVector = False tgtrender = False tgtrenderVector = False - reload_target = False if self.new_src_map != src_map: # remove old layer layers = self.parent.grwiz.SrcMap.GetListOfLayers() @@ -3414,7 +3407,6 @@ def UpdateSettings(self): del layers[0] layers = self.parent.grwiz.TgtMap.GetListOfLayers() # self.parent.grwiz.TgtMap.DeleteAllLayers() - reload_target = True tgt_map["raster"] = self.new_tgt_map["raster"] tgt_map["vector"] = self.new_tgt_map["vector"] diff --git a/gui/wxpython/image2target/ii2t_mapdisplay.py b/gui/wxpython/image2target/ii2t_mapdisplay.py index c148eb314d2..9ee3b77d118 100644 --- a/gui/wxpython/image2target/ii2t_mapdisplay.py +++ b/gui/wxpython/image2target/ii2t_mapdisplay.py @@ -474,7 +474,6 @@ def PrintMenu(self, event): """ Print options and output menu for map display """ - point = wx.GetMousePosition() printmenu = Menu() # Add items to the menu setup = wx.MenuItem(printmenu, wx.ID_ANY, _("Page setup")) @@ -518,7 +517,6 @@ def SaveDisplayRegion(self, event): def OnZoomMenu(self, event): """Popup Zoom menu""" - point = wx.GetMousePosition() zoommenu = Menu() # Add items to the menu From 25619dd06a5dba16414d279c9d895b7e7f444450 Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Tue, 1 Oct 2024 01:36:16 -0400 Subject: [PATCH 317/471] wxGUI: Fix E722 do not use bare 'except' warnings in `gui_core/` (#4396) * updated 722 from gui_core * updated flake8 * updated ghelp v2 * updated ghelp v3 * Update gui/wxpython/gui_core/dialogs.py Co-authored-by: Anna Petrasova * removed prints * Update dialogs.py * updated indexerror * updated exception * Update .flake8 * Update .flake8 --------- Co-authored-by: Anna Petrasova --- .flake8 | 6 ------ gui/wxpython/gui_core/dialogs.py | 4 ++-- gui/wxpython/gui_core/forms.py | 2 +- gui/wxpython/gui_core/ghelp.py | 14 +++++++------- gui/wxpython/gui_core/gselect.py | 2 +- gui/wxpython/gui_core/widgets.py | 2 +- 6 files changed, 12 insertions(+), 18 deletions(-) diff --git a/.flake8 b/.flake8 index ceee4512ca2..580aa6670a1 100644 --- a/.flake8 +++ b/.flake8 @@ -25,12 +25,6 @@ per-file-ignores = doc/python/m.distance.py: E501 doc/gui/wxpython/example/dialogs.py: F401 gui/scripts/d.wms.py: E501 - gui/wxpython/gui_core/*: E722 - gui/wxpython/gui_core/dialogs.py: E722 - gui/wxpython/gui_core/forms.py: E722 - gui/wxpython/gui_core/ghelp.py: E722 - gui/wxpython/gui_core/gselect.py: E722 - gui/wxpython/gui_core/widgets.py: E722 gui/wxpython/image2target/*: F841, E722 gui/wxpython/image2target/g.gui.image2target.py: E501, F841 gui/wxpython/iscatt/*: F841, E722, F405, F403 diff --git a/gui/wxpython/gui_core/dialogs.py b/gui/wxpython/gui_core/dialogs.py index f7c7711cadf..cd2a8dcc24d 100644 --- a/gui/wxpython/gui_core/dialogs.py +++ b/gui/wxpython/gui_core/dialogs.py @@ -1213,7 +1213,7 @@ def _filter(self, data): try: if re.compile(self.flt_pattern).search(dt): flt_data.append(dt) - except: + except re.error: pass return flt_data @@ -1656,7 +1656,7 @@ def OnFilter(self, event): try: if re.compile(event.GetString()).search(layer): list.append(layer) - except: + except re.error: pass list = naturally_sorted(list) diff --git a/gui/wxpython/gui_core/forms.py b/gui/wxpython/gui_core/forms.py index 1ef623feaf7..c1123497abf 100644 --- a/gui/wxpython/gui_core/forms.py +++ b/gui/wxpython/gui_core/forms.py @@ -2016,7 +2016,7 @@ def __init__(self, parent, giface, task, id=wx.ID_ANY, frame=None, *args, **kwar # check wildcard try: fExt = os.path.splitext(p.get("key_desc", ["*.*"])[0])[1] - except: + except IndexError: fExt = None if not fExt: fMask = "*" diff --git a/gui/wxpython/gui_core/ghelp.py b/gui/wxpython/gui_core/ghelp.py index 6713dbcc0a3..0c4f6cdadfb 100644 --- a/gui/wxpython/gui_core/ghelp.py +++ b/gui/wxpython/gui_core/ghelp.py @@ -566,15 +566,15 @@ def _langString(self, k, v): allStr = "%s :" % k.upper() try: allStr += _(" %d translated") % v["good"] - except: + except KeyError: pass try: allStr += _(" %d fuzzy") % v["fuzzy"] - except: + except KeyError: pass try: allStr += _(" %d untranslated") % v["bad"] - except: + except KeyError: pass return allStr @@ -589,7 +589,7 @@ def _langBox(self, par, k, v): ) tgood.SetForegroundColour(wx.Colour(35, 142, 35)) langBox.Add(tgood) - except: + except KeyError: tgood = StaticText(parent=par, id=wx.ID_ANY, label="") langBox.Add(tgood) try: @@ -598,7 +598,7 @@ def _langBox(self, par, k, v): ) tfuzzy.SetForegroundColour(wx.Colour(255, 142, 0)) langBox.Add(tfuzzy) - except: + except KeyError: tfuzzy = StaticText(parent=par, id=wx.ID_ANY, label="") langBox.Add(tfuzzy) try: @@ -607,7 +607,7 @@ def _langBox(self, par, k, v): ) tbad.SetForegroundColour(wx.Colour(255, 0, 0)) langBox.Add(tbad) - except: + except KeyError: tbad = StaticText(parent=par, id=wx.ID_ANY, label="") langBox.Add(tbad) return langBox @@ -844,7 +844,7 @@ def fillContentsFromFile(self, htmlFile, skipDescription=True): contents.append(line) self.SetPage("".join(contents)) self.loaded = True - except: # The Manual file was not found + except Exception: # The Manual file was not found self.loaded = False diff --git a/gui/wxpython/gui_core/gselect.py b/gui/wxpython/gui_core/gselect.py index af71cc4f550..ec873f60eb4 100644 --- a/gui/wxpython/gui_core/gselect.py +++ b/gui/wxpython/gui_core/gselect.py @@ -484,7 +484,7 @@ def GetElementList(self, elements=None, exclude=False): try: self.seltree.EnsureVisible(item) self.seltree.SelectItem(item) - except: + except Exception: pass def _getElementList(self, element, mapsets=None, elements=None, exclude=False): diff --git a/gui/wxpython/gui_core/widgets.py b/gui/wxpython/gui_core/widgets.py index 5f2582f9b1e..dc9041e3578 100644 --- a/gui/wxpython/gui_core/widgets.py +++ b/gui/wxpython/gui_core/widgets.py @@ -752,7 +752,7 @@ def _validate(self, win): if text: try: datetime.strptime(text, "%Y-%m-%d") - except: + except ValueError: self._notvalid() return False From 63829498814e0716fb6d26fe1442d81ddd82cb9d Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Tue, 1 Oct 2024 10:48:54 -0400 Subject: [PATCH 318/471] v.generalize: Fix Resource Leak issue in displacement.c (#4422) * fix Resource Leak * fix for points --- vector/v.generalize/displacement.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vector/v.generalize/displacement.c b/vector/v.generalize/displacement.c index bc7a3f15170..a4d599080e4 100644 --- a/vector/v.generalize/displacement.c +++ b/vector/v.generalize/displacement.c @@ -310,6 +310,8 @@ int snakes_displacement(struct Map_info *In, struct Map_info *Out, matrix_free(&fy); matrix_free(&dx_old); matrix_free(&dy_old); + Vect_destroy_cats_struct(Cats); + Vect_destroy_line_struct(Points); return 0; } From 13cda735ce297be1f45aca9da5c9995413a94abb Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Tue, 1 Oct 2024 14:34:29 -0400 Subject: [PATCH 319/471] wxGUI: Fixed unused 'wizard' variable - F841 in image2target/ (#4400) --- .flake8 | 2 +- gui/wxpython/image2target/g.gui.image2target.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.flake8 b/.flake8 index 580aa6670a1..d704112048f 100644 --- a/.flake8 +++ b/.flake8 @@ -26,7 +26,7 @@ per-file-ignores = doc/gui/wxpython/example/dialogs.py: F401 gui/scripts/d.wms.py: E501 gui/wxpython/image2target/*: F841, E722 - gui/wxpython/image2target/g.gui.image2target.py: E501, F841 + gui/wxpython/image2target/g.gui.image2target.py: E501 gui/wxpython/iscatt/*: F841, E722, F405, F403 gui/wxpython/lmgr/frame.py: F841, E722 # layertree still includes some formatting issues (it is ignored by Black) diff --git a/gui/wxpython/image2target/g.gui.image2target.py b/gui/wxpython/image2target/g.gui.image2target.py index 37d33c2d70e..e2704dc371b 100755 --- a/gui/wxpython/image2target/g.gui.image2target.py +++ b/gui/wxpython/image2target/g.gui.image2target.py @@ -64,7 +64,8 @@ def main(): app = wx.App() - wizard = GCPWizard(parent=None, giface=StandaloneGrassInterface()) + GCPWizard(parent=None, giface=StandaloneGrassInterface()) + app.MainLoop() From ed10037b84e790b32cfabee62ab61c45be6ac875 Mon Sep 17 00:00:00 2001 From: Anna Petrasova Date: Tue, 1 Oct 2024 14:48:11 -0400 Subject: [PATCH 320/471] tests: fix tests that were actually not testing anything (#4349) --- .../v.centroids/testsuite/test_v_centroids.py | 34 ++++++++++++------- .../v.what.vect/testsuite/test_v_what_vect.py | 17 +++++----- vector/v.select/testsuite/test_v_select.py | 33 +++++------------- 3 files changed, 39 insertions(+), 45 deletions(-) diff --git a/scripts/v.centroids/testsuite/test_v_centroids.py b/scripts/v.centroids/testsuite/test_v_centroids.py index 4b0654748a5..a7f56464ef6 100644 --- a/scripts/v.centroids/testsuite/test_v_centroids.py +++ b/scripts/v.centroids/testsuite/test_v_centroids.py @@ -12,37 +12,47 @@ class TestVCentroids(TestCase): """Test v.centroids script""" - mapName = "busroute11" - outRouteMap = "busroute11_boundary" - fromType = "line" - toType = "boundary" - outAreaMap = "busroute11_area" + region_line = "region_line" + region_boundary = "region_boundary" + region_area = "region_area" + output = "output" @classmethod def setUpClass(cls): """Create an area from a closed line""" + cls.runModule("v.in.region", output=cls.region_line, type="line") + cls.runModule("v.in.region", output=cls.region_area, type="area") cls.runModule( "v.type", - input=cls.mapName, - output=cls.outRouteMap, - from_type=cls.fromType, - to_type=cls.toType, + input=cls.region_line, + output=cls.region_boundary, + from_type="line", + to_type="boundary", ) @classmethod def tearDownClass(cls): """Remove the generated maps""" cls.runModule( - "g.remove", flags="f", type="vector", name=(cls.outRouteMap, cls.outAreaMap) + "g.remove", + flags="f", + type="vector", + name=(cls.region_line, cls.region_area, cls.region_boundary), ) + def tearDown(self): + """Remove the generated maps""" + self.runModule("g.remove", flags="f", type="vector", name=self.output) + def test_area(self): """Adds missing centroids to closed boundaries test""" module = SimpleModule( - "v.centroids", input=self.outRouteMap, output=self.outAreaMap + "v.centroids", input=self.region_boundary, output=self.output ) self.assertModule(module) - self.assertVectorExists(self.outAreaMap) + self.assertVectorInfoEqualsVectorInfo( + self.output, self.region_area, precision=1e-6 + ) if __name__ == "__main__": diff --git a/scripts/v.what.vect/testsuite/test_v_what_vect.py b/scripts/v.what.vect/testsuite/test_v_what_vect.py index 09ade33668f..2385a71c400 100644 --- a/scripts/v.what.vect/testsuite/test_v_what_vect.py +++ b/scripts/v.what.vect/testsuite/test_v_what_vect.py @@ -9,7 +9,6 @@ from grass.gunittest.gmodules import SimpleModule from grass.script.core import run_command -from grass.script.utils import decode class TestVWhatVect(TestCase): @@ -29,20 +28,20 @@ def tearDownClass(cls): def test_what_vect(self): """Uploads vector values""" - run_command("v.db.addcolumn", map=self.mapName, columns="urb_name varchar(25)") + run_command("v.db.addcolumn", map=self.mapName, columns="geology_cat integer") module = SimpleModule( "v.what.vect", map=self.mapName, - query_map="urbanarea", - column="urb_name", - query_column="NAME", + query_map="geology", + column="geology_cat", + query_column="cat", ) self.assertModule(module) - - m = SimpleModule("v.db.select", map=self.mapName) - self.assertModule(m) - self.assertRegex(decode(m.outputs.stdout), "urb_name") + minmax = "min=11\nmax=1810" + self.assertVectorFitsUnivar( + map=self.mapName, column="geology_cat", reference=minmax + ) if __name__ == "__main__": diff --git a/vector/v.select/testsuite/test_v_select.py b/vector/v.select/testsuite/test_v_select.py index c74cdb53761..b8564505a6a 100644 --- a/vector/v.select/testsuite/test_v_select.py +++ b/vector/v.select/testsuite/test_v_select.py @@ -13,22 +13,9 @@ class TestRasterReport(TestCase): - binput = "bridges" + binput = "zipcodes" ainput = "geology" output = "testvselect" - overlap = "geonames_wake" - disjoint = "schools_wake" - equals = "streets_wake" - touches = "zipcodes_wake" - within = "geonames_wake" - - @classmethod - def setUpClass(cls): - cls.use_temp_region() - - @classmethod - def tearDownClass(cls): - cls.del_temp_region() def tearDown(cls): cls.runModule("g.remove", type="vector", flags="f", name=cls.output) @@ -42,8 +29,8 @@ def test_opo(self): output=self.output, operator="overlap", ) - topology = {"points": 1088, "lines": 0, "areas": 0} - self.assertVectorFitsTopoInfo(self.overlap, topology) + topology = {"areas": 97} + self.assertVectorFitsTopoInfo(self.output, topology) def test_opd(self): """Testign operator disjoint""" @@ -54,8 +41,8 @@ def test_opd(self): output=self.output, operator="disjoint", ) - topology = {"points": 167, "lines": 0, "areas": 0} - self.assertVectorFitsTopoInfo(self.disjoint, topology) + topology = {"areas": 1770} + self.assertVectorFitsTopoInfo(self.output, topology) def test_ope(self): """Testing operator equals""" @@ -66,8 +53,7 @@ def test_ope(self): output=self.output, operator="equals", ) - topology = {"points": 0, "lines": 49746, "areas": 0} - self.assertVectorFitsTopoInfo(self.equals, topology) + self.assertVectorDoesNotExist(self.output) def test_opt(self): """Testing operator touches""" @@ -78,8 +64,7 @@ def test_opt(self): output=self.output, operator="touches", ) - topology = {"points": 0, "lines": 0, "areas": 48} - self.assertVectorFitsTopoInfo(self.touches, topology) + self.assertVectorDoesNotExist(self.output) def test_opw(self): """Testing operator within""" @@ -90,8 +75,8 @@ def test_opw(self): output=self.output, operator="within", ) - topology = {"points": 1088, "lines": 0, "areas": 0} - self.assertVectorFitsTopoInfo(self.within, topology) + topology = {"areas": 17} + self.assertVectorFitsTopoInfo(self.output, topology) if __name__ == "__main__": From e9768eb98006d002cc92e9d047e17797a4d7a79b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 1 Oct 2024 14:52:28 -0400 Subject: [PATCH 321/471] CI(deps): Update codecov/codecov-action action to v4.6.0 (#4425) --- .github/workflows/pytest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 4ff31728abe..0123b01fc0e 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -108,7 +108,7 @@ jobs: coverage html - name: Upload coverage reports to Codecov - uses: codecov/codecov-action@e28ff129e5465c2c0dcc6f003fc735cb6ae0c673 # v4.5.0 + uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4.6.0 with: verbose: true flags: pytest-python-${{ matrix.python-version }} From 3f61cd7625f728ff0795023e97529e2ec896c5cc Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Tue, 1 Oct 2024 17:01:55 -0400 Subject: [PATCH 322/471] wxGUI: Fixed E722 in ii2t_gis_set.py (#4417) * fix 722 in iit2t_gis_set * added ii2t-manager * Update ii2t_gis_set.py * Update ii2t_manager.py * Update ii2t_gis_set.py --- gui/wxpython/image2target/ii2t_gis_set.py | 12 ++++++------ gui/wxpython/image2target/ii2t_manager.py | 14 -------------- 2 files changed, 6 insertions(+), 20 deletions(-) diff --git a/gui/wxpython/image2target/ii2t_gis_set.py b/gui/wxpython/image2target/ii2t_gis_set.py index e63e39ba365..4e5a5aee200 100644 --- a/gui/wxpython/image2target/ii2t_gis_set.py +++ b/gui/wxpython/image2target/ii2t_gis_set.py @@ -96,7 +96,7 @@ def __init__(self, parent=None, id=wx.ID_ANY, style=wx.DEFAULT_FRAME_STYLE): self.hbitmap = wx.StaticBitmap( self.panel, wx.ID_ANY, wx.Bitmap(name=name, type=wx.BITMAP_TYPE_PNG) ) - except: + except Exception: self.hbitmap = wx.StaticBitmap( self.panel, wx.ID_ANY, BitmapFromImage(wx.EmptyImage(530, 150)) ) @@ -824,8 +824,8 @@ def DeleteMapset(self, event): shutil.rmtree(os.path.join(self.gisdbase, location, mapset)) self.OnSelectLocation(None) self.lbmapsets.SetSelection(0) - except: - wx.MessageBox(message=_("Unable to delete mapset")) + except OSError as e: + wx.MessageBox(message=_("Unable to delete mapset: %s") % str(e)) dlg.Destroy() @@ -856,8 +856,8 @@ def DeleteLocation(self, event): self.lblocations.SetSelection(0) self.OnSelectLocation(None) self.lbmapsets.SetSelection(0) - except: - wx.MessageBox(message=_("Unable to delete location")) + except OSError as e: + wx.MessageBox(message=_("Unable to delete location: %s") % str(e)) dlg.Destroy() @@ -1163,7 +1163,7 @@ def _getDefaultMapsetName(self): defaultName = getpass.getuser() # raise error if not ascii (not valid mapset name) defaultName.encode("ascii") - except: # whatever might go wrong + except Exception: # whatever might go wrong defaultName = "user" return defaultName diff --git a/gui/wxpython/image2target/ii2t_manager.py b/gui/wxpython/image2target/ii2t_manager.py index 9284c3a2b2a..3adcf5ef9bd 100644 --- a/gui/wxpython/image2target/ii2t_manager.py +++ b/gui/wxpython/image2target/ii2t_manager.py @@ -884,20 +884,6 @@ def OnSrcSelection(self, event): else: wx.FindWindowById(wx.ID_FORWARD).Enable(True) - try: - # set computational region to match selected map and zoom display - # to region - if maptype == "raster": - p = RunCommand("g.region", "raster=src_map") - elif maptype == "vector": - p = RunCommand("g.region", "vector=src_map") - - if p.returncode == 0: - print("returncode = ", str(p.returncode)) - self.parent.Map.region = self.parent.Map.GetRegion() - except: - pass - def OnTgtRastSelection(self, event): """Source map to display selected""" global tgt_map From 61407f0e853ec967bf3afdf5870fc377a52c7bf6 Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Wed, 2 Oct 2024 12:07:52 -0400 Subject: [PATCH 323/471] wxGUI: Fixed E722: bare 'except' in iscatt/ (#4427) --- .flake8 | 4 ++-- gui/wxpython/iscatt/controllers.py | 4 ++-- gui/wxpython/iscatt/frame.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.flake8 b/.flake8 index d704112048f..32b0191e00c 100644 --- a/.flake8 +++ b/.flake8 @@ -26,8 +26,8 @@ per-file-ignores = doc/gui/wxpython/example/dialogs.py: F401 gui/scripts/d.wms.py: E501 gui/wxpython/image2target/*: F841, E722 - gui/wxpython/image2target/g.gui.image2target.py: E501 - gui/wxpython/iscatt/*: F841, E722, F405, F403 + gui/wxpython/image2target/g.gui.image2target.py: E501, F841 + gui/wxpython/iscatt/*: F841, F405, F403 gui/wxpython/lmgr/frame.py: F841, E722 # layertree still includes some formatting issues (it is ignored by Black) gui/wxpython/lmgr/layertree.py: E722, E266, W504, E225 diff --git a/gui/wxpython/iscatt/controllers.py b/gui/wxpython/iscatt/controllers.py index e8884f4f78b..aed917aacad 100644 --- a/gui/wxpython/iscatt/controllers.py +++ b/gui/wxpython/iscatt/controllers.py @@ -597,7 +597,7 @@ def _renderscattplts(self, scatt_ids, cats, cats_attrs): try: self.cat_ids.remove(c) scatt_dt[c]["render"] = True - except: + except ValueError: scatt_dt[c]["render"] = False if self.scatt_mgr.pol_sel_mode[0]: @@ -674,7 +674,7 @@ def ChangePosition(self, cat_id, new_pos): try: pos = self.cats_ids.index(cat_id) - except: + except ValueError: return False if pos > new_pos: diff --git a/gui/wxpython/iscatt/frame.py b/gui/wxpython/iscatt/frame.py index 45f50622d2f..803b2344144 100644 --- a/gui/wxpython/iscatt/frame.py +++ b/gui/wxpython/iscatt/frame.py @@ -249,7 +249,7 @@ def CursorPlotMove(self, x, y, scatt_id): x = int(round(x)) y = int(round(y)) coords = True - except: + except TypeError: coords = False pane = self._getPane(scatt_id) From fa3042417609aedb31739056e19fbe89c0675dfe Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 2 Oct 2024 13:08:36 -0400 Subject: [PATCH 324/471] CI(deps): Update mamba-org/setup-micromamba action to v1.10.0 (#4429) --- .github/workflows/macos.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 664649fa5c6..e1166cdacc2 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -50,7 +50,7 @@ jobs: # Year and week of year so cache key changes weekly run: echo "date=$(date +%Y-%U)" >> "${GITHUB_OUTPUT}" - name: Setup Mamba - uses: mamba-org/setup-micromamba@f8b8a1e23a26f60a44c853292711bacfd3eac822 # v1.9.0 + uses: mamba-org/setup-micromamba@59b11321ffd9186cd5165633a02c5bba47de6d13 # v1.10.0 with: init-shell: bash environment-file: .github/workflows/macos_dependencies.txt From 1b639db7bcc630ed2e04332bcd5b9231300ce862 Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Wed, 2 Oct 2024 13:39:26 -0400 Subject: [PATCH 325/471] wxGUI: Fix F405 error by explicitly importing required modules in iscatt/ (#4426) * update 405 in core_c * updated flake8 * support explicit imports * ran isort to make sort imports --- .flake8 | 2 +- gui/wxpython/iscatt/core_c.py | 27 +++++++++++++++++++++++---- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/.flake8 b/.flake8 index 32b0191e00c..09706c297d9 100644 --- a/.flake8 +++ b/.flake8 @@ -27,7 +27,7 @@ per-file-ignores = gui/scripts/d.wms.py: E501 gui/wxpython/image2target/*: F841, E722 gui/wxpython/image2target/g.gui.image2target.py: E501, F841 - gui/wxpython/iscatt/*: F841, F405, F403 + gui/wxpython/iscatt/*: F841, E722 gui/wxpython/lmgr/frame.py: F841, E722 # layertree still includes some formatting issues (it is ignored by Black) gui/wxpython/lmgr/layertree.py: E722, E266, W504, E225 diff --git a/gui/wxpython/iscatt/core_c.py b/gui/wxpython/iscatt/core_c.py index a2a3d5da9da..4678d2654b1 100644 --- a/gui/wxpython/iscatt/core_c.py +++ b/gui/wxpython/iscatt/core_c.py @@ -11,15 +11,34 @@ @author Stepan Turek (mentor: Martin Landa) """ +import ctypes import sys -import numpy as np +from ctypes import POINTER, c_char_p, c_double, c_int, c_uint8, pointer from multiprocessing import Process, Queue -from ctypes import * +import numpy as np try: - from grass.lib.imagery import * from grass.lib.gis import G_get_window + from grass.lib.imagery import ( + SC_SCATT_CONDITIONS, + SC_SCATT_DATA, + I_apply_colormap, + I_compute_scatts, + I_create_cat_rast, + I_insert_patch_to_cat_rast, + I_merge_arrays, + I_rasterize, + I_sc_add_cat, + I_sc_free_cats, + I_sc_init_cats, + I_sc_insert_scatt_data, + I_scd_init_scatt_data, + scdScattData, + struct_Cell_head, + struct_Range, + struct_scCats, + ) except ImportError as e: sys.stderr.write(_("Loading ctypes libs failed")) @@ -238,7 +257,7 @@ def _getComputationStruct(cats, cats_rasts, cats_type, n_bands): scatt_vals = scdScattData() - c_void_p = ctypes.POINTER(ctypes.c_void_p) + c_void_p = POINTER(ctypes.c_void_p) if cats_type == SC_SCATT_DATA: vals[:] = 0 From f0a997ca86ff93740e1549fbd6dc5bbf40b19441 Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Wed, 2 Oct 2024 17:52:05 -0400 Subject: [PATCH 326/471] wxGUI: FIxed F841 is iscatt/core_c.py (#4430) * fixed 841 * updated flake8 --- .flake8 | 2 +- gui/wxpython/iscatt/core_c.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.flake8 b/.flake8 index 09706c297d9..0a48712c808 100644 --- a/.flake8 +++ b/.flake8 @@ -27,7 +27,7 @@ per-file-ignores = gui/scripts/d.wms.py: E501 gui/wxpython/image2target/*: F841, E722 gui/wxpython/image2target/g.gui.image2target.py: E501, F841 - gui/wxpython/iscatt/*: F841, E722 + gui/wxpython/iscatt/*: F841 gui/wxpython/lmgr/frame.py: F841, E722 # layertree still includes some formatting issues (it is ignored by Black) gui/wxpython/lmgr/layertree.py: E722, E266, W504, E225 diff --git a/gui/wxpython/iscatt/core_c.py b/gui/wxpython/iscatt/core_c.py index 4678d2654b1..8be04964b90 100644 --- a/gui/wxpython/iscatt/core_c.py +++ b/gui/wxpython/iscatt/core_c.py @@ -40,7 +40,7 @@ struct_scCats, ) except ImportError as e: - sys.stderr.write(_("Loading ctypes libs failed")) + sys.stderr.write(_("Loading ctypes libs failed: %s") % e) from core.gcmd import GException from grass.script import encode @@ -288,7 +288,6 @@ def _updateCatRastProcess(patch_rast, region, cat_rast, output_queue): def _rasterize(polygon, rast, region, value, output_queue): - pol_size = len(polygon) * 2 pol = np.array(polygon, dtype=float) c_uint8_p = POINTER(c_uint8) From aa1c32a59a029d0f78c00452f9085a0bac379144 Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Wed, 2 Oct 2024 21:46:53 -0400 Subject: [PATCH 327/471] wxGUI: Fixed F841 in dialogs.py, frame.py (#4431) * fixed 841 * removed dlgSize comments --- gui/wxpython/iscatt/dialogs.py | 3 --- gui/wxpython/iscatt/frame.py | 3 --- 2 files changed, 6 deletions(-) diff --git a/gui/wxpython/iscatt/dialogs.py b/gui/wxpython/iscatt/dialogs.py index 3e2d7fa7a59..a2d054b64a4 100644 --- a/gui/wxpython/iscatt/dialogs.py +++ b/gui/wxpython/iscatt/dialogs.py @@ -90,8 +90,6 @@ def _layout(self): border = wx.BoxSizer(wx.VERTICAL) dialogSizer = wx.BoxSizer(wx.VERTICAL) - regionSizer = wx.BoxSizer(wx.HORIZONTAL) - dialogSizer.Add( self._addSelectSizer(title=self.band_1_label, sel=self.band_1_ch) ) @@ -356,7 +354,6 @@ def __init__( self.scatt_mgr = scatt_mgr - maxValue = 1e8 self.parent = parent self.settings = {} diff --git a/gui/wxpython/iscatt/frame.py b/gui/wxpython/iscatt/frame.py index 803b2344144..122aa053b9b 100644 --- a/gui/wxpython/iscatt/frame.py +++ b/gui/wxpython/iscatt/frame.py @@ -221,9 +221,6 @@ def __init__(self, parent, scatt_mgr, id=wx.ID_ANY): self.Bind(aui.EVT_AUI_PANE_CLOSE, self.OnPlotPaneClosed) - dlgSize = (-1, 400) - # self.SetBestSize(dlgSize) - # self.SetInitialSize(dlgSize) self.SetAutoLayout(1) # fix goutput's pane size (required for Mac OSX) # if self.gwindow: From a4d3ceacd3885463334972db327ac4310e180e74 Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Wed, 2 Oct 2024 21:50:15 -0400 Subject: [PATCH 328/471] v.colors: Fix resource leak issue in scan_attr.c file (#4423) * fix resource leak * close database driver --- vector/v.colors/scan_attr.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/vector/v.colors/scan_attr.c b/vector/v.colors/scan_attr.c index 18c51635f81..e805cd74a7f 100644 --- a/vector/v.colors/scan_attr.c +++ b/vector/v.colors/scan_attr.c @@ -42,6 +42,8 @@ int scan_attr(struct Map_info *Map, int layer, const char *column_name, &cvarr); if (nrec < 1) { G_important_message(_("No data selected")); + Vect_destroy_field_info(fi); + db_close_database(driver); return 0; } @@ -100,6 +102,7 @@ int scan_attr(struct Map_info *Map, int layer, const char *column_name, } db_close_database(driver); + Vect_destroy_field_info(fi); return is_fp; } From 617828d7f4f363006010734126b4104a0b389fc5 Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Wed, 2 Oct 2024 21:51:20 -0400 Subject: [PATCH 329/471] r.fill.dir: Fix unchecked return value in filldir.c (#4433) * fix unchecked value * Update raster/r.fill.dir/filldir.c * G_fatal --- raster/r.fill.dir/filldir.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/raster/r.fill.dir/filldir.c b/raster/r.fill.dir/filldir.c index 55380b7dfc5..cfe0ae69586 100644 --- a/raster/r.fill.dir/filldir.c +++ b/raster/r.fill.dir/filldir.c @@ -145,14 +145,20 @@ void filldir(int fe, int fd, int nl, struct band3 *bnd) CELL *dir; /* fill single-cell depressions, except on outer rows and columns */ - lseek(fe, 0, SEEK_SET); + if (lseek(fe, 0, SEEK_SET) == -1) { + G_fatal_error(_("Unable to seek: %s"), strerror(errno)); + } advance_band3(fe, bnd); advance_band3(fe, bnd); for (i = 1; i < nl - 1; i += 1) { - lseek(fe, (off_t)(i + 1) * bnd->sz, SEEK_SET); + if (lseek(fe, (off_t)(i + 1) * bnd->sz, SEEK_SET) == -1) { + G_fatal_error(_("Unable to seek: %s"), strerror(errno)); + } advance_band3(fe, bnd); if (fill_row(nl, bnd->ns, bnd)) { - lseek(fe, (off_t)i * bnd->sz, SEEK_SET); + if (lseek(fe, (off_t)i * bnd->sz, SEEK_SET) == -1) { + G_fatal_error(_("Unable to seek: %s"), strerror(errno)); + } if (write(fe, bnd->b[1], bnd->sz) < 0) G_fatal_error(_("File writing error in %s() %d:%s"), __func__, errno, strerror(errno)); @@ -172,8 +178,12 @@ void filldir(int fe, int fd, int nl, struct band3 *bnd) dir = G_calloc(bnd->ns, sizeof(CELL)); bufsz = bnd->ns * sizeof(CELL); - lseek(fe, 0, SEEK_SET); - lseek(fd, 0, SEEK_SET); + if (lseek(fe, 0, SEEK_SET) == -1) { + G_fatal_error(_("Unable to seek: %s"), strerror(errno)); + } + if (lseek(fd, 0, SEEK_SET) == -1) { + G_fatal_error(_("Unable to seek: %s"), strerror(errno)); + } advance_band3(fe, bnd); for (i = 0; i < nl; i += 1) { advance_band3(fe, bnd); From 4bec7147e29fc81951498e38e382df140be6956d Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Wed, 2 Oct 2024 21:53:02 -0400 Subject: [PATCH 330/471] r.thin: Fix unchecked return value in io.c (#4434) check return value --- raster/r.thin/io.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/raster/r.thin/io.c b/raster/r.thin/io.c index d5ba76245c3..3b2d0bd8aa3 100644 --- a/raster/r.thin/io.c +++ b/raster/r.thin/io.c @@ -67,13 +67,17 @@ int put_a_row(int row, CELL *buf) static int read_row(int file, void *buf, int row, int buf_len) { - lseek(file, ((off_t)row) * buf_len, 0); + if (lseek(file, ((off_t)row) * buf_len, 0) == -1) { + G_fatal_error(_("Unable to seek: %s"), strerror(errno)); + } return (read(file, buf, buf_len) == buf_len); } static int write_row(int file, const void *buf, int row, int buf_len) { - lseek(file, ((off_t)row) * buf_len, 0); + if (lseek(file, ((off_t)row) * buf_len, 0) == -1) { + G_fatal_error(_("Unable to seek: %s"), strerror(errno)); + } return (write(file, buf, buf_len) == buf_len); } From de8a6fd631409a9c50f22fd31e149f9251a26c55 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 3 Oct 2024 02:57:17 +0000 Subject: [PATCH 331/471] CI(deps): Update rui314/setup-mold digest to c49f92e (#4435) --- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/pytest.yml | 2 +- .github/workflows/python-code-quality.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 84d857a3505..acafe77daea 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -52,7 +52,7 @@ jobs: sudo apt-get install -y wget git gawk findutils xargs -a <(awk '! /^ *(#|$)/' ".github/workflows/apt.txt") -r -- \ sudo apt-get install -y --no-install-recommends --no-install-suggests - - uses: rui314/setup-mold@0bf4f07ef9048ec62a45f9dbf2f098afa49695f0 # v1 + - uses: rui314/setup-mold@c49f92ee787f4e2bba3330d01755ef8c7221699a # v1 if: ${{ matrix.language == 'c-cpp' }} - name: Initialize CodeQL diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 0123b01fc0e..cb724086ea0 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -48,7 +48,7 @@ jobs: xargs -a <(awk '! /^ *(#|$)/' ".github/workflows/apt.txt") -r -- \ sudo apt-get install -y --no-install-recommends --no-install-suggests - - uses: rui314/setup-mold@0bf4f07ef9048ec62a45f9dbf2f098afa49695f0 # v1 + - uses: rui314/setup-mold@c49f92ee787f4e2bba3330d01755ef8c7221699a # v1 - name: Install Python dependencies run: | diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index 2e156b08417..92c647875b0 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -147,7 +147,7 @@ jobs: run: | echo "MAKEFLAGS=-j$(nproc)" >> $GITHUB_ENV - - uses: rui314/setup-mold@0bf4f07ef9048ec62a45f9dbf2f098afa49695f0 # v1 + - uses: rui314/setup-mold@c49f92ee787f4e2bba3330d01755ef8c7221699a # v1 - name: Build run: .github/workflows/build_${{ matrix.os }}.sh $HOME/install From d7c79a87a790ef58ef4f727cafd89cbb5111d0f2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 3 Oct 2024 10:29:53 -0400 Subject: [PATCH 332/471] CI(deps): Update docker/setup-buildx-action action to v3.7.0 (#4436) --- .github/workflows/docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index f8440e8e71f..4056ed63ea4 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -68,7 +68,7 @@ jobs: - name: Set up QEMU uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # v3.2.0 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # v3.6.1 + uses: docker/setup-buildx-action@8026d2bc3645ea78b0d2544766a1225eb5691f89 # v3.7.0 - name: Login to DockerHub uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 with: From 7b73900583bb79181ccc78d69da8f455dbae4f05 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 3 Oct 2024 12:31:36 -0400 Subject: [PATCH 333/471] CI(deps): Update mamba-org/setup-micromamba action to v1.11.0 (#4437) --- .github/workflows/macos.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index e1166cdacc2..d9fdca6b058 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -50,7 +50,7 @@ jobs: # Year and week of year so cache key changes weekly run: echo "date=$(date +%Y-%U)" >> "${GITHUB_OUTPUT}" - name: Setup Mamba - uses: mamba-org/setup-micromamba@59b11321ffd9186cd5165633a02c5bba47de6d13 # v1.10.0 + uses: mamba-org/setup-micromamba@4b9113af4fba0e9e1124b252dd6497a419e7396d # v1.11.0 with: init-shell: bash environment-file: .github/workflows/macos_dependencies.txt From d4e46dcf52169167f6d490dae5ec823ffdb89914 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 4 Oct 2024 10:03:28 -0400 Subject: [PATCH 334/471] CI(deps): Update docker/setup-buildx-action action to v3.7.1 (#4448) --- .github/workflows/docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 4056ed63ea4..8a23d8e200b 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -68,7 +68,7 @@ jobs: - name: Set up QEMU uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # v3.2.0 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@8026d2bc3645ea78b0d2544766a1225eb5691f89 # v3.7.0 + uses: docker/setup-buildx-action@c47758b77c9736f4b2ef4073d4d51994fabfe349 # v3.7.1 - name: Login to DockerHub uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 with: From a96c073c137924d9146f89ab3d859e1659c92d01 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 4 Oct 2024 10:09:30 -0400 Subject: [PATCH 335/471] CI(deps): Update rui314/setup-mold digest to b015f7e (#4447) --- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/pytest.yml | 2 +- .github/workflows/python-code-quality.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index acafe77daea..1e163b5ad98 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -52,7 +52,7 @@ jobs: sudo apt-get install -y wget git gawk findutils xargs -a <(awk '! /^ *(#|$)/' ".github/workflows/apt.txt") -r -- \ sudo apt-get install -y --no-install-recommends --no-install-suggests - - uses: rui314/setup-mold@c49f92ee787f4e2bba3330d01755ef8c7221699a # v1 + - uses: rui314/setup-mold@b015f7e3f2938ad3a5ed6e5111a8c6c7c1d6db6e # v1 if: ${{ matrix.language == 'c-cpp' }} - name: Initialize CodeQL diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index cb724086ea0..f0cda6a95f5 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -48,7 +48,7 @@ jobs: xargs -a <(awk '! /^ *(#|$)/' ".github/workflows/apt.txt") -r -- \ sudo apt-get install -y --no-install-recommends --no-install-suggests - - uses: rui314/setup-mold@c49f92ee787f4e2bba3330d01755ef8c7221699a # v1 + - uses: rui314/setup-mold@b015f7e3f2938ad3a5ed6e5111a8c6c7c1d6db6e # v1 - name: Install Python dependencies run: | diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index 92c647875b0..d83747a43bd 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -147,7 +147,7 @@ jobs: run: | echo "MAKEFLAGS=-j$(nproc)" >> $GITHUB_ENV - - uses: rui314/setup-mold@c49f92ee787f4e2bba3330d01755ef8c7221699a # v1 + - uses: rui314/setup-mold@b015f7e3f2938ad3a5ed6e5111a8c6c7c1d6db6e # v1 - name: Build run: .github/workflows/build_${{ matrix.os }}.sh $HOME/install From a186638d1c939d203bb0ba830d6bf951cca2767f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 4 Oct 2024 10:12:15 -0400 Subject: [PATCH 336/471] CI(deps): Update github/codeql-action action to v3.26.11 (#4442) --- .github/workflows/codeql-analysis.yml | 4 ++-- .github/workflows/python-code-quality.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 1e163b5ad98..64994f93535 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -56,7 +56,7 @@ jobs: if: ${{ matrix.language == 'c-cpp' }} - name: Initialize CodeQL - uses: github/codeql-action/init@e2b3eafc8d227b0241d48be5f425d47c2d750a13 # v3.26.10 + uses: github/codeql-action/init@6db8d6351fd0be61f9ed8ebd12ccd35dcec51fea # v3.26.11 with: languages: ${{ matrix.language }} config-file: ./.github/codeql/codeql-config.yml @@ -81,6 +81,6 @@ jobs: run: .github/workflows/build_ubuntu-22.04.sh "${HOME}/install" - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@e2b3eafc8d227b0241d48be5f425d47c2d750a13 # v3.26.10 + uses: github/codeql-action/analyze@6db8d6351fd0be61f9ed8ebd12ccd35dcec51fea # v3.26.11 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index d83747a43bd..744ad9a3012 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -135,7 +135,7 @@ jobs: path: bandit.sarif - name: Upload SARIF File into Security Tab - uses: github/codeql-action/upload-sarif@e2b3eafc8d227b0241d48be5f425d47c2d750a13 # v3.26.10 + uses: github/codeql-action/upload-sarif@6db8d6351fd0be61f9ed8ebd12ccd35dcec51fea # v3.26.11 with: sarif_file: bandit.sarif From 359a62bb9a0e82d29a8b7626ad40b945ea58996c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 4 Oct 2024 10:17:38 -0400 Subject: [PATCH 337/471] CI(deps): Update mamba-org/setup-micromamba action to v2 (#4438) --- .github/workflows/macos.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index d9fdca6b058..a90cdcd7ad5 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -50,7 +50,7 @@ jobs: # Year and week of year so cache key changes weekly run: echo "date=$(date +%Y-%U)" >> "${GITHUB_OUTPUT}" - name: Setup Mamba - uses: mamba-org/setup-micromamba@4b9113af4fba0e9e1124b252dd6497a419e7396d # v1.11.0 + uses: mamba-org/setup-micromamba@617811f69075e3fd3ae68ca64220ad065877f246 # v2.0.0 with: init-shell: bash environment-file: .github/workflows/macos_dependencies.txt From 7f81c6dc5053b97560b66bee3db41f69e79035ca Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Fri, 4 Oct 2024 10:21:27 -0400 Subject: [PATCH 338/471] wxGUI: Fixed F841 in iscatt/ (#4432) * fixed all F841 * updated flake8 * reverted add_callback * Update plots.py --- .flake8 | 1 - gui/wxpython/iscatt/controllers.py | 3 --- gui/wxpython/iscatt/plots.py | 8 +------- 3 files changed, 1 insertion(+), 11 deletions(-) diff --git a/.flake8 b/.flake8 index 0a48712c808..737d8880c4f 100644 --- a/.flake8 +++ b/.flake8 @@ -27,7 +27,6 @@ per-file-ignores = gui/scripts/d.wms.py: E501 gui/wxpython/image2target/*: F841, E722 gui/wxpython/image2target/g.gui.image2target.py: E501, F841 - gui/wxpython/iscatt/*: F841 gui/wxpython/lmgr/frame.py: F841, E722 # layertree still includes some formatting issues (it is ignored by Black) gui/wxpython/lmgr/layertree.py: E722, E266, W504, E225 diff --git a/gui/wxpython/iscatt/controllers.py b/gui/wxpython/iscatt/controllers.py index aed917aacad..4cd1c3c66f6 100644 --- a/gui/wxpython/iscatt/controllers.py +++ b/gui/wxpython/iscatt/controllers.py @@ -192,9 +192,7 @@ def SetDataDone(self, event): del self.busy self.data_set = True - todo = event.ret self.bad_bands = event.ret - bands = self.core.GetBands() self.bad_rasts = event.ret self.cats_mgr.SetData() @@ -809,7 +807,6 @@ def ExportCatRast(self, cat_id): def OnExportCatRastDone(self, event): ret, err = event.ret if ret == 0: - cat_attrs = self.GetCategoryAttrs(event.kwds["cat_id"]) GMessage( _("Scatter plot raster of class <%s> exported to raster map <%s>.") % (event.userdata["name"], event.kwds["rast_name"]) diff --git a/gui/wxpython/iscatt/plots.py b/gui/wxpython/iscatt/plots.py index a29098e8bcd..9c6f50b3b9f 100644 --- a/gui/wxpython/iscatt/plots.py +++ b/gui/wxpython/iscatt/plots.py @@ -255,7 +255,6 @@ def Plot(self, cats_order, scatts, ellipses, styles): if not e: continue - colors = styles[cat_id]["color"].split(":") if self.transpose: e["theta"] = 360 - e["theta"] + 90 if e["theta"] >= 360: @@ -355,9 +354,6 @@ def ZoomRectangle(self, event): if event.button != 1: return - cur_xlim = self.axes.get_xlim() - cur_ylim = self.axes.get_ylim() - x1, y1 = event.xdata, event.ydata x2 = deepcopy(self.zoom_rect_coords["x"]) y2 = deepcopy(self.zoom_rect_coords["y"]) @@ -651,13 +647,11 @@ def __init__(self, ax, pol, empty_pol): x, y = zip(*self.pol.xy) - style = self._getPolygonStyle() - self.line = Line2D(x, y, marker="o", markerfacecolor="r", animated=True) self.ax.add_line(self.line) # self._update_line(pol) - cid = self.pol.add_callback(self.poly_changed) + self.pol.add_callback(self.poly_changed) self.moving_ver_idx = None # the active vert self.mode = None From 60f71359cefb75de467f8b026407cfc98179e50c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 4 Oct 2024 18:54:21 +0000 Subject: [PATCH 339/471] CI(deps): Update ruff to v0.6.9 (#4449) --- .github/workflows/python-code-quality.yml | 2 +- .pre-commit-config.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index 744ad9a3012..ac9fa5ea4cc 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -36,7 +36,7 @@ jobs: # renovate: datasource=pypi depName=bandit BANDIT_VERSION: "1.7.10" # renovate: datasource=pypi depName=ruff - RUFF_VERSION: "0.6.8" + RUFF_VERSION: "0.6.9" runs-on: ${{ matrix.os }} permissions: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b7a517fba37..f51f1637422 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -37,7 +37,7 @@ repos: ) - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.6.8 + rev: v0.6.9 hooks: # Run the linter. - id: ruff From 1e866290b8fa0e226ec4fede4697656df30b8705 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Fri, 4 Oct 2024 16:17:58 -0400 Subject: [PATCH 340/471] style: Fix invalid-escape-sequence (W605) (#4409) * style: Fix invalid-escape-sequence (W605) Ruff rule: https://docs.astral.sh/ruff/rules/invalid-escape-sequence/ * r.in.ascii: Remove test_csv and test_uncommon_delims --- imagery/i.pca/testsuite/test_pca.py | 2 +- pyproject.toml | 1 - .../r.in.ascii/testsuite/test_r_in_ascii.py | 70 ------------------- 3 files changed, 1 insertion(+), 72 deletions(-) diff --git a/imagery/i.pca/testsuite/test_pca.py b/imagery/i.pca/testsuite/test_pca.py index 8e9df84f2eb..f42fc6ddcc0 100644 --- a/imagery/i.pca/testsuite/test_pca.py +++ b/imagery/i.pca/testsuite/test_pca.py @@ -48,7 +48,7 @@ def test_pca_sample(self): cells=250325 datatype=CELL ncats=0 - comments=\"Eigen values, (vectors), and [percent importance]:PC1 4334.35 ( 0.2824, 0.3342, 0.5092,-0.0087, 0.5264, 0.5217) [83.04%]PC2 588.31 ( 0.2541, 0.1885, 0.2923,-0.7428,-0.5110,-0.0403) [11.27%]PC3 239.22 ( 0.3801, 0.3819, 0.2681, 0.6238,-0.4000,-0.2980) [ 4.58%]PC4 32.85 ( 0.1752,-0.0191,-0.4053, 0.1593,-0.4435, 0.7632) [ 0.63%]PC5 20.73 (-0.6170,-0.2514, 0.6059, 0.1734,-0.3235, 0.2330) [ 0.40%]PC6 4.08 (-0.5475, 0.8021,-0.2282,-0.0607,-0.0208, 0.0252) [ 0.08%]i.pca input=\"lsat7_2002_10,lsat7_2002_20,lsat7_2002_30,lsat7_2002_40\,lsat7_2002_50,lsat7_2002_70\" output=\"lsat7_2002_pca\" rescale=0,255 \percent=99" """ + comments=\"Eigen values, (vectors), and [percent importance]:PC1 4334.35 ( 0.2824, 0.3342, 0.5092,-0.0087, 0.5264, 0.5217) [83.04%]PC2 588.31 ( 0.2541, 0.1885, 0.2923,-0.7428,-0.5110,-0.0403) [11.27%]PC3 239.22 ( 0.3801, 0.3819, 0.2681, 0.6238,-0.4000,-0.2980) [ 4.58%]PC4 32.85 ( 0.1752,-0.0191,-0.4053, 0.1593,-0.4435, 0.7632) [ 0.63%]PC5 20.73 (-0.6170,-0.2514, 0.6059, 0.1734,-0.3235, 0.2330) [ 0.40%]PC6 4.08 (-0.5475, 0.8021,-0.2282,-0.0607,-0.0208, 0.0252) [ 0.08%]i.pca input=\"lsat7_2002_10,lsat7_2002_20,lsat7_2002_30,lsat7_2002_40\\,lsat7_2002_50,lsat7_2002_70\" output=\"lsat7_2002_pca\" rescale=0,255 \\percent=99" """ lsat7_2002_pca_univar_out = [ """n=250325 diff --git a/pyproject.toml b/pyproject.toml index c6cfac4185b..18dbdd75067 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -262,7 +262,6 @@ ignore = [ "UP031", # printf-string-formatting "UP032", # f-string "UP036", # outdated-version-block - "W605", # invalid-escape-sequence ] diff --git a/raster/r.in.ascii/testsuite/test_r_in_ascii.py b/raster/r.in.ascii/testsuite/test_r_in_ascii.py index 20e0c2a8f9e..cff31b7b3e2 100644 --- a/raster/r.in.ascii/testsuite/test_r_in_ascii.py +++ b/raster/r.in.ascii/testsuite/test_r_in_ascii.py @@ -32,44 +32,6 @@ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 """ -INPUT_TSV = """north: 4299000.00 -south: 4247000.00 -east: 528000.00 -west: 500000.00 -rows: 10 -cols: 15 -null: -9999 - -1\ 2\ 3\ 4\ 5\ 6\ 7\ 8\ 9\ 10\ 11\ 12\ 13\ 14\ 15 -1\ 2\ 3\ 4\ 5\ 6\ 7\ 8\ 9\ 10\ 11\ 12\ 13\ 14\ 15 -1\ 2\ 3\ 4\ 5\ 6\ 7\ 8\ 9\ 10\ 11\ 12\ 13\ 14\ 15 -1\ 2\ 3\ 4\ 5\ 6\ 7\ 8\ 9\ 10\ 11\ 12\ 13\ 14\ 15 -1\ 2\ 3\ 4\ 5\ 6\ 7\ 8\ 9\ 10\ 11\ 12\ 13\ 14\ 15 -1\ 2\ 3\ 4\ 5\ 6\ 7\ 8\ 9\ 10\ 11\ 12\ 13\ 14\ 15 -1\ 2\ 3\ 4\ 5\ 6\ 7\ 8\ 9\ 10\ 11\ 12\ 13\ 14\ 15 -1\ 2\ 3\ 4\ 5\ 6\ 7\ 8\ 9\ 10\ 11\ 12\ 13\ 14\ 15 -1\ 2\ 3\ 4\ 5\ 6\ 7\ 8\ 9\ 10\ 11\ 12\ 13\ 14\ 15 -1\ 2\ 3\ 4\ 5\ 6\ 7\ 8\ 9\ 10\ 11\ 12\ 13\ 14\ 15 """ - -INPUT_UNCOMMON = """north: 4299000.00 -south: 4247000.00 -east: 528000.00 -west: 500000.00 -rows: 10 -cols: 15 -null: -9999 - -1@ 2@ 3@ 4@ 5@ 6@ 7@ 8@ 9@ 10@ 11@ 12@ 13@ 14@ 15 -1@ 2@ 3@ 4@ 5@ 6@ 7@ 8@ 9@ 10@ 11@ 12@ 13@ 14@ 15 -1@ 2@ 3@ 4@ 5@ 6@ 7@ 8@ 9@ 10@ 11@ 12@ 13@ 14@ 15 -1@ 2@ 3@ 4@ 5@ 6@ 7@ 8@ 9@ 10@ 11@ 12@ 13@ 14@ 15 -1@ 2@ 3@ 4@ 5@ 6@ 7@ 8@ 9@ 10@ 11@ 12@ 13@ 14@ 15 -1@ 2@ 3@ 4@ 5@ 6@ 7@ 8@ 9@ 10@ 11@ 12@ 13@ 14@ 15 -1@ 2@ 3@ 4@ 5@ 6@ 7@ 8@ 9@ 10@ 11@ 12@ 13@ 14@ 15 -1@ 2@ 3@ 4@ 5@ 6@ 7@ 8@ 9@ 10@ 11@ 12@ 13@ 14@ 15 -1@ 2@ 3@ 4@ 5@ 6@ 7@ 8@ 9@ 10@ 11@ 12@ 13@ 14@ 15 -1@ 2@ 3@ 4@ 5@ 6@ 7@ 8@ 9@ 10@ 11@ 12@ 13@ 14@ 15 """ - class SimpleCsvTestCase(TestCase): ascii_test = "ascii" @@ -119,38 +81,6 @@ def test_text_delimeter(self): msg="ascii_test in degrees must be between 1 and 5", ) - def test_tsv(self): - """Test loading TSV""" - self.assertModule( - "r.in.ascii", - input="-", - output=self.ascii_test, - type="CELL", - stdin_=INPUT_TSV, - ) - self.assertRasterMinMax( - map=self.ascii_test, - refmin=1, - refmax=15, - msg="ascii_test in degrees must be between 1 and 15", - ) - - def test_uncommon_delims(self): - """Test loading with uncommon delimiters""" - self.assertModule( - "r.in.ascii", - input="-", - output=self.ascii_test, - type="CELL", - stdin_=INPUT_UNCOMMON, - ) - self.assertRasterMinMax( - map=self.ascii_test, - refmin=1, - refmax=15, - msg="ascii_test in degrees must be between 1 and 15", - ) - if __name__ == "__main__": test() From eebf3692cc6c18560c8d0b8b6f6f4468df0abeb1 Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Fri, 4 Oct 2024 17:37:43 -0400 Subject: [PATCH 341/471] wxGUI: Fixed F841 and E266 in lmgr/ (#4439) fixed f841 and e266 --- .flake8 | 5 ++--- gui/wxpython/lmgr/frame.py | 2 +- gui/wxpython/lmgr/layertree.py | 2 +- gui/wxpython/lmgr/workspace.py | 11 ++++++++--- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/.flake8 b/.flake8 index 737d8880c4f..5126c042439 100644 --- a/.flake8 +++ b/.flake8 @@ -27,10 +27,9 @@ per-file-ignores = gui/scripts/d.wms.py: E501 gui/wxpython/image2target/*: F841, E722 gui/wxpython/image2target/g.gui.image2target.py: E501, F841 - gui/wxpython/lmgr/frame.py: F841, E722 + gui/wxpython/lmgr/frame.py: E722 # layertree still includes some formatting issues (it is ignored by Black) - gui/wxpython/lmgr/layertree.py: E722, E266, W504, E225 - gui/wxpython/lmgr/workspace.py: F841 + gui/wxpython/lmgr/layertree.py: E722 gui/wxpython/modules/*: F841, E722 gui/wxpython/nviz/*: F841, E266, E722, F403, F405 gui/wxpython/photo2image/*: F841, E722, E265 diff --git a/gui/wxpython/lmgr/frame.py b/gui/wxpython/lmgr/frame.py index 4eae371f68b..f9ce45c813b 100644 --- a/gui/wxpython/lmgr/frame.py +++ b/gui/wxpython/lmgr/frame.py @@ -1950,7 +1950,7 @@ def AddMaps(self, mapLayers, ltype, check=False): ) return - newItem = maptree.AddLayer( + maptree.AddLayer( ltype=ltype, lname=layerName, lchecked=check, diff --git a/gui/wxpython/lmgr/layertree.py b/gui/wxpython/lmgr/layertree.py index 4779a40db9b..52dc0115480 100644 --- a/gui/wxpython/lmgr/layertree.py +++ b/gui/wxpython/lmgr/layertree.py @@ -756,7 +756,7 @@ def OnLayerContextMenu(self, event): # self.popupMenu.Enable(self.popupID['bgmap'], False) self.popupMenu.Enable(self.popupID["topo"], False) # else: - ### self.popupMenu.Enable(self.popupID['bgmap'], True) + # self.popupMenu.Enable(self.popupID['bgmap'], True) item = wx.MenuItem( self.popupMenu, id=self.popupID["meta"], text=_("Metadata") diff --git a/gui/wxpython/lmgr/workspace.py b/gui/wxpython/lmgr/workspace.py index a593c19b19c..774c22bf4ed 100644 --- a/gui/wxpython/lmgr/workspace.py +++ b/gui/wxpython/lmgr/workspace.py @@ -179,9 +179,10 @@ def Load(self, filename): parent=self.lmgr, message=_( "Reading workspace file <%s> failed.\n" - "Invalid file, unable to parse XML document." + "Invalid file, unable to parse XML document.\n" + "Error details: %s" ) - % filename, + % (filename, str(e)), ) return False @@ -436,7 +437,11 @@ def SaveToFile(self, filename): except Exception as e: GError( parent=self.lmgr, - message=_("Writing current settings to workspace file failed."), + message=_( + "Writing current settings to workspace file <%s> failed.\n" + "Error details: %s" + ) + % (tmpfile, str(e)), ) return False From a16a02f7eb1aec90bcf7055858ee729e45905b67 Mon Sep 17 00:00:00 2001 From: Yann Chemin Date: Sat, 5 Oct 2024 01:36:07 +0200 Subject: [PATCH 342/471] i.atcorr: update man page to fix numbering of AVIRIS bands and make style consistent (#4445) --- imagery/i.atcorr/i.atcorr.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/imagery/i.atcorr/i.atcorr.html b/imagery/i.atcorr/i.atcorr.html index 7808705d440..7c0b772e089 100644 --- a/imagery/i.atcorr/i.atcorr.html +++ b/imagery/i.atcorr/i.atcorr.html @@ -786,13 +786,13 @@

    F. Sensor band

    - - + + - + From 093411be44f55657177cc9aab719e1e62df7b20b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Sat, 5 Oct 2024 15:49:22 -0400 Subject: [PATCH 343/471] style: Fix all remaining `PT` flake8-pytest-style issues (#4452) * style: Fix pytest-parametrize-names-wrong-type (PT006) Ruff rule: https://docs.astral.sh/ruff/rules/pytest-parametrize-names-wrong-type/ * style: Fix pytest-composite-assertion (PT018) Ruff rule: https://docs.astral.sh/ruff/rules/pytest-composite-assertion/ * style: Remove unneeded exclusions of pytest rules * style: Fix pytest-incorrect-pytest-import (PT013) Ruff rule: https://docs.astral.sh/ruff/rules/pytest-incorrect-pytest-import/ * Move PT009 exclusion to only exclude on python/grass/gunittest/case.py * grass.script: Ignore pytest-raises-too-broad (PT011) in grass_script_setup_test.py::test_init_session_finish --- pyproject.toml | 9 +-------- .../tests/grass_script_mapset_session_test.py | 12 ++++++++---- .../jupyter/tests/reprojection_renderer_test.py | 6 +++--- .../pygrass/modules/tests/grass_pygrass_grid_test.py | 4 ++-- python/grass/script/tests/grass_script_setup_test.py | 2 +- temporal/t.rast.list/tests/t_rast_list_test.py | 2 +- 6 files changed, 16 insertions(+), 19 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 18dbdd75067..5a4bd25283f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -179,13 +179,6 @@ ignore = [ "PLW1641", # eq-without-hash "PLW2901", # redefined-loop-name "PLW3201", # bad-dunder-method-name - "PT001", # pytest-fixture-incorrect-parentheses-style - "PT004", # pytest-missing-fixture-name-underscore - "PT006", # pytest-parametrize-names-wrong-type - "PT009", # pytest-unittest-assertion - "PT011", # pytest-raises-too-broad - "PT018", # pytest-composite-assertion - "PT023", # pytest-incorrect-mark-parentheses-style "PTH100", # os-path-abspath "PTH101", # os-chmod "PTH102", # os-mkdir @@ -313,12 +306,12 @@ ignore = [ "python/grass/__init__.py" = ["PYI056"] "python/grass/exp*/tests/grass_script_mapset_session_test.py" = ["SIM117"] "python/grass/exp*/tests/grass_script_tmp_mapset_session_test.py" = ["SIM117"] +"python/grass/gunittest/case.py" = ["PT009"] "python/grass/gunittest/loader.py" = ["PYI024"] "python/grass/gunittest/multireport.py" = ["PYI024"] "python/grass/gunittest/testsu*/d*/s*/s*/subsub*/t*/test_segfaut.py" = ["B018"] "python/grass/gunittest/testsuite/test_assertions_rast3d.py" = ["FLY002"] "python/grass/imaging/images2*.py" = ["SIM115"] -"python/grass/jupyter/tests/reprojection_renderer_test.py" = ["PT013"] "python/grass/jupyter/testsuite/interactivemap_test.py" = ["PGH004"] "python/grass/jupyter/testsuite/map_test.py" = ["PGH004"] "python/grass/pydispatch/signal.py" = ["A005"] diff --git a/python/grass/experimental/tests/grass_script_mapset_session_test.py b/python/grass/experimental/tests/grass_script_mapset_session_test.py index 3877e61576e..740770937dd 100644 --- a/python/grass/experimental/tests/grass_script_mapset_session_test.py +++ b/python/grass/experimental/tests/grass_script_mapset_session_test.py @@ -68,7 +68,8 @@ def test_create_overwrite(xy_session): .strip() .split() ) - assert len(rasters) == 1 and rasters[0] == "a" + assert len(rasters) == 1 + assert rasters[0] == "a" with experimental.MapsetSession( name, create=True, overwrite=True, env=xy_session.env ) as session: @@ -86,7 +87,8 @@ def test_create_overwrite(xy_session): .strip() .split() ) - assert len(rasters) == 1 and rasters[0] == "a" + assert len(rasters) == 1 + assert rasters[0] == "a" assert os.path.exists(session_file) @@ -103,7 +105,8 @@ def test_ensure(xy_session): .strip() .split() ) - assert len(rasters) == 1 and rasters[0] == "a" + assert len(rasters) == 1 + assert rasters[0] == "a" with experimental.MapsetSession(name, ensure=True, env=xy_session.env) as session: session_mapset = gs.read_command("g.mapset", flags="p", env=session.env).strip() assert name == session_mapset @@ -112,7 +115,8 @@ def test_ensure(xy_session): .strip() .split() ) - assert len(rasters) == 1 and rasters[0] == "a" + assert len(rasters) == 1 + assert rasters[0] == "a" gs.run_command("r.mapcalc", expression="b = 1", env=session.env) rasters = ( gs.read_command("g.list", type="raster", mapset=".", env=session.env) diff --git a/python/grass/jupyter/tests/reprojection_renderer_test.py b/python/grass/jupyter/tests/reprojection_renderer_test.py index 907522ab2f8..f9ef7888110 100644 --- a/python/grass/jupyter/tests/reprojection_renderer_test.py +++ b/python/grass/jupyter/tests/reprojection_renderer_test.py @@ -1,7 +1,7 @@ """Test ReprojectionRenderer functions""" from pathlib import Path -from pytest import approx +import pytest from grass.jupyter.reprojection_renderer import ReprojectionRenderer @@ -21,8 +21,8 @@ def test_render_raster(simple_dataset): assert Path(filename).exists() # Test bounding box is correct # Raster is same extent as region so no need to test bbox for use_region=True - assert bbox[0] == approx([0.00072155, -85.48874388]) - assert bbox[1] == approx([0.00000000, -85.48766880]) + assert bbox[0] == pytest.approx([0.00072155, -85.48874388]) + assert bbox[1] == pytest.approx([0.00000000, -85.48766880]) # render_vector produces json diff --git a/python/grass/pygrass/modules/tests/grass_pygrass_grid_test.py b/python/grass/pygrass/modules/tests/grass_pygrass_grid_test.py index 6a9915b6a14..c4ada9c4667 100644 --- a/python/grass/pygrass/modules/tests/grass_pygrass_grid_test.py +++ b/python/grass/pygrass/modules/tests/grass_pygrass_grid_test.py @@ -210,7 +210,7 @@ def run_grid_module(): @xfail_mp_spawn @pytest.mark.parametrize( - "width, height, processes", + ("width", "height", "processes"), [ (None, None, max_processes()), (10, None, max_processes()), @@ -249,7 +249,7 @@ def run_grid_module(): @xfail_mp_spawn @pytest.mark.needs_solo_run @pytest.mark.parametrize( - "processes, backend", + ("processes", "backend"), [ (1, "RasterRow"), (9, "RasterRow"), diff --git a/python/grass/script/tests/grass_script_setup_test.py b/python/grass/script/tests/grass_script_setup_test.py index 62a0ca37b74..36dfbb77dbb 100644 --- a/python/grass/script/tests/grass_script_setup_test.py +++ b/python/grass/script/tests/grass_script_setup_test.py @@ -51,7 +51,7 @@ def test_init_session_finish(tmp_path): gs.run_command("g.region", flags="p", env=session.env) session_file = session.env["GISRC"] session.finish() - with pytest.raises(ValueError): + with pytest.raises(ValueError): # noqa: PT011 session.finish() assert not session.active assert not os.path.exists(session_file) diff --git a/temporal/t.rast.list/tests/t_rast_list_test.py b/temporal/t.rast.list/tests/t_rast_list_test.py index fbc092175db..86e539467cd 100644 --- a/temporal/t.rast.list/tests/t_rast_list_test.py +++ b/temporal/t.rast.list/tests/t_rast_list_test.py @@ -83,7 +83,7 @@ def test_yaml(space_time_raster_dataset): @pytest.mark.needs_solo_run @pytest.mark.parametrize( - "separator,delimiter", [(None, ","), (",", ","), (";", ";"), ("tab", "\t")] + ("separator", "delimiter"), [(None, ","), (",", ","), (";", ";"), ("tab", "\t")] ) def test_csv(space_time_raster_dataset, separator, delimiter): """Check CSV can be parsed with different separators""" From 528160218c184121f854674f6b011ba5bdc18630 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Sat, 5 Oct 2024 16:38:06 -0400 Subject: [PATCH 344/471] style: Fix unnecessary-list-comprehension-dict (C404) (#4454) --- pyproject.toml | 1 - python/grass/pydispatch/robustapply.py | 2 +- python/grass/pygrass/modules/grid/grid.py | 6 ++---- python/grass/pygrass/modules/interface/env.py | 9 +++------ utils/g.html2man/ghtml.py | 2 +- 5 files changed, 7 insertions(+), 13 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 5a4bd25283f..3495f022052 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -98,7 +98,6 @@ ignore = [ "B904", # raise-without-from-inside-except "B909", # loop-iterator-mutation "BLE001", # blind-except - "C404", # unnecessary-list-comprehension-dict "C414", # unnecessary-double-cast-or-process "C416", # unnecessary-comprehension "COM812", # missing-trailing-comma diff --git a/python/grass/pydispatch/robustapply.py b/python/grass/pydispatch/robustapply.py index a050f54341a..cd52f74c22e 100644 --- a/python/grass/pydispatch/robustapply.py +++ b/python/grass/pydispatch/robustapply.py @@ -86,5 +86,5 @@ def robustApply(receiver, *arguments, **named): ) # fc does not have a **kwds type parameter, therefore # remove unacceptable arguments. - named = dict([(k, v) for k, v in named.items() if k in acceptable]) + named = {k: v for k, v in named.items() if k in acceptable} return receiver(*arguments, **named) diff --git a/python/grass/pygrass/modules/grid/grid.py b/python/grass/pygrass/modules/grid/grid.py index e0dbd309656..661d5a3df64 100644 --- a/python/grass/pygrass/modules/grid/grid.py +++ b/python/grass/pygrass/modules/grid/grid.py @@ -122,9 +122,7 @@ def read_gisrc(gisrc): True """ with open(gisrc) as gfile: - gis = dict( - [(k.strip(), v.strip()) for k, v in [row.split(":", 1) for row in gfile]] - ) + gis = {k.strip(): v.strip() for k, v in [row.split(":", 1) for row in gfile]} return gis["MAPSET"], gis["LOCATION_NAME"], gis["GISDBASE"] @@ -603,7 +601,7 @@ def get_works(self): indx = row * cols + col inms[key] = "%s@%s" % (self.inlist[key][indx], self.mset.name) # set the computational region, prepare the region parameters - bbox = dict([(k[0], str(v)) for k, v in box.items()[:-2]]) + bbox = {k[0]: str(v) for k, v in box.items()[:-2]} bbox["nsres"] = "%f" % reg.nsres bbox["ewres"] = "%f" % reg.ewres new_mset = ( diff --git a/python/grass/pygrass/modules/interface/env.py b/python/grass/pygrass/modules/interface/env.py index 1f0519d23c5..d3d6e371238 100644 --- a/python/grass/pygrass/modules/interface/env.py +++ b/python/grass/pygrass/modules/interface/env.py @@ -14,12 +14,9 @@ def get_env(): if gisrc is None: raise RuntimeError("You are not in a GRASS session, GISRC not found.") with open(gisrc) as grc: - return dict( - [ - (k.strip(), v.strip()) - for k, v in [row.split(":", 1) for row in grc if row] - ] - ) + return { + k.strip(): v.strip() for k, v in [row.split(":", 1) for row in grc if row] + } def get_debug_level(): diff --git a/utils/g.html2man/ghtml.py b/utils/g.html2man/ghtml.py index eabb494198d..1a36fd3af06 100644 --- a/utils/g.html2man/ghtml.py +++ b/utils/g.html2man/ghtml.py @@ -95,7 +95,7 @@ def setify(d): - return dict([(key, frozenset(val)) for key, val in d.items()]) + return {key: frozenset(val) for key, val in d.items()} def omit(allowed, tags): From 209739a4dfffa6a08efbfeb6dc92c17b91b0cb35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Sat, 5 Oct 2024 17:22:59 -0400 Subject: [PATCH 345/471] style: Fix unnecessary-range-start (PIE808) (#4455) As 0 is the default start value, it can be safely omitted. Ruff rule: https://docs.astral.sh/ruff/rules/unnecessary-range-start/ --- general/g.remove/testsuite/test_g_remove.py | 2 +- gui/wxpython/core/workspace.py | 2 +- gui/wxpython/gui_core/gselect.py | 2 +- gui/wxpython/gui_core/menu.py | 2 +- gui/wxpython/lmgr/frame.py | 4 ++-- gui/wxpython/main_window/frame.py | 4 ++-- gui/wxpython/mapdisp/statusbar.py | 2 +- gui/wxpython/vdigit/wxdisplay.py | 2 +- pyproject.toml | 1 - .../tests/grass_script_tmp_mapset_session_test.py | 4 ++-- scripts/d.polar/d.polar.py | 4 ++-- scripts/i.oif/i.oif.py | 2 +- scripts/i.pansharpen/i.pansharpen.py | 2 +- scripts/i.tasscap/i.tasscap.py | 2 +- scripts/v.report/v.report.py | 2 +- 15 files changed, 18 insertions(+), 19 deletions(-) diff --git a/general/g.remove/testsuite/test_g_remove.py b/general/g.remove/testsuite/test_g_remove.py index 955466f29ca..10c3018f67a 100644 --- a/general/g.remove/testsuite/test_g_remove.py +++ b/general/g.remove/testsuite/test_g_remove.py @@ -52,7 +52,7 @@ def tearDownClass(cls): def test_remove_procedure(self): """Test that maps are removed only with -f""" - for i in range(0, 10): + for i in range(10): rmapcalc("test_map_%i = 100" % i) rmapcalc("test_two = 2") diff --git a/gui/wxpython/core/workspace.py b/gui/wxpython/core/workspace.py index be4eb1fda37..22aa56bfe62 100644 --- a/gui/wxpython/core/workspace.py +++ b/gui/wxpython/core/workspace.py @@ -933,7 +933,7 @@ def __init__(self, lmgr, file): file.write("{indent}\n".format(indent=" " * self.indent)) # list of displays - for page in range(0, self.lmgr.GetLayerNotebook().GetPageCount()): + for page in range(self.lmgr.GetLayerNotebook().GetPageCount()): dispName = self.lmgr.GetLayerNotebook().GetPageText(page) mapTree = self.lmgr.GetLayerNotebook().GetPage(page).maptree region = mapTree.GetMap().GetCurrentRegion() diff --git a/gui/wxpython/gui_core/gselect.py b/gui/wxpython/gui_core/gselect.py index ec873f60eb4..86683476c2e 100644 --- a/gui/wxpython/gui_core/gselect.py +++ b/gui/wxpython/gui_core/gselect.py @@ -2745,7 +2745,7 @@ def __init__( if elements: values = [] valuesDesc = [] - for idx in range(0, len(self.values)): + for idx in range(len(self.values)): value = self.values[idx] if value in elements: values.append(value) diff --git a/gui/wxpython/gui_core/menu.py b/gui/wxpython/gui_core/menu.py index de6b49b7e93..b284461af0a 100644 --- a/gui/wxpython/gui_core/menu.py +++ b/gui/wxpython/gui_core/menu.py @@ -444,7 +444,7 @@ def RemoveFileFromHistory(self, file_index): def RemoveNonExistentFiles(self): """Remove non existent files from the history""" - for i in reversed(range(0, self._filehistory.GetCount())): + for i in reversed(range(self._filehistory.GetCount())): file = self._filehistory.GetHistoryFile(index=i) if not os.path.exists(file): self._filehistory.RemoveFileFromHistory(i=i) diff --git a/gui/wxpython/lmgr/frame.py b/gui/wxpython/lmgr/frame.py index f9ce45c813b..9673ab0f1e5 100644 --- a/gui/wxpython/lmgr/frame.py +++ b/gui/wxpython/lmgr/frame.py @@ -1093,7 +1093,7 @@ def GetMapDisplay(self, onlyCurrent=True): return None else: # -> return list of all mapdisplays mlist = [] - for idx in range(0, self.notebookLayers.GetPageCount()): + for idx in range(self.notebookLayers.GetPageCount()): mlist.append(self.notebookLayers.GetPage(idx).maptree.GetMapDisplay()) return mlist @@ -1906,7 +1906,7 @@ def _onMapDisplayStarting3dMode(self, mapDisplayPage): """Disables 3D mode for all map displays except for @p mapDisplay""" # TODO: it should be disabled also for newly created map windows # moreover mapdisp.Disable3dMode() does not work properly - for page in range(0, self.GetLayerNotebook().GetPageCount()): + for page in range(self.GetLayerNotebook().GetPageCount()): mapdisp = self.GetLayerNotebook().GetPage(page).maptree.GetMapDisplay() if self.GetLayerNotebook().GetPage(page) != mapDisplayPage: mapdisp.Disable3dMode() diff --git a/gui/wxpython/main_window/frame.py b/gui/wxpython/main_window/frame.py index 8fc8dc8dde7..0bb28afd883 100644 --- a/gui/wxpython/main_window/frame.py +++ b/gui/wxpython/main_window/frame.py @@ -1244,7 +1244,7 @@ def GetMapDisplay(self, onlyCurrent=True): return None else: # -> return list of all mapdisplays mlist = [] - for idx in range(0, self.notebookLayers.GetPageCount()): + for idx in range(self.notebookLayers.GetPageCount()): mlist.append(self.notebookLayers.GetPage(idx).maptree.GetMapDisplay()) return mlist @@ -2054,7 +2054,7 @@ def _onStarting3dMode(self, mapDisplayPage): """Disables 3D mode for all map displays except for @p mapDisplay""" # TODO: it should be disabled also for newly created map windows # moreover mapdisp.Disable3dMode() does not work properly - for page in range(0, self.GetLayerNotebook().GetPageCount()): + for page in range(self.GetLayerNotebook().GetPageCount()): mapdisp = self.GetLayerNotebook().GetPage(page).maptree.GetMapDisplay() if self.GetLayerNotebook().GetPage(page) != mapDisplayPage: mapdisp.Disable3dMode() diff --git a/gui/wxpython/mapdisp/statusbar.py b/gui/wxpython/mapdisp/statusbar.py index 72e9d0afdbc..649baebc18d 100644 --- a/gui/wxpython/mapdisp/statusbar.py +++ b/gui/wxpython/mapdisp/statusbar.py @@ -138,7 +138,7 @@ def DisableStatusbarItemsByClass(self, itemClasses): :param itemClasses list of classes of items to be disabled """ for itemClass in itemClasses: - for i in range(0, len(self.statusbarItems.values())): + for i in range(len(self.statusbarItems.values())): item = list(self.statusbarItems.values())[i] if item.__class__ == itemClass: self.disabledItems[i] = item diff --git a/gui/wxpython/vdigit/wxdisplay.py b/gui/wxpython/vdigit/wxdisplay.py index 9085aae8caf..d8e4d0a14bf 100644 --- a/gui/wxpython/vdigit/wxdisplay.py +++ b/gui/wxpython/vdigit/wxdisplay.py @@ -824,7 +824,7 @@ def SetSelected(self, ids, layer=-1): found = False cats = self.poCats.contents - for i in range(0, cats.n_cats): + for i in range(cats.n_cats): for cat in self.selected["cats"]: if cats.cat[i] == cat: found = True diff --git a/pyproject.toml b/pyproject.toml index 3495f022052..59fc30964b8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -151,7 +151,6 @@ ignore = [ "PERF402", # manual-list-copy "PERF403", # manual-dict-comprehension "PIE794", # duplicate-class-field-definition - "PIE808", # unnecessary-range-start "PLC0415", # import-outside-top-level "PLC1901", # compare-to-empty-string "PLC2701", # import-private-name diff --git a/python/grass/experimental/tests/grass_script_tmp_mapset_session_test.py b/python/grass/experimental/tests/grass_script_tmp_mapset_session_test.py index 744becc1e7b..f7098004527 100644 --- a/python/grass/experimental/tests/grass_script_tmp_mapset_session_test.py +++ b/python/grass/experimental/tests/grass_script_tmp_mapset_session_test.py @@ -74,7 +74,7 @@ def test_without_context_manager(xy_session): def test_multiple_sequential_with_context_manager(xy_session): """Session creates, starts, and finishes""" session_file = xy_session.env["GISRC"] - for i in range(0, 5): + for i in range(5): with experimental.TemporaryMapsetSession(env=xy_session.env) as session: assert session.active gs.run_command("g.region", flags="p", env=session.env) @@ -104,7 +104,7 @@ def test_multiple_parallel_without_context_manager(xy_session): """Session creates, starts, and finishes""" session_file = xy_session.env["GISRC"] sessions = [] - for i in range(0, 5): + for i in range(5): session_file = xy_session.env["GISRC"] session = experimental.TemporaryMapsetSession(env=xy_session.env) gs.run_command("g.region", flags="p", env=session.env) diff --git a/scripts/d.polar/d.polar.py b/scripts/d.polar/d.polar.py index 915177e94d4..d7e67486a0e 100755 --- a/scripts/d.polar/d.polar.py +++ b/scripts/d.polar/d.polar.py @@ -112,7 +112,7 @@ def plot_dgraph(): 50 * (1 + ring * math.sin(math.radians(i))), 50 * (1 + ring * math.cos(math.radians(i))), ) - for i in range(0, 361) + for i in range(361) ] # trend vector @@ -512,7 +512,7 @@ def main(): outercircle = [] outercircle.append('"All Data incl. NULLs') scale = 1.0 * totalnumber / totalvalidnumber * maxradius - for i in range(0, 361): + for i in range(361): a = math.radians(i) x = math.cos(a) * scale y = math.sin(a) * scale diff --git a/scripts/i.oif/i.oif.py b/scripts/i.oif/i.oif.py index aea65197954..64dffcf70b9 100755 --- a/scripts/i.oif/i.oif.py +++ b/scripts/i.oif/i.oif.py @@ -63,7 +63,7 @@ def oifcalc(sdev, corr, k1, k2, k3): def perms(bands): n = len(bands) - for i in range(0, n - 2): + for i in range(n - 2): for j in range(i + 1, n - 1): for k in range(j + 1, n): yield (bands[i], bands[j], bands[k]) diff --git a/scripts/i.pansharpen/i.pansharpen.py b/scripts/i.pansharpen/i.pansharpen.py index c62a53a536a..bb271c07deb 100755 --- a/scripts/i.pansharpen/i.pansharpen.py +++ b/scripts/i.pansharpen/i.pansharpen.py @@ -749,7 +749,7 @@ def matchhist(original, target, matched): 0 # cumulative total of cells for sum of current and all lower grey values ) - for n in range(0, 256): + for n in range(256): if str(n) in stats_dict: num_cells = stats_dict[str(n)] else: diff --git a/scripts/i.tasscap/i.tasscap.py b/scripts/i.tasscap/i.tasscap.py index 4c68c234c73..dc767fd3f19 100755 --- a/scripts/i.tasscap/i.tasscap.py +++ b/scripts/i.tasscap/i.tasscap.py @@ -352,7 +352,7 @@ def main(): # assign "Data Description" field in all four component maps num_comp = len(parms[satellites.index(satellite)]) - for i in range(0, num_comp): + for i in range(num_comp): comp = names[i] gs.run_command( "r.support", diff --git a/scripts/v.report/v.report.py b/scripts/v.report/v.report.py index 9da9a8b1de7..cec84582155 100755 --- a/scripts/v.report/v.report.py +++ b/scripts/v.report/v.report.py @@ -116,7 +116,7 @@ def main(): cols = decode(line).rstrip("\r\n").split("|") if catcol == -1: ncols = len(cols) - for i in range(0, ncols): + for i in range(ncols): if cols[i] == f["key"]: catcol = i break From cd9e0a48caecd9ff59b1d20ce32b76a374835db0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Sat, 5 Oct 2024 17:39:13 -0400 Subject: [PATCH 346/471] style: Fix unnecessary-comprehension (C416) (#4453) --- gui/wxpython/lmgr/giface.py | 6 +++--- gui/wxpython/timeline/frame.py | 9 +++------ gui/wxpython/tplot/frame.py | 9 +++------ gui/wxpython/web_services/widgets.py | 2 +- pyproject.toml | 1 - python/grass/imaging/images2gif.py | 4 ++-- python/grass/imaging/images2swf.py | 2 +- python/grass/pygrass/gis/__init__.py | 2 +- python/grass/pygrass/modules/grid/grid.py | 12 ++++++------ python/grass/pygrass/vector/basic.py | 2 +- python/grass/pygrass/vector/table.py | 2 +- python/grass/pygrass/vector/testsuite/test_table.py | 2 +- scripts/i.oif/i.oif.py | 4 +--- 13 files changed, 24 insertions(+), 33 deletions(-) diff --git a/gui/wxpython/lmgr/giface.py b/gui/wxpython/lmgr/giface.py index aa5af7223aa..484a32e8c2d 100644 --- a/gui/wxpython/lmgr/giface.py +++ b/gui/wxpython/lmgr/giface.py @@ -54,7 +54,7 @@ def __init__(self, tree): self._tree = tree def __len__(self): - return len([layer for layer in self]) + return len(list(self)) def __iter__(self): """Iterates over the contents of the list.""" @@ -66,11 +66,11 @@ def __iter__(self): def __getitem__(self, index): """Select a layer from the LayerList using the index.""" - return [layer for layer in self][index] + return list(self)[index] def __repr__(self): """Return a representation of the object.""" - return "LayerList(%r)" % [layer for layer in self] + return "LayerList(%r)" % list(self) def GetSelectedLayers(self, checkedOnly=True): items = self._tree.GetSelectedLayer(multi=True, checkedOnly=checkedOnly) diff --git a/gui/wxpython/timeline/frame.py b/gui/wxpython/timeline/frame.py index 9a783f4f5d5..7ca51cd8989 100644 --- a/gui/wxpython/timeline/frame.py +++ b/gui/wxpython/timeline/frame.py @@ -498,12 +498,9 @@ def _checkDatasets(self, datasets): if allDatasets: allDatasets = reduce(add, reduce(add, allDatasets)) mapsets = tgis.get_tgis_c_library_interface().available_mapsets() - allDatasets = [ - i - for i in sorted( - allDatasets, key=lambda dataset_info: mapsets.index(dataset_info[1]) - ) - ] + allDatasets = sorted( + allDatasets, key=lambda dataset_info: mapsets.index(dataset_info[1]) + ) for dataset in datasets: errorMsg = _("Space time dataset <%s> not found.") % dataset diff --git a/gui/wxpython/tplot/frame.py b/gui/wxpython/tplot/frame.py index 8994485dacc..f52c5a4d190 100755 --- a/gui/wxpython/tplot/frame.py +++ b/gui/wxpython/tplot/frame.py @@ -1156,12 +1156,9 @@ def _checkDatasets(self, datasets, typ): if allDatasets: allDatasets = reduce(add, reduce(add, allDatasets)) mapsets = tgis.get_tgis_c_library_interface().available_mapsets() - allDatasets = [ - i - for i in sorted( - allDatasets, key=lambda dataset_info: mapsets.index(dataset_info[1]) - ) - ] + allDatasets = sorted( + allDatasets, key=lambda dataset_info: mapsets.index(dataset_info[1]) + ) for dataset in datasets: errorMsg = _("Space time dataset <%s> not found.") % dataset diff --git a/gui/wxpython/web_services/widgets.py b/gui/wxpython/web_services/widgets.py index 3cb3af18068..473a8fb58a2 100644 --- a/gui/wxpython/web_services/widgets.py +++ b/gui/wxpython/web_services/widgets.py @@ -633,7 +633,7 @@ def UpdateWidgetsByCmd(self, cmd): # WMS standard - first layer in params is most bottom... # therefore layers order need to be reversed - l_st_list = [layer for layer in reversed(l_st_list)] + l_st_list.reverse() self.list.SelectLayers(l_st_list) params = {} diff --git a/pyproject.toml b/pyproject.toml index 59fc30964b8..f1a5aec9a1c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -99,7 +99,6 @@ ignore = [ "B909", # loop-iterator-mutation "BLE001", # blind-except "C414", # unnecessary-double-cast-or-process - "C416", # unnecessary-comprehension "COM812", # missing-trailing-comma "COM818", # trailing-comma-on-bare-tuple "D1", diff --git a/python/grass/imaging/images2gif.py b/python/grass/imaging/images2gif.py index a4dc2dc0824..fdb6423eec3 100644 --- a/python/grass/imaging/images2gif.py +++ b/python/grass/imaging/images2gif.py @@ -265,7 +265,7 @@ def handleSubRectangles(self, images, subRectangles): xy = (0, 0) if hasattr(xy, "__len__"): if len(xy) == len(images): - xy = [xxyy for xxyy in xy] + xy = list(xy) else: raise ValueError("len(xy) doesn't match amount of images.") else: @@ -594,7 +594,7 @@ def writeGifVisvis( # Check duration if hasattr(duration, "__len__"): if len(duration) == len(images): - duration = [d for d in duration] + duration = list(duration) else: raise ValueError("len(duration) doesn't match amount of images.") else: diff --git a/python/grass/imaging/images2swf.py b/python/grass/imaging/images2swf.py index 0a08ed9d46d..60352a48f5f 100644 --- a/python/grass/imaging/images2swf.py +++ b/python/grass/imaging/images2swf.py @@ -796,7 +796,7 @@ def writeSwf(filename, images, duration=0.1, repeat=True): # Check duration if hasattr(duration, "__len__"): if len(duration) == len(images2): - duration = [d for d in duration] + duration = list(duration) else: raise ValueError("len(duration) doesn't match amount of images.") else: diff --git a/python/grass/pygrass/gis/__init__.py b/python/grass/pygrass/gis/__init__.py index d31bc31b972..61768859231 100644 --- a/python/grass/pygrass/gis/__init__.py +++ b/python/grass/pygrass/gis/__init__.py @@ -271,7 +271,7 @@ def mapsets(self, pattern=None, permissions=True): [...] """ - mapsets = [mapset for mapset in self] + mapsets = [mapset for mapset in self] # noqa: C416 if permissions: mapsets = [ mapset diff --git a/python/grass/pygrass/modules/grid/grid.py b/python/grass/pygrass/modules/grid/grid.py index 661d5a3df64..ffae173ca79 100644 --- a/python/grass/pygrass/modules/grid/grid.py +++ b/python/grass/pygrass/modules/grid/grid.py @@ -145,7 +145,7 @@ def get_mapset(gisrc_src, gisrc_dst): copy_special_mapset_files(path_src, path_dst) src = Mapset(msrc, lsrc, gsrc) dst = Mapset(mdst, ldst, gdst) - visible = [m for m in src.visible] + visible = list(src.visible) if src.name not in visible: visible.append(src.name) dst.visible.extend(visible) @@ -187,7 +187,7 @@ def rmloc(r): # change gisdbase to src env["GISRC"] = gisrc_src get_grp(group=grp, env_=env) - rasts = [r for r in get_grp.outputs.stdout.split()] + rasts = list(get_grp.outputs.stdout.split()) # change gisdbase to dst env["GISRC"] = gisrc_dst rast2cp = [r for r in rasts if rmloc(r) not in all_rasts] @@ -495,15 +495,15 @@ def __init__( self.gisrc_dst = write_gisrc( self.n_mset.gisdbase, self.n_mset.location, self.n_mset.name ) - rasters = [r for r in select(self.module.inputs, "raster")] + rasters = list(select(self.module.inputs, "raster")) if rasters: copy_rasters( rasters, self.gisrc_src, self.gisrc_dst, region=self.region ) - vectors = [v for v in select(self.module.inputs, "vector")] + vectors = list(select(self.module.inputs, "vector")) if vectors: copy_vectors(vectors, self.gisrc_src, self.gisrc_dst) - groups = [g for g in select(self.module.inputs, "group")] + groups = list(select(self.module.inputs, "group")) if groups: copy_groups(groups, self.gisrc_src, self.gisrc_dst, region=self.region) self.bboxes = split_region_in_overlapping_tiles( @@ -590,7 +590,7 @@ def get_works(self): else: ldst, gdst = self.mset.location, self.mset.gisdbase cmd = self.module.get_dict() - groups = [g for g in select(self.module.inputs, "group")] + groups = list(select(self.module.inputs, "group")) for row, box_row in enumerate(self.bboxes): for col, box in enumerate(box_row): inms = None diff --git a/python/grass/pygrass/vector/basic.py b/python/grass/pygrass/vector/basic.py index 079f7ac829f..b86ae3edb4e 100644 --- a/python/grass/pygrass/vector/basic.py +++ b/python/grass/pygrass/vector/basic.py @@ -329,7 +329,7 @@ def __iter__(self): return (self.c_ilist.contents.value[i] for i in range(self.__len__())) def __repr__(self): - return "Ilist(%r)" % [i for i in self.__iter__()] + return "Ilist(%r)" % list(self.__iter__()) def __contains__(self, item): return item in self.__iter__() diff --git a/python/grass/pygrass/vector/table.py b/python/grass/pygrass/vector/table.py index 634c440a583..78c1c8709b4 100644 --- a/python/grass/pygrass/vector/table.py +++ b/python/grass/pygrass/vector/table.py @@ -944,7 +944,7 @@ def __getitem__(self, item): return self.by_name(item) def __repr__(self): - return "DBlinks(%r)" % [link for link in self.__iter__()] + return "DBlinks(%r)" % list(self.__iter__()) def by_index(self, indx): """Return a Link object by index diff --git a/python/grass/pygrass/vector/testsuite/test_table.py b/python/grass/pygrass/vector/testsuite/test_table.py index 1acb410a10f..d87786fb3bd 100644 --- a/python/grass/pygrass/vector/testsuite/test_table.py +++ b/python/grass/pygrass/vector/testsuite/test_table.py @@ -65,7 +65,7 @@ def get_table_random_values(nrows, columns): raise TypeError("Unknown column type %s for: %s" % (ctype, cname)) vals.append(COL2VALS[ctype](nrows)) dtype.append((cname, vals[-1].dtype.str)) - return np.array([v for v in zip(*vals)], dtype=dtype) + return np.array(list(zip(*vals)), dtype=dtype) class DBconnection: diff --git a/scripts/i.oif/i.oif.py b/scripts/i.oif/i.oif.py index 64dffcf70b9..0dabbe7cb38 100755 --- a/scripts/i.oif/i.oif.py +++ b/scripts/i.oif/i.oif.py @@ -122,9 +122,7 @@ def main(): grass.message(_("Calculating Correlation Matrix...")) correlation = {} - s = grass.read_command( - "r.covar", flags="r", map=[band for band in bands], quiet=True - ) + s = grass.read_command("r.covar", flags="r", map=list(bands), quiet=True) # We need to skip the first line, since r.covar prints the number of values lines = s.splitlines() From 6596d49e636060ac9e92f3dd44fff2c51fc5007f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 5 Oct 2024 23:25:13 +0000 Subject: [PATCH 347/471] CI(deps): Update pre-commit hook pre-commit/pre-commit-hooks to v5 (#4460) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f51f1637422..60865cd3432 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,7 +3,7 @@ ci: skip: [flake8] repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.6.0 + rev: v5.0.0 hooks: - id: trailing-whitespace exclude: | From 3932773e292e6ea2f3030d79fac15061f985fdd8 Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Mon, 7 Oct 2024 08:16:02 -0400 Subject: [PATCH 348/471] lib/lidar: Fix resource leak issue in raster.c (#4458) Fix resource leak --- lib/lidar/raster.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/lidar/raster.c b/lib/lidar/raster.c index 4d7305c9a9b..ad531865df7 100644 --- a/lib/lidar/raster.c +++ b/lib/lidar/raster.c @@ -3,6 +3,7 @@ #include #include #include +#include /*------------------------------------------------------------------------------------------------*/ void P_Sparse_Points(struct Map_info *Out, struct Cell_head *Elaboration, @@ -187,6 +188,7 @@ void P_Sparse_Points(struct Map_info *Out, struct Cell_head *Elaboration, } /*IF*/} /*FOR*/ db_commit_transaction(driver); + Vect_destroy_line_struct(point); return; } From aa0b4979e0ff65725c799f2f4630462a5646d238 Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Mon, 7 Oct 2024 08:16:32 -0400 Subject: [PATCH 349/471] v.reclass: Fix Resource Leak issue in reclass.c (#4457) fix Resource Leak --- vector/v.reclass/reclass.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/vector/v.reclass/reclass.c b/vector/v.reclass/reclass.c index 5af40e5473e..4300f4216ff 100644 --- a/vector/v.reclass/reclass.c +++ b/vector/v.reclass/reclass.c @@ -79,6 +79,9 @@ int reclass(struct Map_info *In, struct Map_info *Out, int type, int field, G_warning("For %d elements requested negative category (ignored, no " "category in output)", negative); + Vect_destroy_cats_struct(Cats); + Vect_destroy_cats_struct(NewCats); + Vect_destroy_line_struct(Points); return (rclelem); } From b5c4a273418060c820cad6552a82fdd085a680e6 Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Mon, 7 Oct 2024 08:40:44 -0400 Subject: [PATCH 350/471] v.rectify: Fix Copy into fix Buffer size issue (#4463) --- vector/v.rectify/main.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/vector/v.rectify/main.c b/vector/v.rectify/main.c index b02630c88ae..86999d6286d 100644 --- a/vector/v.rectify/main.c +++ b/vector/v.rectify/main.c @@ -126,7 +126,9 @@ int main(int argc, char *argv[]) if (grp->answer) { G_strip(grp->answer); - strcpy(group, grp->answer); + if (G_strlcpy(group, grp->answer, sizeof(group)) >= sizeof(group)) { + G_fatal_error(_("Group name <%s> is too long"), grp->answer); + } } else group[0] = '\0'; From de39e1c264c745553948f313e6e0716fcc6a7e19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Mon, 7 Oct 2024 10:39:48 -0400 Subject: [PATCH 351/471] style: Fix unnecessary-paren-on-raise-exception (RSE102) (#4456) Ruff rule: https://docs.astral.sh/ruff/rules/unnecessary-paren-on-raise-exception/ --- gui/wxpython/core/giface.py | 40 ++++++++++++------------- gui/wxpython/core/gthread.py | 2 +- gui/wxpython/core/layerlist.py | 2 +- gui/wxpython/mapwin/analysis.py | 8 ++--- gui/wxpython/mapwin/base.py | 8 ++--- gui/wxpython/modules/import_export.py | 6 ++-- pyproject.toml | 1 - python/grass/imaging/images2swf.py | 2 +- python/grass/pygrass/vector/geometry.py | 2 +- 9 files changed, 35 insertions(+), 36 deletions(-) diff --git a/gui/wxpython/core/giface.py b/gui/wxpython/core/giface.py index 864044593c0..3a1d8c61612 100644 --- a/gui/wxpython/core/giface.py +++ b/gui/wxpython/core/giface.py @@ -60,7 +60,7 @@ def GetSelectedLayers(self, checkedOnly=True): However, this may be the same for some implementations (e.g. it d.mon has all layers checked and selected). """ - raise NotImplementedError() + raise NotImplementedError def GetSelectedLayer(self, checkedOnly=False): """Returns selected layer or None when there is no selected layer. @@ -69,7 +69,7 @@ def GetSelectedLayer(self, checkedOnly=False): Parameter checkedOnly is here False by default. This might change if we find the right way of handling unchecked layers. """ - raise NotImplementedError() + raise NotImplementedError def AddLayer(self, ltype, name=None, checked=None, opacity=1.0, cmd=None): """Adds a new layer to the layer list. @@ -82,7 +82,7 @@ def AddLayer(self, ltype, name=None, checked=None, opacity=1.0, cmd=None): :param opacity: layer opacity level :param cmd: command (given as a list) """ - raise NotImplementedError() + raise NotImplementedError def GetLayersByName(self, name): """Returns list of layers with a given name. @@ -93,7 +93,7 @@ def GetLayersByName(self, name): if common usage is just to check the presence of layer, intoroduce a new method ContainsLayerByName(name) """ - raise NotImplementedError() + raise NotImplementedError def GetLayerByData(self, key, value): """Returns layer with specified. @@ -104,7 +104,7 @@ def GetLayerByData(self, key, value): .. warning:: Avoid using this method, it might be removed in the future. """ - raise NotImplementedError() + raise NotImplementedError class GrassInterface: @@ -118,31 +118,31 @@ class GrassInterface: def RunCmd(self, *args, **kwargs): """Executes a command.""" - raise NotImplementedError() + raise NotImplementedError def Help(self, entry): """Shows a manual page for a given entry.""" - raise NotImplementedError() + raise NotImplementedError def WriteLog(self, text, wrap=None, notification=Notification.HIGHLIGHT): """Writes log message.""" - raise NotImplementedError() + raise NotImplementedError def WriteCmdLog(self, text, pid=None, notification=Notification.MAKE_VISIBLE): """Writes message related to start or end of the command.""" - raise NotImplementedError() + raise NotImplementedError def WriteWarning(self, text): """Writes warning message for the user.""" - raise NotImplementedError() + raise NotImplementedError def WriteError(self, text): """Writes error message for the user.""" - raise NotImplementedError() + raise NotImplementedError def GetLog(self, err=False): """Returns file-like object for writing.""" - raise NotImplementedError() + raise NotImplementedError def GetLayerTree(self): """Returns LayerManager's tree GUI object. @@ -150,11 +150,11 @@ def GetLayerTree(self): Will be removed from the interface. """ - raise NotImplementedError() + raise NotImplementedError def GetLayerList(self): """Returns a layer management object.""" - raise NotImplementedError() + raise NotImplementedError def GetMapDisplay(self): """Returns current map display. @@ -166,7 +166,7 @@ def GetMapDisplay(self): :return: MapFrame instance :return: None when no mapdisplay open """ - raise NotImplementedError() + raise NotImplementedError def GetAllMapDisplays(self): """Get list of all map displays. @@ -177,7 +177,7 @@ def GetAllMapDisplays(self): :return: list of MapFrame instances """ - raise NotImplementedError() + raise NotImplementedError def GetMapWindow(self): """Returns current map window. @@ -186,7 +186,7 @@ def GetMapWindow(self): For layer related tasks use GetLayerList(). """ - raise NotImplementedError() + raise NotImplementedError def GetProgress(self): """Returns object which shows the progress. @@ -195,7 +195,7 @@ def GetProgress(self): Some implementations may not implement this method. """ - raise NotImplementedError() + raise NotImplementedError class StandaloneGrassInterface(GrassInterface): @@ -343,9 +343,9 @@ def GetAllMapDisplays(self): return [] def GetMapWindow(self): - raise NotImplementedError() + raise NotImplementedError def GetProgress(self): # TODO: implement some progress with same inface as gui one # (probably using g.message or similarly to Write... functions) - raise NotImplementedError() + raise NotImplementedError diff --git a/gui/wxpython/core/gthread.py b/gui/wxpython/core/gthread.py index 44a938f335a..1e8e006a969 100644 --- a/gui/wxpython/core/gthread.py +++ b/gui/wxpython/core/gthread.py @@ -160,7 +160,7 @@ def localtrace(self, frame, event, arg): if event == "line": # Send event wx.PostEvent(self, self._terminate_evt) - raise SystemExit() + raise SystemExit return self.localtrace def OnTerminate(self, event): diff --git a/gui/wxpython/core/layerlist.py b/gui/wxpython/core/layerlist.py index b1a9448227d..5b14a4039fd 100644 --- a/gui/wxpython/core/layerlist.py +++ b/gui/wxpython/core/layerlist.py @@ -125,7 +125,7 @@ def GetLayerByData(self, key, value): .. warning:: Avoid using this method, it might be removed in the future. """ - raise NotImplementedError() + raise NotImplementedError def GetLayerIndex(self, layer): """Get index of layer.""" diff --git a/gui/wxpython/mapwin/analysis.py b/gui/wxpython/mapwin/analysis.py index d0a7be1bc0c..4c9ac37a5e0 100644 --- a/gui/wxpython/mapwin/analysis.py +++ b/gui/wxpython/mapwin/analysis.py @@ -93,20 +93,20 @@ def _doAnalysis(self, coords): :param coords: EN coordinates """ - raise NotImplementedError() + raise NotImplementedError def _disconnectAll(self): """Disconnect all mouse signals to stop drawing.""" - raise NotImplementedError() + raise NotImplementedError def _connectAll(self): """Connect all mouse signals to draw.""" - raise NotImplementedError() + raise NotImplementedError def _getPen(self): """Returns wx.Pen instance.""" - raise NotImplementedError() + raise NotImplementedError def Stop(self, restore=True): """Analysis mode is stopped. diff --git a/gui/wxpython/mapwin/base.py b/gui/wxpython/mapwin/base.py index e9752caf953..c05cc8f840b 100644 --- a/gui/wxpython/mapwin/base.py +++ b/gui/wxpython/mapwin/base.py @@ -370,10 +370,10 @@ def UnregisterMouseEventHandler(self, event, handler): return True def Pixel2Cell(self, xyCoords): - raise NotImplementedError() + raise NotImplementedError def Cell2Pixel(self, enCoords): - raise NotImplementedError() + raise NotImplementedError def OnMotion(self, event): """Tracks mouse motion and update statusbar @@ -453,8 +453,8 @@ def SetModeQuery(self): def DisactivateWin(self): """Use when the class instance is hidden in MapFrame.""" - raise NotImplementedError() + raise NotImplementedError def ActivateWin(self): """Used when the class instance is activated in MapFrame.""" - raise NotImplementedError() + raise NotImplementedError diff --git a/gui/wxpython/modules/import_export.py b/gui/wxpython/modules/import_export.py index 03d179ffc2f..43115c943da 100644 --- a/gui/wxpython/modules/import_export.py +++ b/gui/wxpython/modules/import_export.py @@ -236,15 +236,15 @@ def doLayout(self): def _getCommand(self): """Get command""" - raise NotImplementedError() + raise NotImplementedError def _getBlackListedParameters(self): """Get parameters which will not be showed in Settings page""" - raise NotImplementedError() + raise NotImplementedError def _getBlackListedFlags(self): """Get flags which will not be showed in Settings page""" - raise NotImplementedError() + raise NotImplementedError def _nameValidationFailed(self, layers_list): """Output map name validation callback diff --git a/pyproject.toml b/pyproject.toml index f1a5aec9a1c..ce708210d5b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -204,7 +204,6 @@ ignore = [ "RET506", # superfluous-else-raise "RET507", # superfluous-else-continue "RET508", # superfluous-else-break - "RSE102", # unnecessary-paren-on-raise-exception "RUF003", # ambiguous-unicode-character-comment "RUF005", # collection-literal-concatenation "RUF012", # mutable-class-default diff --git a/python/grass/imaging/images2swf.py b/python/grass/imaging/images2swf.py index 60352a48f5f..7d6f5a25f9b 100644 --- a/python/grass/imaging/images2swf.py +++ b/python/grass/imaging/images2swf.py @@ -392,7 +392,7 @@ def __init__(self): def ProcessTag(self): """Implement this to create the tag.""" - raise NotImplementedError() + raise NotImplementedError def GetTag(self): """Calls processTag and attaches the header.""" diff --git a/python/grass/pygrass/vector/geometry.py b/python/grass/pygrass/vector/geometry.py index 1c5d378e6ef..2ff4cb1bb8f 100644 --- a/python/grass/pygrass/vector/geometry.py +++ b/python/grass/pygrass/vector/geometry.py @@ -1869,7 +1869,7 @@ def c_read_next_line(c_mapinfo, c_points, c_cats): v_id = v_id if v_id != 0 else None ftype = libvect.Vect_read_next_line(c_mapinfo, c_points, c_cats) if ftype == -2: - raise StopIteration() + raise StopIteration if ftype == -1: raise return ftype, v_id, c_points, c_cats From 703d9f8bddbc436181c05299d84b18313e1cfb82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Mon, 7 Oct 2024 10:53:02 -0400 Subject: [PATCH 352/471] style: Fix superfluous-else-return (RET505) (#4459) * style: Fix superfluous-else-return (RET505) Ruff rule: https://docs.astral.sh/ruff/rules/superfluous-else-return/ When there is an if with a return, the next `elif:` or `else:` can be flattened with one less nested level. This kind of early-return pattern is common in C#. Having simpler conditions that return, then the rest of flow continues otherwise * Apply black --- gui/wxpython/animation/dialogs.py | 57 +++-- gui/wxpython/animation/provider.py | 5 +- gui/wxpython/animation/utils.py | 3 +- gui/wxpython/core/gcmd.py | 12 +- gui/wxpython/core/gconsole.py | 199 +++++++++--------- gui/wxpython/core/gthread.py | 3 +- gui/wxpython/core/menutree.py | 7 +- gui/wxpython/core/render.py | 12 +- gui/wxpython/core/settings.py | 3 +- gui/wxpython/core/toolboxes.py | 5 +- gui/wxpython/core/treemodel.py | 5 +- gui/wxpython/core/utils.py | 67 +++--- gui/wxpython/datacatalog/tree.py | 11 +- gui/wxpython/dbmgr/base.py | 120 +++++------ gui/wxpython/dbmgr/vinfo.py | 3 +- gui/wxpython/gmodeler/model.py | 24 +-- gui/wxpython/gui_core/dialogs.py | 5 +- gui/wxpython/gui_core/forms.py | 6 +- gui/wxpython/gui_core/gselect.py | 10 +- gui/wxpython/gui_core/mapdisp.py | 3 +- gui/wxpython/gui_core/treeview.py | 3 +- gui/wxpython/gui_core/widgets.py | 15 +- gui/wxpython/gui_core/wrap.py | 102 ++++----- gui/wxpython/history/browser.py | 6 +- gui/wxpython/history/tree.py | 36 ++-- gui/wxpython/image2target/ii2t_gis_set.py | 6 +- gui/wxpython/iscatt/controllers.py | 4 +- gui/wxpython/iscatt/iscatt_core.py | 8 +- gui/wxpython/lmgr/frame.py | 16 +- gui/wxpython/lmgr/giface.py | 22 +- gui/wxpython/lmgr/layertree.py | 3 +- gui/wxpython/location_wizard/wizard.py | 126 ++++++----- gui/wxpython/main_window/frame.py | 13 +- gui/wxpython/mapdisp/frame.py | 3 +- gui/wxpython/mapdisp/main.py | 11 +- gui/wxpython/mapdisp/statusbar.py | 46 ++-- gui/wxpython/mapswipe/dialogs.py | 3 +- gui/wxpython/mapswipe/mapwindow.py | 6 +- gui/wxpython/mapwin/buffered.py | 6 +- gui/wxpython/modules/import_export.py | 6 +- gui/wxpython/nviz/mapwindow.py | 4 +- gui/wxpython/nviz/tools.py | 8 +- gui/wxpython/nviz/wxnviz.py | 6 +- gui/wxpython/psmap/dialogs.py | 3 +- gui/wxpython/psmap/frame.py | 3 +- gui/wxpython/psmap/instructions.py | 6 +- gui/wxpython/psmap/utils.py | 41 ++-- gui/wxpython/rdigit/controller.py | 40 ++-- gui/wxpython/rdigit/dialogs.py | 5 +- gui/wxpython/rlisetup/functions.py | 5 +- gui/wxpython/rlisetup/sampling_frame.py | 55 +++-- gui/wxpython/rlisetup/wizard.py | 18 +- gui/wxpython/startup/locdownload.py | 8 +- gui/wxpython/vdigit/wxdigit.py | 18 +- gui/wxpython/vnet/dialogs.py | 4 +- gui/wxpython/vnet/vnet_core.py | 36 ++-- gui/wxpython/vnet/vnet_data.py | 10 +- gui/wxpython/web_services/cap_interface.py | 25 +-- gui/wxpython/wxplot/histogram.py | 3 +- gui/wxpython/wxplot/profile.py | 3 +- lib/init/grass.py | 17 +- locale/grass_po_stats.py | 5 +- man/build_html.py | 3 +- man/build_manual_gallery.py | 3 +- man/build_rest.py | 3 +- pyproject.toml | 1 - python/grass/grassdb/checks.py | 6 +- python/grass/gunittest/case.py | 7 +- python/grass/gunittest/multireport.py | 3 +- python/grass/gunittest/multirunner.py | 6 +- python/grass/gunittest/reporters.py | 47 ++--- python/grass/gunittest/runner.py | 3 +- python/grass/imaging/images2avi.py | 12 +- python/grass/imaging/images2gif.py | 5 +- python/grass/imaging/images2ims.py | 3 +- python/grass/pydispatch/robustapply.py | 2 +- python/grass/pydispatch/saferef.py | 12 +- python/grass/pygrass/errors.py | 15 +- python/grass/pygrass/gis/__init__.py | 6 +- .../pygrass/modules/interface/docstring.py | 3 +- .../grass/pygrass/modules/interface/flag.py | 6 +- .../grass/pygrass/modules/interface/module.py | 22 +- python/grass/pygrass/raster/__init__.py | 7 +- python/grass/pygrass/raster/abstract.py | 13 +- python/grass/pygrass/raster/buffer.py | 9 +- python/grass/pygrass/raster/category.py | 2 +- python/grass/pygrass/utils.py | 28 ++- python/grass/pygrass/vector/__init__.py | 37 ++-- python/grass/pygrass/vector/abstract.py | 3 +- python/grass/pygrass/vector/basic.py | 8 +- python/grass/pygrass/vector/find.py | 43 ++-- python/grass/pygrass/vector/geometry.py | 27 +-- python/grass/pygrass/vector/table.py | 26 +-- python/grass/script/core.py | 8 +- python/grass/script/db.py | 3 +- python/grass/script/raster.py | 3 +- python/grass/script/raster3d.py | 3 +- python/grass/script/task.py | 9 +- python/grass/script/utils.py | 8 +- python/grass/temporal/abstract_dataset.py | 8 +- python/grass/temporal/abstract_map_dataset.py | 50 ++--- .../temporal/abstract_space_time_dataset.py | 3 +- python/grass/temporal/base.py | 57 ++--- .../grass/temporal/c_libraries_interface.py | 99 +++++---- python/grass/temporal/core.py | 101 +++++---- python/grass/temporal/metadata.py | 178 ++++++---------- python/grass/temporal/space_time_datasets.py | 30 +-- python/grass/temporal/spatial_extent.py | 21 +- python/grass/temporal/temporal_algebra.py | 9 +- python/grass/temporal/temporal_extent.py | 32 ++- python/grass/temporal/temporal_granularity.py | 65 +++--- .../temporal/temporal_raster_base_algebra.py | 2 +- raster/r.topidx/arc_to_gridatb.py | 5 +- scripts/g.extension/g.extension.py | 40 ++-- scripts/g.search.modules/g.search.modules.py | 3 +- scripts/i.image.mosaic/i.image.mosaic.py | 5 +- scripts/r.in.wms/wms_base.py | 3 +- scripts/r.in.wms/wms_drv.py | 2 +- temporal/t.info/t.info.py | 2 +- utils/g.html2man/g.html2man.py | 8 +- utils/g.html2man/ggroff.py | 3 +- utils/g.html2man/ghtml.py | 3 +- utils/mkhtml.py | 10 +- 123 files changed, 1054 insertions(+), 1414 deletions(-) diff --git a/gui/wxpython/animation/dialogs.py b/gui/wxpython/animation/dialogs.py index f6e4db341c5..bc7e7fe2482 100644 --- a/gui/wxpython/animation/dialogs.py +++ b/gui/wxpython/animation/dialogs.py @@ -1548,37 +1548,36 @@ def _export_file_validation(self, filebrowsebtn, file_path, file_postfix): if not file_path: GError(parent=self, message=_("Export file is missing.")) return False - else: - if not file_path.endswith(file_postfix): - filebrowsebtn.SetValue(file_path + file_postfix) - file_path += file_postfix - - base_dir = os.path.dirname(file_path) - if not os.path.exists(base_dir): - GError( - parent=self, - message=file_path_does_not_exist_err_message.format( - base_dir=base_dir, - ), - ) - return False + if not file_path.endswith(file_postfix): + filebrowsebtn.SetValue(file_path + file_postfix) + file_path += file_postfix - if os.path.exists(file_path): - overwrite_dlg = wx.MessageDialog( - self.GetParent(), - message=_( - "Exported animation file <{file}> exists. " - "Do you want to overwrite it?" - ).format( - file=file_path, - ), - caption=_("Overwrite?"), - style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION, - ) - if overwrite_dlg.ShowModal() != wx.ID_YES: - overwrite_dlg.Destroy() - return False + base_dir = os.path.dirname(file_path) + if not os.path.exists(base_dir): + GError( + parent=self, + message=file_path_does_not_exist_err_message.format( + base_dir=base_dir, + ), + ) + return False + + if os.path.exists(file_path): + overwrite_dlg = wx.MessageDialog( + self.GetParent(), + message=_( + "Exported animation file <{file}> exists. " + "Do you want to overwrite it?" + ).format( + file=file_path, + ), + caption=_("Overwrite?"), + style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION, + ) + if overwrite_dlg.ShowModal() != wx.ID_YES: overwrite_dlg.Destroy() + return False + overwrite_dlg.Destroy() return True diff --git a/gui/wxpython/animation/provider.py b/gui/wxpython/animation/provider.py index fd966219407..43929a1873d 100644 --- a/gui/wxpython/animation/provider.py +++ b/gui/wxpython/animation/provider.py @@ -328,9 +328,8 @@ def LoadOverlay(self, cmd): if returncode == 0: return BitmapFromImage(autoCropImageFromFile(filename)) - else: - os.remove(filename) - raise GException(messages) + os.remove(filename) + raise GException(messages) class BitmapRenderer: diff --git a/gui/wxpython/animation/utils.py b/gui/wxpython/animation/utils.py index 9e5f685545e..5f69bc40d35 100644 --- a/gui/wxpython/animation/utils.py +++ b/gui/wxpython/animation/utils.py @@ -69,8 +69,7 @@ def validateTimeseriesName(timeseries, etype="strds"): nameShort, mapset = timeseries.split("@", 1) if nameShort in trastDict[mapset]: return timeseries - else: - raise GException(_("Space time dataset <%s> not found.") % timeseries) + raise GException(_("Space time dataset <%s> not found.") % timeseries) mapsets = tgis.get_tgis_c_library_interface().available_mapsets() for mapset in mapsets: diff --git a/gui/wxpython/core/gcmd.py b/gui/wxpython/core/gcmd.py index eb40ec57103..a173e28c7f8 100644 --- a/gui/wxpython/core/gcmd.py +++ b/gui/wxpython/core/gcmd.py @@ -202,11 +202,10 @@ def kill(self): handle = win32api.OpenProcess(1, 0, self.pid) return win32api.TerminateProcess(handle, 0) != 0 - else: - try: - os.kill(-self.pid, signal.SIGTERM) # kill whole group - except OSError: - pass + try: + os.kill(-self.pid, signal.SIGTERM) # kill whole group + except OSError: + pass if sys.platform == "win32": @@ -750,8 +749,7 @@ def RunCommand( if not read: if not getErrorMsg: return ret - else: - return ret, _formatMsg(stderr) + return ret, _formatMsg(stderr) if stdout: Debug.msg(3, "gcmd.RunCommand(): return stdout\n'%s'" % stdout) diff --git a/gui/wxpython/core/gconsole.py b/gui/wxpython/core/gconsole.py index 092f9bc704a..a4b58435cf4 100644 --- a/gui/wxpython/core/gconsole.py +++ b/gui/wxpython/core/gconsole.py @@ -552,114 +552,111 @@ def RunCmd( wx.PostEvent(self, event) return - else: - # other GRASS commands (r|v|g|...) - try: - task = GUI(show=None).ParseCommand(command) - except GException as e: - GError(parent=self._guiparent, message=str(e), showTraceback=False) - return - - hasParams = False - if task: - options = task.get_options() - hasParams = options["params"] and options["flags"] - # check for =- - for p in options["params"]: - if ( - p.get("prompt", "") == "input" - and p.get("element", "") == "file" - and p.get("age", "new") == "old" - and p.get("value", "") == "-" - ): - GError( - parent=self._guiparent, - message=_( - "Unable to run command:\n%(cmd)s\n\n" - "Option <%(opt)s>: read from standard input is not " - "supported by wxGUI" - ) - % {"cmd": " ".join(command), "opt": p.get("name", "")}, - ) - return - - if len(command) == 1: - if command[0].startswith("g.gui."): - import inspect - import importlib.util - import importlib.machinery + # other GRASS commands (r|v|g|...) + try: + task = GUI(show=None).ParseCommand(command) + except GException as e: + GError(parent=self._guiparent, message=str(e), showTraceback=False) + return - def load_source(modname, filename): - loader = importlib.machinery.SourceFileLoader( - modname, filename - ) - spec = importlib.util.spec_from_file_location( - modname, filename, loader=loader - ) - module = importlib.util.module_from_spec(spec) - # Module is always executed and not cached in sys.modules. - # Uncomment the following line to cache the module. - # sys.modules[module.__name__] = module - loader.exec_module(module) - return module - - pyFile = command[0] - if sys.platform == "win32": - pyFile += ".py" - pyPath = os.path.join(os.environ["GISBASE"], "scripts", pyFile) - if not os.path.exists(pyPath): - pyPath = os.path.join( - os.environ["GRASS_ADDON_BASE"], "scripts", pyFile - ) - if not os.path.exists(pyPath): - GError( - parent=self._guiparent, - message=_("Module <%s> not found.") % command[0], + hasParams = False + if task: + options = task.get_options() + hasParams = options["params"] and options["flags"] + # check for =- + for p in options["params"]: + if ( + p.get("prompt", "") == "input" + and p.get("element", "") == "file" + and p.get("age", "new") == "old" + and p.get("value", "") == "-" + ): + GError( + parent=self._guiparent, + message=_( + "Unable to run command:\n%(cmd)s\n\n" + "Option <%(opt)s>: read from standard input is not " + "supported by wxGUI" ) - pymodule = load_source(command[0].replace(".", "_"), pyPath) - pymain = inspect.getfullargspec(pymodule.main) - if pymain and "giface" in pymain.args: - pymodule.main(self._giface) - return - - # no arguments given - if hasParams and not isinstance(self._guiparent, FormNotebook): - # also parent must be checked, see #3135 for details - try: - GUI( - parent=self._guiparent, giface=self._giface - ).ParseCommand(command) - self.UpdateHistory(status=Status.SUCCESS) - except GException as e: - print(e, file=sys.stderr) + % {"cmd": " ".join(command), "opt": p.get("name", "")}, + ) + return + if len(command) == 1: + if command[0].startswith("g.gui."): + import inspect + import importlib.util + import importlib.machinery + + def load_source(modname, filename): + loader = importlib.machinery.SourceFileLoader(modname, filename) + spec = importlib.util.spec_from_file_location( + modname, filename, loader=loader + ) + module = importlib.util.module_from_spec(spec) + # Module is always executed and not cached in sys.modules. + # Uncomment the following line to cache the module. + # sys.modules[module.__name__] = module + loader.exec_module(module) + return module + + pyFile = command[0] + if sys.platform == "win32": + pyFile += ".py" + pyPath = os.path.join(os.environ["GISBASE"], "scripts", pyFile) + if not os.path.exists(pyPath): + pyPath = os.path.join( + os.environ["GRASS_ADDON_BASE"], "scripts", pyFile + ) + if not os.path.exists(pyPath): + GError( + parent=self._guiparent, + message=_("Module <%s> not found.") % command[0], + ) + pymodule = load_source(command[0].replace(".", "_"), pyPath) + pymain = inspect.getfullargspec(pymodule.main) + if pymain and "giface" in pymain.args: + pymodule.main(self._giface) return - if env: - env = env.copy() - else: - env = os.environ.copy() - # activate computational region (set with g.region) - # for all non-display commands. - if compReg and "GRASS_REGION" in env: - del env["GRASS_REGION"] + # no arguments given + if hasParams and not isinstance(self._guiparent, FormNotebook): + # also parent must be checked, see #3135 for details + try: + GUI(parent=self._guiparent, giface=self._giface).ParseCommand( + command + ) + self.UpdateHistory(status=Status.SUCCESS) + except GException as e: + print(e, file=sys.stderr) - # process GRASS command with argument - self.cmdThread.RunCmd( - command, - stdout=self.cmdStdOut, - stderr=self.cmdStdErr, - onDone=onDone, - onPrepare=onPrepare, - userData=userData, - addLayer=addLayer, - env=env, - notification=notification, - ) - self.cmdOutputTimer.Start(50) + return + + if env: + env = env.copy() + else: + env = os.environ.copy() + # activate computational region (set with g.region) + # for all non-display commands. + if compReg and "GRASS_REGION" in env: + del env["GRASS_REGION"] + + # process GRASS command with argument + self.cmdThread.RunCmd( + command, + stdout=self.cmdStdOut, + stderr=self.cmdStdErr, + onDone=onDone, + onPrepare=onPrepare, + userData=userData, + addLayer=addLayer, + env=env, + notification=notification, + ) + self.cmdOutputTimer.Start(50) - # we don't need to change computational region settings - # because we work on a copy + # we don't need to change computational region settings + # because we work on a copy else: # Send any other command to the shell. Send output to # console output window diff --git a/gui/wxpython/core/gthread.py b/gui/wxpython/core/gthread.py index 1e8e006a969..3437d04bf56 100644 --- a/gui/wxpython/core/gthread.py +++ b/gui/wxpython/core/gthread.py @@ -152,8 +152,7 @@ def __run(self): def globaltrace(self, frame, event, arg): if event == "call": return self.localtrace - else: - return None + return None def localtrace(self, frame, event, arg): if self.terminate: diff --git a/gui/wxpython/core/menutree.py b/gui/wxpython/core/menutree.py index 4c55d7e9521..fba33daf705 100644 --- a/gui/wxpython/core/menutree.py +++ b/gui/wxpython/core/menutree.py @@ -160,10 +160,9 @@ def GetModel(self, separators=False): """ if separators: return copy.deepcopy(self.model) - else: - model = copy.deepcopy(self.model) - removeSeparators(model) - return model + model = copy.deepcopy(self.model) + removeSeparators(model) + return model def PrintTree(self, fh): for child in self.model.root.children: diff --git a/gui/wxpython/core/render.py b/gui/wxpython/core/render.py index 1d4ed6f5386..2db3287bf78 100644 --- a/gui/wxpython/core/render.py +++ b/gui/wxpython/core/render.py @@ -229,10 +229,8 @@ def GetCmd(self, string=False): scmd.append(utils.GetCmdString(c)) return ";".join(scmd) - else: - return utils.GetCmdString(self.cmd) - else: - return self.cmd + return utils.GetCmdString(self.cmd) + return self.cmd def GetType(self): """Get map layer type""" @@ -462,8 +460,7 @@ def _render(self, cmd, env): stdout, stderr = p.communicate() if p.returncode: return grass.decode(stderr) - else: - return None + return None def Abort(self): """Abort rendering process""" @@ -1690,8 +1687,7 @@ def GetOverlay(self, id, list=False): if not list: if len(ovl) != 1: return None - else: - return ovl[0] + return ovl[0] return ovl diff --git a/gui/wxpython/core/settings.py b/gui/wxpython/core/settings.py index ef3a3031cee..5cc2fddaf44 100644 --- a/gui/wxpython/core/settings.py +++ b/gui/wxpython/core/settings.py @@ -59,8 +59,7 @@ def color(item): return [color(e) for e in item] if isinstance(item, dict): return {key: color(value) for key, value in item.items()} - else: - return item + return item return super().iterencode(color(obj)) diff --git a/gui/wxpython/core/toolboxes.py b/gui/wxpython/core/toolboxes.py index e77010ff213..46b886e4996 100644 --- a/gui/wxpython/core/toolboxes.py +++ b/gui/wxpython/core/toolboxes.py @@ -861,9 +861,8 @@ def module_test(): if someDiff: print("Difference between files.") return 1 - else: - print("OK") - return 0 + print("OK") + return 0 def validate_file(filename): diff --git a/gui/wxpython/core/treemodel.py b/gui/wxpython/core/treemodel.py index dd84fed7bf9..fb18384ba36 100644 --- a/gui/wxpython/core/treemodel.py +++ b/gui/wxpython/core/treemodel.py @@ -137,8 +137,7 @@ def GetChildrenByIndex(self, index): def _getNode(self, node, index): if len(index) == 1: return node.children[index[0]] - else: - return self._getNode(node.children[index[0]], index[1:]) + return self._getNode(node.children[index[0]], index[1:]) def RemoveNode(self, node): """Removes node. If node is root, removes root's children, root is kept.""" @@ -257,7 +256,7 @@ def match(self, method="exact", **kwargs): if method == "exact": return self._match_exact(**kwargs) - elif method == "filtering": + if method == "filtering": return self._match_filtering(**kwargs) def _match_exact(self, **kwargs): diff --git a/gui/wxpython/core/utils.py b/gui/wxpython/core/utils.py index 4be6c18e2a2..3c1b4a7dd14 100644 --- a/gui/wxpython/core/utils.py +++ b/gui/wxpython/core/utils.py @@ -45,8 +45,7 @@ def split(s): try: if sys.platform == "win32": return shlex.split(s.replace("\\", r"\\")) - else: - return shlex.split(s) + return shlex.split(s) except ValueError as e: sys.stderr.write(_("Syntax error: %s") % e) @@ -75,8 +74,7 @@ def GetTempfile(pref=None): path, file = os.path.split(tempfile) if pref: return os.path.join(pref, file) - else: - return tempfile + return tempfile except Exception: return None @@ -341,8 +339,7 @@ def GetVectorNumberOfLayers(vector): _("Vector map <%(map)s>: %(msg)s\n") % {"map": fullname, "msg": msg} ) return layers - else: - Debug.msg(1, "GetVectorNumberOfLayers(): ret %s" % ret) + Debug.msg(1, "GetVectorNumberOfLayers(): ret %s" % ret) for layer in out.splitlines(): layers.append(layer) @@ -374,8 +371,7 @@ def Deg2DMS(lon, lat, string=True, hemisphere=True, precision=3): except ValueError: if string: return "" - else: - return None + return None # fix longitude while flon > 180.0: @@ -457,38 +453,38 @@ def __ll_parts(value, reverse=False, precision=3): s = "%.*f" % (precision, s) return str(d) + ":" + m + ":" + s - else: # -> reverse + # -> reverse + try: + d, m, s = value.split(":") + hs = s[-1] + s = s[:-1] + except ValueError: try: - d, m, s = value.split(":") - hs = s[-1] - s = s[:-1] + d, m = value.split(":") + hs = m[-1] + m = m[:-1] + s = "0.0" except ValueError: try: - d, m = value.split(":") - hs = m[-1] - m = m[:-1] + d = value + hs = d[-1] + d = d[:-1] + m = "0" s = "0.0" except ValueError: - try: - d = value - hs = d[-1] - d = d[:-1] - m = "0" - s = "0.0" - except ValueError: - raise ValueError + raise ValueError - if hs not in {"N", "S", "E", "W"}: - raise ValueError + if hs not in {"N", "S", "E", "W"}: + raise ValueError - coef = 1.0 - if hs in {"S", "W"}: - coef = -1.0 + coef = 1.0 + if hs in {"S", "W"}: + coef = -1.0 - fm = int(m) / 60.0 - fs = float(s) / (60 * 60) + fm = int(m) / 60.0 + fs = float(s) / (60 * 60) - return coef * (float(d) + fm + fs) + return coef * (float(d) + fm + fs) def GetCmdString(cmd): @@ -555,11 +551,10 @@ def ReprojectCoordinates(coord, projOut, projIn=None, flags=""): proj = "" if proj in {"ll", "latlong", "longlat"} and "d" not in flags: return (proj, (e, n)) - else: - try: - return (proj, (float(e), float(n))) - except ValueError: - return (None, None) + try: + return (proj, (float(e), float(n))) + except ValueError: + return (None, None) return (None, None) diff --git a/gui/wxpython/datacatalog/tree.py b/gui/wxpython/datacatalog/tree.py index 9a460d05f53..176f5231712 100644 --- a/gui/wxpython/datacatalog/tree.py +++ b/gui/wxpython/datacatalog/tree.py @@ -184,13 +184,13 @@ def label(self): owner = data["owner"] or _("name unknown") if data["current"]: return _("{name} (current)").format(**data) - elif data["is_different_owner"] and data["lock"]: + if data["is_different_owner"] and data["lock"]: return _("{name} (in use, owner: {owner})").format( name=data["name"], owner=owner ) - elif data["lock"]: + if data["lock"]: return _("{name} (in use)").format(**data) - elif data["is_different_owner"]: + if data["is_different_owner"]: return _("{name} (owner: {owner})").format( name=data["name"], owner=owner ) @@ -957,7 +957,7 @@ def OnGetItemTextColour(self, index): if node.data["type"] == "mapset": if node.data["current"]: return wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT) - elif node.data["lock"] or node.data["is_different_owner"]: + if node.data["lock"] or node.data["is_different_owner"]: return wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT) return wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT) @@ -2052,8 +2052,7 @@ def _isCurrent(self, genv): currentMapset = False break return currentGrassDb, currentLocation, currentMapset - else: - return True, True, True + return True, True, True def _popupMenuLayer(self): """Create popup menu for layers""" diff --git a/gui/wxpython/dbmgr/base.py b/gui/wxpython/dbmgr/base.py index d7ea818da4c..1af8d6fccb1 100644 --- a/gui/wxpython/dbmgr/base.py +++ b/gui/wxpython/dbmgr/base.py @@ -706,8 +706,7 @@ def Sorter(self, key1, key2): if ascending: return cmpVal - else: - return -cmpVal + return -cmpVal def GetSortImages(self): """Used by the ColumnSorterMixin, see wx/lib/mixins/listctrl.py""" @@ -2004,37 +2003,36 @@ def OnExtractSelected(self, event): if len(cats) == 0: GMessage(parent=self, message=_("Nothing to extract.")) return - else: - # dialog to get file name - dlg = CreateNewVector( - parent=self, - title=_("Extract selected features"), - giface=self.giface, - cmd=( - ( - "v.extract", - { - "input": self.dbMgrData["vectName"], - "cats": ListOfCatsToRange(cats), - }, - "output", - ) - ), - disableTable=True, - ) - if not dlg: - return + # dialog to get file name + dlg = CreateNewVector( + parent=self, + title=_("Extract selected features"), + giface=self.giface, + cmd=( + ( + "v.extract", + { + "input": self.dbMgrData["vectName"], + "cats": ListOfCatsToRange(cats), + }, + "output", + ) + ), + disableTable=True, + ) + if not dlg: + return - name = dlg.GetName(full=True) + name = dlg.GetName(full=True) - if not self.mapdisplay and self.mapdisplay.tree: - pass - elif name and dlg.IsChecked("add"): - # add layer to map layer tree - self.mapdisplay.tree.AddLayer( - ltype="vector", lname=name, lcmd=["d.vect", "map=%s" % name] - ) - dlg.Destroy() + if not self.mapdisplay and self.mapdisplay.tree: + pass + elif name and dlg.IsChecked("add"): + # add layer to map layer tree + self.mapdisplay.tree.AddLayer( + ltype="vector", lname=name, lcmd=["d.vect", "map=%s" % name] + ) + dlg.Destroy() def OnDeleteSelected(self, event): """Delete vector objects selected in attribute browse window @@ -2606,43 +2604,41 @@ def OnTableItemChange(self, event): message=_("Unable to rename column. No column name defined."), ) return - else: - item = tlist.FindItem(start=-1, str=name) - if item > -1: - if tlist.FindItem(start=-1, str=nameTo) > -1: - GError( - parent=self, - message=_( - "Unable to rename column <%(column)s> to " - "<%(columnTo)s>. Column already exists " - "in the table <%(table)s>." - ) - % {"column": name, "columnTo": nameTo, "table": table}, - ) - return - else: - tlist.SetItemText(item, nameTo) - - self.listOfCommands.append( - ( - "v.db.renamecolumn", - { - "map": self.dbMgrData["vectName"], - "layer": self.selLayer, - "column": "%s,%s" % (name, nameTo), - }, - ) - ) - else: + item = tlist.FindItem(start=-1, str=name) + if item > -1: + if tlist.FindItem(start=-1, str=nameTo) > -1: GError( parent=self, message=_( - "Unable to rename column. " - "Column <%(column)s> doesn't exist in the table <%(table)s>." + "Unable to rename column <%(column)s> to " + "<%(columnTo)s>. Column already exists " + "in the table <%(table)s>." ) - % {"column": name, "table": table}, + % {"column": name, "columnTo": nameTo, "table": table}, ) return + tlist.SetItemText(item, nameTo) + + self.listOfCommands.append( + ( + "v.db.renamecolumn", + { + "map": self.dbMgrData["vectName"], + "layer": self.selLayer, + "column": "%s,%s" % (name, nameTo), + }, + ) + ) + else: + GError( + parent=self, + message=_( + "Unable to rename column. " + "Column <%(column)s> doesn't exist in the table <%(table)s>." + ) + % {"column": name, "table": table}, + ) + return # apply changes self.ApplyCommands(self.listOfCommands, self.listOfSQLStatements) diff --git a/gui/wxpython/dbmgr/vinfo.py b/gui/wxpython/dbmgr/vinfo.py index 6b04ee7a64e..c32ac5572b4 100644 --- a/gui/wxpython/dbmgr/vinfo.py +++ b/gui/wxpython/dbmgr/vinfo.py @@ -37,8 +37,7 @@ def GetUnicodeValue(value): if isinstance(value, bytes): enc = GetDbEncoding() return str(value, enc, errors="replace") - else: - return str(value) + return str(value) def GetDbEncoding(): diff --git a/gui/wxpython/gmodeler/model.py b/gui/wxpython/gmodeler/model.py index 98a7204abbe..23e308037ff 100644 --- a/gui/wxpython/gmodeler/model.py +++ b/gui/wxpython/gmodeler/model.py @@ -1233,8 +1233,7 @@ def GetLog(self, string=True, substitute=None): if string: if cmd is None: return "" - else: - return " ".join(cmd) + return " ".join(cmd) return cmd @@ -1420,8 +1419,7 @@ def GetLog(self, string=True): name.append(rel.GetLabel()) if name: return "/".join(name) + "=" + self.value + " (" + self.prompt + ")" - else: - return self.value + " (" + self.prompt + ")" + return self.value + " (" + self.prompt + ")" def GetLabel(self): """Get list of names""" @@ -1672,7 +1670,7 @@ def GetData(self): """ if isinstance(self.fromShape, ModelData): return self.fromShape - elif isinstance(self.toShape, ModelData): + if isinstance(self.toShape, ModelData): return self.toShape return None @@ -1743,8 +1741,7 @@ def GetLog(self): """Get log info""" if self.label: return _("Condition: ") + self.label - else: - return _("Condition: not defined") + return _("Condition: not defined") def AddRelation(self, rel): """Record relation""" @@ -2024,8 +2021,7 @@ def _getNodeText(self, node, tag, default=""): if p is not None: if p.text: return utils.normalize_whitespace(p.text) - else: - return "" + return "" return default @@ -3322,15 +3318,15 @@ def _getStandardizedOption(self, string): """ if string == "raster": return "G_OPT_R_MAP" - elif string == "vector": + if string == "vector": return "G_OPT_V_MAP" - elif string == "mapset": + if string == "mapset": return "G_OPT_M_MAPSET" - elif string == "file": + if string == "file": return "G_OPT_F_INPUT" - elif string == "dir": + if string == "dir": return "G_OPT_M_DIR" - elif string == "region": + if string == "region": return "G_OPT_M_REGION" return None diff --git a/gui/wxpython/gui_core/dialogs.py b/gui/wxpython/gui_core/dialogs.py index cd2a8dcc24d..a38836ac23e 100644 --- a/gui/wxpython/gui_core/dialogs.py +++ b/gui/wxpython/gui_core/dialogs.py @@ -283,8 +283,7 @@ def GetName(self, full=False): if full: if "@" in name: return name - else: - return name + "@" + grass.gisenv()["MAPSET"] + return name + "@" + grass.gisenv()["MAPSET"] return name.split("@", 1)[0] @@ -427,7 +426,7 @@ def IsChecked(self, key): """ if key == "add": return self.addbox.IsChecked() - elif key == "table": + if key == "table": return self.table.IsChecked() return None diff --git a/gui/wxpython/gui_core/forms.py b/gui/wxpython/gui_core/forms.py index c1123497abf..2636b213df3 100644 --- a/gui/wxpython/gui_core/forms.py +++ b/gui/wxpython/gui_core/forms.py @@ -140,8 +140,7 @@ def text_beautify(someString, width=70): textwrap.wrap(utils.normalize_whitespace(someString), width) ).strip(".,;:") ) - else: - return escape_ampersand(utils.normalize_whitespace(someString).strip(".,;:")) + return escape_ampersand(utils.normalize_whitespace(someString).strip(".,;:")) def escape_ampersand(text): @@ -3206,8 +3205,7 @@ def ParseCommand(self, cmd, completed=None): if self.checkError: return self.grass_task, err - else: - return self.grass_task + return self.grass_task def GetCommandInputMapParamKey(self, cmd): """Get parameter key for input raster/vector map diff --git a/gui/wxpython/gui_core/gselect.py b/gui/wxpython/gui_core/gselect.py index 86683476c2e..ff256803a97 100644 --- a/gui/wxpython/gui_core/gselect.py +++ b/gui/wxpython/gui_core/gselect.py @@ -281,8 +281,7 @@ def GetControl(self): def GetComboCtrl(self): if globalvar.wxPythonPhoenix: return super().GetComboCtrl() - else: - return self.GetCombo() + return self.GetCombo() def GetStringValue(self): """Get value as a string separated by commas""" @@ -524,8 +523,7 @@ def _getElementList(self, element, mapsets=None, elements=None, exclude=False): if elem not in elementdict: self.AddItem(_("Not selectable element"), node=False) return - else: - renamed_elements.append(elementdict[elem]) + renamed_elements.append(elementdict[elem]) if element in {"stds", "strds", "str3ds", "stvds"}: if not self.tgis_error: @@ -2805,9 +2803,9 @@ def GetType(self): sel = self.ftype.GetSelection() if sel == 0: return "point" - elif sel == 1: + if sel == 1: return "line" - elif sel == 2: + if sel == 2: return "boundary" diff --git a/gui/wxpython/gui_core/mapdisp.py b/gui/wxpython/gui_core/mapdisp.py index 4fe1c25ac18..a2810c9892b 100644 --- a/gui/wxpython/gui_core/mapdisp.py +++ b/gui/wxpython/gui_core/mapdisp.py @@ -176,8 +176,7 @@ def GetProperty(self, name): """Returns property""" if hasattr(self.mapWindowProperties, name): return getattr(self.mapWindowProperties, name) - else: - return self.statusbarManager.GetProperty(name) + return self.statusbarManager.GetProperty(name) def HasProperty(self, name): """Checks whether object has property""" diff --git a/gui/wxpython/gui_core/treeview.py b/gui/wxpython/gui_core/treeview.py index b5ee50a7822..a7660308843 100644 --- a/gui/wxpython/gui_core/treeview.py +++ b/gui/wxpython/gui_core/treeview.py @@ -256,8 +256,7 @@ def OnGetItemText(self, index, column=0): # remove & because of & needed in menu (&Files) if column > 0: return node.data.get(self._columns[column], "") - else: - return node.label.replace("&", "") + return node.label.replace("&", "") def OnRightClick(self, event): """Select item on right click. diff --git a/gui/wxpython/gui_core/widgets.py b/gui/wxpython/gui_core/widgets.py index dc9041e3578..87a09a590d8 100644 --- a/gui/wxpython/gui_core/widgets.py +++ b/gui/wxpython/gui_core/widgets.py @@ -188,8 +188,7 @@ def DeletePage(self, page): if ret: del self.notebookPages[page] return ret - else: - return False + return False def RemovePage(self, page): """Delete page without deleting the associated window. @@ -203,8 +202,7 @@ def RemovePage(self, page): if ret: del self.notebookPages[page] return ret - else: - return False + return False def SetSelectionByName(self, page): """Set active notebook page. @@ -814,8 +812,7 @@ def Validate(self, win): if len(text) == 0: self.callback(ctrl) return False - else: - return True + return True def TransferToWindow(self): """Transfer data from validator to window. @@ -865,8 +862,7 @@ def Validate(self, win): if not self._condition(text): self._callback(ctrl) return False - else: - return True + return True def TransferToWindow(self): """Transfer data from validator to window.""" @@ -1183,8 +1179,7 @@ def GetData(self, checked=None): if checked is not None: return tuple(data) - else: - return (tuple(data), tuple(checkedList)) + return (tuple(data), tuple(checkedList)) def LoadData(self, data=None, selectOne=True): """Load data into list""" diff --git a/gui/wxpython/gui_core/wrap.py b/gui/wxpython/gui_core/wrap.py index 29299bbbbe2..b971e5e6eab 100644 --- a/gui/wxpython/gui_core/wrap.py +++ b/gui/wxpython/gui_core/wrap.py @@ -99,36 +99,31 @@ def luminance(c): def BitmapFromImage(image, depth=-1): if wxPythonPhoenix: return wx.Bitmap(img=image, depth=depth) - else: - return wx.BitmapFromImage(image, depth=depth) + return wx.BitmapFromImage(image, depth=depth) def ImageFromBitmap(bitmap): if wxPythonPhoenix: return bitmap.ConvertToImage() - else: - return wx.ImageFromBitmap(bitmap) + return wx.ImageFromBitmap(bitmap) def EmptyBitmap(width, height, depth=-1): if wxPythonPhoenix: return wx.Bitmap(width=width, height=height, depth=depth) - else: - return wx.EmptyBitmap(width=width, height=height, depth=depth) + return wx.EmptyBitmap(width=width, height=height, depth=depth) def EmptyImage(width, height, clear=True): if wxPythonPhoenix: return wx.Image(width=width, height=height, clear=clear) - else: - return wx.EmptyImage(width=width, height=height, clear=clear) + return wx.EmptyImage(width=width, height=height, clear=clear) def StockCursor(cursorId): if wxPythonPhoenix: return wx.Cursor(cursorId=cursorId) - else: - return wx.StockCursor(cursorId) + return wx.StockCursor(cursorId) class Window(wx.Window): @@ -437,20 +432,18 @@ def InsertItem(self, index, label, imageIndex=-1): return wx.ListCtrl.InsertItem( self, index=index, label=label, imageIndex=imageIndex ) - else: - return wx.ListCtrl.InsertStringItem( - self, index=index, label=label, imageIndex=imageIndex - ) + return wx.ListCtrl.InsertStringItem( + self, index=index, label=label, imageIndex=imageIndex + ) def SetItem(self, index, column, label, imageId=-1): if wxPythonPhoenix: return wx.ListCtrl.SetItem( self, index=index, column=column, label=label, imageId=imageId ) - else: - return wx.ListCtrl.SetStringItem( - self, index=index, col=column, label=label, imageId=imageId - ) + return wx.ListCtrl.SetStringItem( + self, index=index, col=column, label=label, imageId=imageId + ) def CheckItem(self, item, check=True): """Uses either deprecated listmix.CheckListCtrlMixin @@ -463,8 +456,7 @@ def CheckItem(self, item, check=True): def IsItemChecked(self, item): if hasattr(self, "HasCheckBoxes"): return wx.ListCtrl.IsItemChecked(self, item) - else: - return super().IsChecked(item) + return super().IsChecked(item) if CheckWxVersion([4, 1, 0]): @@ -497,16 +489,14 @@ def __init__(self, *args, **kwargs): def AppendItem(self, parent, text, image=-1, selImage=-1, data=None): if wxPythonPhoenix: return wx.TreeCtrl.AppendItem(self, parent, text, image, selImage, data) - else: - return wx.TreeCtrl.AppendItem( - self, parent, text, image, selImage, wx.TreeItemData(data) - ) + return wx.TreeCtrl.AppendItem( + self, parent, text, image, selImage, wx.TreeItemData(data) + ) def GetItemData(self, item): if wxPythonPhoenix: return wx.TreeCtrl.GetItemData(self, item) - else: - return wx.TreeCtrl.GetPyData(self, item) + return wx.TreeCtrl.GetPyData(self, item) class CustomTreeCtrl(CT.CustomTreeCtrl): @@ -553,18 +543,17 @@ def AddLabelTool( longHelp=longHelpString, clientData=clientData, ) - else: - return wx.ToolBar.AddLabelTool( - self, - toolId, - label, - bitmap, - bmpDisabled, - kind, - shortHelpString, - longHelpString, - clientData, - ) + return wx.ToolBar.AddLabelTool( + self, + toolId, + label, + bitmap, + bmpDisabled, + kind, + shortHelpString, + longHelpString, + clientData, + ) def InsertLabelTool( self, @@ -591,19 +580,18 @@ def InsertLabelTool( longHelp=longHelpString, clientData=clientData, ) - else: - return wx.ToolBar.InsertLabelTool( - self, - pos, - toolId, - label, - bitmap, - bmpDisabled, - kind, - shortHelpString, - longHelpString, - clientData, - ) + return wx.ToolBar.InsertLabelTool( + self, + pos, + toolId, + label, + bitmap, + bmpDisabled, + kind, + shortHelpString, + longHelpString, + clientData, + ) class Menu(wx.Menu): @@ -689,8 +677,7 @@ def __init__(self, *args, **kwargs): def GetFullMultiLineTextExtent(self, string, font=None): if wxPythonPhoenix: return super().GetFullMultiLineTextExtent(string, font) - else: - return super().GetMultiLineTextExtent(string, font) + return super().GetMultiLineTextExtent(string, font) class Rect(wx.Rect): @@ -705,20 +692,17 @@ def __init__(self, *args, **kwargs): def ContainsXY(self, x, y): if wxPythonPhoenix: return wx.Rect.Contains(self, x=int(x), y=int(y)) - else: - return wx.Rect.ContainsXY(self, int(x), int(y)) + return wx.Rect.ContainsXY(self, int(x), int(y)) def ContainsRect(self, rect): if wxPythonPhoenix: return wx.Rect.Contains(self, rect=rect) - else: - return wx.Rect.ContainsRect(self, rect) + return wx.Rect.ContainsRect(self, rect) def OffsetXY(self, dx, dy): if wxPythonPhoenix: return wx.Rect.Offset(self, int(dx), int(dy)) - else: - return wx.Rect.OffsetXY(self, int(dx), int(dy)) + return wx.Rect.OffsetXY(self, int(dx), int(dy)) class CheckBox(wx.CheckBox): diff --git a/gui/wxpython/history/browser.py b/gui/wxpython/history/browser.py index 422d6dcc9c2..03e39a884a3 100644 --- a/gui/wxpython/history/browser.py +++ b/gui/wxpython/history/browser.py @@ -59,11 +59,11 @@ def get_translated_value(key, value): if key == "timestamp": exec_datetime = datetime.fromisoformat(value) return exec_datetime.strftime("%Y-%m-%d %H:%M:%S") - elif key == "runtime": + if key == "runtime": return _("{} sec").format(value) - elif key == "status": + if key == "status": return _(value.capitalize()) - elif key in {"mask2d", "mask3d"}: + if key in {"mask2d", "mask3d"}: return _(str(value)) diff --git a/gui/wxpython/history/tree.py b/gui/wxpython/history/tree.py index 74c321aeb67..3701fefb001 100644 --- a/gui/wxpython/history/tree.py +++ b/gui/wxpython/history/tree.py @@ -62,8 +62,7 @@ def __init__(self, data=None): def label(self): if "day" in self.data.keys(): return self.dayToLabel(self.data["day"]) - else: - return self.data["name"] + return self.data["name"] @property def time_sort(self): @@ -91,14 +90,13 @@ def dayToLabel(self, day): if day == current_date: return _("{base_date} (today)").format(base_date=base_date) - elif day == current_date - datetime.timedelta(days=1): + if day == current_date - datetime.timedelta(days=1): return _("{base_date} (yesterday)").format(base_date=base_date) - elif day >= (current_date - datetime.timedelta(days=current_date.weekday())): + if day >= (current_date - datetime.timedelta(days=current_date.weekday())): return _("{base_date} (this week)").format(base_date=base_date) - elif day.year == current_date.year: + if day.year == current_date.year: return _("{base_date}").format(base_date=base_date) - else: - return _("{base_date}, {year}").format(base_date=base_date, year=day.year) + return _("{base_date}, {year}").format(base_date=base_date, year=day.year) class HistoryTreeModel(TreeModel): @@ -338,12 +336,11 @@ def _getIndexFromFile(self, command_node): """ if not command_node.data["timestamp"]: return self._model.GetIndexOfNode(command_node)[1] - else: - return history.filter( - json_data=self.ReadFromHistory(), - command=command_node.data["name"], - timestamp=self._timestampToISO(command_node.data["timestamp"]), - ) + return history.filter( + json_data=self.ReadFromHistory(), + command=command_node.data["name"], + timestamp=self._timestampToISO(command_node.data["timestamp"]), + ) def ReadFromHistory(self): """Read content of command history log. @@ -409,13 +406,10 @@ def GetHistoryNode(self, entry, command_index=None): if command_index is None: # If no command index is specified, return the first found day node return day_nodes[0] - else: - # Search for command nodes under the first day node - command_nodes = self._model.SearchNodes( - parent=day_nodes[0], type=COMMAND - ) - if 0 <= command_index < len(command_nodes): - return command_nodes[command_index] + # Search for command nodes under the first day node + command_nodes = self._model.SearchNodes(parent=day_nodes[0], type=COMMAND) + if 0 <= command_index < len(command_nodes): + return command_nodes[command_index] return None @@ -585,7 +579,7 @@ def OnGetItemImage(self, index, which=wx.TreeItemIcon_Normal, column=0): try: if node.data["type"] == TIME_PERIOD: return self._iconTypes.index(node.data["type"]) - elif node.data["type"] == COMMAND: + if node.data["type"] == COMMAND: return self._iconTypes.index(node.data["status"]) except ValueError: return 0 diff --git a/gui/wxpython/image2target/ii2t_gis_set.py b/gui/wxpython/image2target/ii2t_gis_set.py index 4e5a5aee200..6ed97bc46c1 100644 --- a/gui/wxpython/image2target/ii2t_gis_set.py +++ b/gui/wxpython/image2target/ii2t_gis_set.py @@ -596,8 +596,7 @@ def GetRCValue(self, value): """Return GRASS variable (read from GISRC)""" if value in self.grassrc: return self.grassrc[value] - else: - return None + return None def OnWizard(self, event): """Location wizard started""" @@ -1046,8 +1045,7 @@ def OnCreateMapset(self, event): if dlg.ShowModal() == wx.ID_OK: mapset = dlg.GetValue() return self.CreateNewMapset(mapset=mapset) - else: - return False + return False def CreateNewMapset(self, mapset): if mapset in self.listOfMapsets: diff --git a/gui/wxpython/iscatt/controllers.py b/gui/wxpython/iscatt/controllers.py index 4cd1c3c66f6..fef4e95505e 100644 --- a/gui/wxpython/iscatt/controllers.py +++ b/gui/wxpython/iscatt/controllers.py @@ -238,7 +238,7 @@ def AddScattPlot(self): ) ) return - elif ncells > WARN_NCELLS: + if ncells > WARN_NCELLS: dlg = wx.MessageDialog( parent=self.guiparent, message=_( @@ -344,7 +344,7 @@ def CheckBands(self, b_1, b_2): ), ) return False - elif mrange > WARN_SCATT_SIZE: + if mrange > WARN_SCATT_SIZE: dlg = wx.MessageDialog( parent=self.guiparent, message=_( diff --git a/gui/wxpython/iscatt/iscatt_core.py b/gui/wxpython/iscatt/iscatt_core.py index a881fab5845..86321ed2ff1 100644 --- a/gui/wxpython/iscatt/iscatt_core.py +++ b/gui/wxpython/iscatt/iscatt_core.py @@ -323,7 +323,7 @@ def _create_grass_region_env(self, bbox): if bbox["maxy"] <= r["s"]: return 0 - elif bbox["maxy"] >= r["n"]: + if bbox["maxy"] >= r["n"]: new_r["n"] = bbox["maxy"] else: new_r["n"] = ( @@ -332,7 +332,7 @@ def _create_grass_region_env(self, bbox): if bbox["miny"] >= r["n"]: return 0 - elif bbox["miny"] <= r["s"]: + if bbox["miny"] <= r["s"]: new_r["s"] = bbox["miny"] else: new_r["s"] = ( @@ -341,7 +341,7 @@ def _create_grass_region_env(self, bbox): if bbox["maxx"] <= r["w"]: return 0 - elif bbox["maxx"] >= r["e"]: + if bbox["maxx"] >= r["e"]: new_r["e"] = bbox["maxx"] else: new_r["e"] = ( @@ -350,7 +350,7 @@ def _create_grass_region_env(self, bbox): if bbox["minx"] >= r["e"]: return 0 - elif bbox["minx"] <= r["w"]: + if bbox["minx"] <= r["w"]: new_r["w"] = bbox["minx"] else: new_r["w"] = ( diff --git a/gui/wxpython/lmgr/frame.py b/gui/wxpython/lmgr/frame.py index 9673ab0f1e5..80f373239f8 100644 --- a/gui/wxpython/lmgr/frame.py +++ b/gui/wxpython/lmgr/frame.py @@ -385,8 +385,7 @@ def _createNotebook(self): """Initialize notebook widget""" if sys.platform == "win32": return GNotebook(parent=self, style=globalvar.FNPageDStyle) - else: - return FormNotebook(parent=self, style=wx.NB_BOTTOM) + return FormNotebook(parent=self, style=wx.NB_BOTTOM) def _createDataCatalog(self, parent): """Initialize Data Catalog widget""" @@ -1089,14 +1088,13 @@ def GetMapDisplay(self, onlyCurrent=True): if onlyCurrent: if self.currentPage: return self.GetLayerTree().GetMapDisplay() - else: - return None - else: # -> return list of all mapdisplays - mlist = [] - for idx in range(self.notebookLayers.GetPageCount()): - mlist.append(self.notebookLayers.GetPage(idx).maptree.GetMapDisplay()) + return None + # -> return list of all mapdisplays + mlist = [] + for idx in range(self.notebookLayers.GetPageCount()): + mlist.append(self.notebookLayers.GetPage(idx).maptree.GetMapDisplay()) - return mlist + return mlist def GetAllMapDisplays(self): """Get all (open) map displays""" diff --git a/gui/wxpython/lmgr/giface.py b/gui/wxpython/lmgr/giface.py index 484a32e8c2d..eac9a7fb2d8 100644 --- a/gui/wxpython/lmgr/giface.py +++ b/gui/wxpython/lmgr/giface.py @@ -86,9 +86,8 @@ def GetSelectedLayer(self, checkedOnly=False): item = self._tree.GetSelectedLayer(multi=False, checkedOnly=checkedOnly) if item is None: return None - else: - data = self._tree.GetPyData(item) - return Layer(item, data) + data = self._tree.GetPyData(item) + return Layer(item, data) def GetLayerInfo(self, layer): """For compatibility only, will be removed.""" @@ -147,12 +146,11 @@ def GetLayersByName(self, name): items = self._tree.FindItemByData(key="name", value=name) if items is None: return [] - else: - layers = [] - for item in items: - layer = Layer(item, self._tree.GetPyData(item)) - layers.append(layer) - return layers + layers = [] + for item in items: + layer = Layer(item, self._tree.GetPyData(item)) + layers.append(layer) + return layers def GetLayerByData(self, key, value): """Returns layer with specified. @@ -168,8 +166,7 @@ def GetLayerByData(self, key, value): item = self._tree.FindItemByData(key=key, value=value) if item is None: return None - else: - return Layer(item, self._tree.GetPyData(item)) + return Layer(item, self._tree.GetPyData(item)) class LayerManagerGrassInterface: @@ -268,8 +265,7 @@ def GetAllMapDisplays(self): def GetMapWindow(self): if self.lmgr.GetMapDisplay(onlyCurrent=True): return self.lmgr.GetMapDisplay(onlyCurrent=True).GetMapWindow() - else: - return None + return None def GetProgress(self): return self.lmgr.goutput.GetProgressBar() diff --git a/gui/wxpython/lmgr/layertree.py b/gui/wxpython/lmgr/layertree.py index 52dc0115480..daefab44223 100644 --- a/gui/wxpython/lmgr/layertree.py +++ b/gui/wxpython/lmgr/layertree.py @@ -2336,8 +2336,7 @@ def FindItemByData(self, key, value): item = self.GetFirstChild(self.root)[0] if key == "name": return self.__FindSubItemByName(item, value) - else: - return self.__FindSubItemByData(item, key, value) + return self.__FindSubItemByData(item, key, value) def FindItemByIndex(self, index): """Find item by index (starting at 0) diff --git a/gui/wxpython/location_wizard/wizard.py b/gui/wxpython/location_wizard/wizard.py index 28ce94fbc48..d8b6b4180ba 100644 --- a/gui/wxpython/location_wizard/wizard.py +++ b/gui/wxpython/location_wizard/wizard.py @@ -591,7 +591,7 @@ def OnText(self, event): self.tproj.SetValue(self.proj) nextButton.Enable(False) return - elif self.proj.lower() == "ll": + if self.proj.lower() == "ll": self.p4proj = "+proj=longlat" else: self.p4proj = "+proj=" + self.proj.lower() @@ -771,8 +771,7 @@ def Sorter(self, key1, key2): if ascending: return cmpVal - else: - return -cmpVal + return -cmpVal def GetListCtrl(self): """Used by listmix.ColumnSorterMixin""" @@ -804,12 +803,10 @@ def Search(self, index, pattern, firstOnly=True): if len(data) > 0: if firstOnly: return data[0] - else: - return data - elif firstOnly: + return data + if firstOnly: return None - else: - return [] + return [] class ProjParamsPage(TitledPage): @@ -1660,29 +1657,28 @@ def OnPageChanging(self, event): if not self.epsgcode: event.Veto() return - else: - # check for datum transforms - ret = RunCommand( - "g.proj", read=True, epsg=self.epsgcode, datum_trans="-1", flags="t" - ) + # check for datum transforms + ret = RunCommand( + "g.proj", read=True, epsg=self.epsgcode, datum_trans="-1", flags="t" + ) - if ret != "": - dtrans = "" - # open a dialog to select datum transform number - dlg = SelectTransformDialog(self.parent.parent, transforms=ret) + if ret != "": + dtrans = "" + # open a dialog to select datum transform number + dlg = SelectTransformDialog(self.parent.parent, transforms=ret) - if dlg.ShowModal() == wx.ID_OK: - dtrans = dlg.GetTransform() - if dtrans == "": - dlg.Destroy() - event.Veto() - return "Datum transform is required." - else: + if dlg.ShowModal() == wx.ID_OK: + dtrans = dlg.GetTransform() + if dtrans == "": dlg.Destroy() event.Veto() return "Datum transform is required." + else: + dlg.Destroy() + event.Veto() + return "Datum transform is required." - self.parent.datum_trans = dtrans + self.parent.datum_trans = dtrans self.GetNext().SetPrev(self) def EnableNext(self, enable=True): @@ -1880,55 +1876,51 @@ def OnPageChanging(self, event): if not self.epsgcode: event.Veto() return - else: - # check for datum transforms - ret = RunCommand( - "g.proj", - read=True, - proj4=self.epsgparams, - datum_trans="-1", - flags="t", - ) + # check for datum transforms + ret = RunCommand( + "g.proj", + read=True, + proj4=self.epsgparams, + datum_trans="-1", + flags="t", + ) - if ret != "": - dtrans = "" - # open a dialog to select datum transform number - dlg = SelectTransformDialog(self.parent.parent, transforms=ret) + if ret != "": + dtrans = "" + # open a dialog to select datum transform number + dlg = SelectTransformDialog(self.parent.parent, transforms=ret) - if dlg.ShowModal() == wx.ID_OK: - dtrans = dlg.GetTransform() - if dtrans == "": - dlg.Destroy() - event.Veto() - return "Datum transform is required." - else: + if dlg.ShowModal() == wx.ID_OK: + dtrans = dlg.GetTransform() + if dtrans == "": dlg.Destroy() event.Veto() return "Datum transform is required." + else: + dlg.Destroy() + event.Veto() + return "Datum transform is required." - self.parent.datum_trans = dtrans - self.parent.epsgcode = self.epsgcode - self.parent.epsgdesc = self.epsgdesc + self.parent.datum_trans = dtrans + self.parent.epsgcode = self.epsgcode + self.parent.epsgdesc = self.epsgdesc - # prepare +nadgrids or +towgs84 terms for Summary page. first - # convert them: - ret, projlabel, err = RunCommand( - "g.proj", - flags="jft", - proj4=self.epsgparams, - datum_trans=self.parent.datum_trans, - getErrorMsg=True, - read=True, - ) - # splitting on space alone would break for grid files with - # space in pathname - for projterm in projlabel.split(" +"): - if ( - projterm.find("towgs84=") != -1 - or projterm.find("nadgrids=") != -1 - ): - self.custom_dtrans_string = " +%s" % projterm - break + # prepare +nadgrids or +towgs84 terms for Summary page. first + # convert them: + ret, projlabel, err = RunCommand( + "g.proj", + flags="jft", + proj4=self.epsgparams, + datum_trans=self.parent.datum_trans, + getErrorMsg=True, + read=True, + ) + # splitting on space alone would break for grid files with + # space in pathname + for projterm in projlabel.split(" +"): + if projterm.find("towgs84=") != -1 or projterm.find("nadgrids=") != -1: + self.custom_dtrans_string = " +%s" % projterm + break self.GetNext().SetPrev(self) diff --git a/gui/wxpython/main_window/frame.py b/gui/wxpython/main_window/frame.py index 0bb28afd883..cef4aee7121 100644 --- a/gui/wxpython/main_window/frame.py +++ b/gui/wxpython/main_window/frame.py @@ -1240,14 +1240,13 @@ def GetMapDisplay(self, onlyCurrent=True): if onlyCurrent: if self.currentPage: return self.GetLayerTree().GetMapDisplay() - else: - return None - else: # -> return list of all mapdisplays - mlist = [] - for idx in range(self.notebookLayers.GetPageCount()): - mlist.append(self.notebookLayers.GetPage(idx).maptree.GetMapDisplay()) + return None + # -> return list of all mapdisplays + mlist = [] + for idx in range(self.notebookLayers.GetPageCount()): + mlist.append(self.notebookLayers.GetPage(idx).maptree.GetMapDisplay()) - return mlist + return mlist def GetAllMapDisplays(self): """Get all (open) map displays""" diff --git a/gui/wxpython/mapdisp/frame.py b/gui/wxpython/mapdisp/frame.py index f13ffbe0a81..c9b5d86e4ba 100644 --- a/gui/wxpython/mapdisp/frame.py +++ b/gui/wxpython/mapdisp/frame.py @@ -1230,8 +1230,7 @@ def AddTmpVectorMapLayer(self, name, cats, useId=False, addLayer=True): render=True, **args, ) - else: - return cmd + return cmd def OnMeasureDistance(self, event): self._onMeasure(MeasureDistanceController) diff --git a/gui/wxpython/mapdisp/main.py b/gui/wxpython/mapdisp/main.py index 016261d248d..6449bc35d7c 100644 --- a/gui/wxpython/mapdisp/main.py +++ b/gui/wxpython/mapdisp/main.py @@ -334,14 +334,14 @@ def __init__(self, maplayer): def __getattr__(self, name): if name == "cmd": return cmdtuple_to_list(self._maplayer.GetCmd()) - elif hasattr(self._maplayer, name): + if hasattr(self._maplayer, name): return getattr(self._maplayer, name) - elif name == "maplayer": + if name == "maplayer": return self._maplayer - elif name == "type": + if name == "type": return self._maplayer.GetType() # elif name == 'ctrl': - elif name == "label": + if name == "label": return self._maplayer.GetName() # elif name == 'propwin': @@ -386,8 +386,7 @@ def GetSelectedLayer(self, checkedOnly=False): layers = self.GetSelectedLayers() if len(layers) > 0: return layers[0] - else: - return None + return None def AddLayer(self, ltype, name=None, checked=None, opacity=1.0, cmd=None): """Adds a new layer to the layer list. diff --git a/gui/wxpython/mapdisp/statusbar.py b/gui/wxpython/mapdisp/statusbar.py index 649baebc18d..4fea1ffcc81 100644 --- a/gui/wxpython/mapdisp/statusbar.py +++ b/gui/wxpython/mapdisp/statusbar.py @@ -631,10 +631,8 @@ def GetCenterString(self, map): return "%s" % utils.Deg2DMS( coord[0], coord[1], precision=precision ) - else: - return "%.*f; %.*f" % (precision, coord[0], precision, coord[1]) - else: - raise SbException(_("Error in projection (check the settings)")) + return "%.*f; %.*f" % (precision, coord[0], precision, coord[1]) + raise SbException(_("Error in projection (check the settings)")) elif self.mapFrame.GetMap().projinfo["proj"] == "ll" and format == "DMS": return "%s" % utils.Deg2DMS( region["center_easting"], @@ -806,10 +804,8 @@ def ReprojectENFromMap(self, e, n, useDefinedProjection, precision, format): e, n = coord if proj in {"ll", "latlong", "longlat"} and format == "DMS": return utils.Deg2DMS(e, n, precision=precision) - else: - return "%.*f; %.*f" % (precision, e, precision, n) - else: - raise SbException(_("Error in projection (check the settings)")) + return "%.*f; %.*f" % (precision, e, precision, n) + raise SbException(_("Error in projection (check the settings)")) elif self.mapFrame.GetMap().projinfo["proj"] == "ll" and format == "DMS": return utils.Deg2DMS(e, n, precision=precision) else: @@ -860,8 +856,7 @@ def _formatRegion(self, w, e, s, n, nsres, ewres, precision=None): precision, n, ) - else: - return "%s - %s, %s - %s" % (w, e, s, n) + return "%s - %s, %s - %s" % (w, e, s, n) def ReprojectRegionFromMap(self, region, useDefinedProjection, precision, format): """Reproject region values @@ -910,21 +905,19 @@ def ReprojectRegionFromMap(self, region, useDefinedProjection, precision, format return self._formatRegion( w=w, s=s, e=e, n=n, ewres=ewres, nsres=nsres ) - else: - w, s = coord1 - e, n = coord2 - ewres, nsres = coord3 - return self._formatRegion( - w=w, - s=s, - e=e, - n=n, - ewres=ewres, - nsres=nsres, - precision=precision, - ) - else: - raise SbException(_("Error in projection (check the settings)")) + w, s = coord1 + e, n = coord2 + ewres, nsres = coord3 + return self._formatRegion( + w=w, + s=s, + e=e, + n=n, + ewres=ewres, + nsres=nsres, + precision=precision, + ) + raise SbException(_("Error in projection (check the settings)")) elif self.mapFrame.GetMap().projinfo["proj"] == "ll" and format == "DMS": w, s = utils.Deg2DMS( @@ -971,8 +964,7 @@ def _formatRegion(self, w, e, s, n, ewres, nsres, precision=None): precision, nsres, ) - else: - return "%s - %s, %s - %s (%s, %s)" % (w, e, s, n, ewres, nsres) + return "%s - %s, %s - %s (%s, %s)" % (w, e, s, n, ewres, nsres) def _getRegion(self): """Returns computational region.""" diff --git a/gui/wxpython/mapswipe/dialogs.py b/gui/wxpython/mapswipe/dialogs.py index 945aee590ea..a765f3774ce 100644 --- a/gui/wxpython/mapswipe/dialogs.py +++ b/gui/wxpython/mapswipe/dialogs.py @@ -241,8 +241,7 @@ def GetValues(self): """Get raster maps""" if self.IsSimpleMode(): return (self._firstRaster.GetValue(), self._secondRaster.GetValue()) - else: - return (self._firstLayerList, self._secondLayerList) + return (self._firstLayerList, self._secondLayerList) def IsSimpleMode(self) -> bool: return bool(self._switchSizer.IsShown(self._firstPanel)) diff --git a/gui/wxpython/mapswipe/mapwindow.py b/gui/wxpython/mapswipe/mapwindow.py index dbb181eb62b..a203ef2e6d1 100644 --- a/gui/wxpython/mapswipe/mapwindow.py +++ b/gui/wxpython/mapswipe/mapwindow.py @@ -81,8 +81,7 @@ def GetClientSize(self): """Overridden method which returns simulated window size.""" if self._mode == "swipe": return self.specialSize - else: - return super().GetClientSize() + return super().GetClientSize() def SetClientSize(self, size): """Overridden method which sets simulated window size.""" @@ -100,8 +99,7 @@ def GetImageCoords(self): """Returns coordinates of rendered image""" if self._mode == "swipe": return self.specialCoords - else: - return (0, 0) + return (0, 0) def SetImageCoords(self, coords): """Sets coordinates of rendered image""" diff --git a/gui/wxpython/mapwin/buffered.py b/gui/wxpython/mapwin/buffered.py index d8c6413017b..c4b07a94fd2 100644 --- a/gui/wxpython/mapwin/buffered.py +++ b/gui/wxpython/mapwin/buffered.py @@ -560,8 +560,7 @@ def TextBounds(self, textinfo, relcoords=False): bbox[2], bbox[3] = w, h if relcoords: return coords, bbox, relCoords - else: - return coords, bbox + return coords, bbox boxh = math.fabs(math.sin(math.radians(rotation)) * w) + h boxw = math.fabs(math.cos(math.radians(rotation)) * w) + h @@ -580,8 +579,7 @@ def TextBounds(self, textinfo, relcoords=False): bbox.Inflate(h, h) if relcoords: return coords, bbox, relCoords - else: - return coords, bbox + return coords, bbox def OnPaint(self, event): """Draw PseudoDC's to buffered paint DC diff --git a/gui/wxpython/modules/import_export.py b/gui/wxpython/modules/import_export.py index 43115c943da..f2f70e47831 100644 --- a/gui/wxpython/modules/import_export.py +++ b/gui/wxpython/modules/import_export.py @@ -538,8 +538,7 @@ def _getCommand(self): """Get command""" if self.link: return "r.external" - else: - return "r.import" + return "r.import" def _getBlackListedParameters(self): """Get flags which will not be showed in Settings page""" @@ -693,8 +692,7 @@ def _getCommand(self): """Get command""" if self.link: return "v.external" - else: - return "v.import" + return "v.import" def _getBlackListedParameters(self): """Get parametrs which will not be showed in Settings page""" diff --git a/gui/wxpython/nviz/mapwindow.py b/gui/wxpython/nviz/mapwindow.py index 550939da7ce..8cf972704e2 100644 --- a/gui/wxpython/nviz/mapwindow.py +++ b/gui/wxpython/nviz/mapwindow.py @@ -2358,10 +2358,10 @@ def GetLayerId(self, type, name, vsubtyp=None): try: if type == "raster": return data["surface"]["object"]["id"] - elif type == "vector": + if type == "vector": if vsubtyp == "vpoint": return data["vector"]["points"]["object"]["id"] - elif vsubtyp == "vline": + if vsubtyp == "vline": return data["vector"]["lines"]["object"]["id"] elif type == "raster_3d": return data["volume"]["object"]["id"] diff --git a/gui/wxpython/nviz/tools.py b/gui/wxpython/nviz/tools.py index 0659c5cc2ba..20b1df1ffbd 100644 --- a/gui/wxpython/nviz/tools.py +++ b/gui/wxpython/nviz/tools.py @@ -2646,9 +2646,9 @@ def GetLayerData(self, nvizType, nameOnly=False): if nvizType in {"surface", "fringe"}: return self._getLayerPropertiesByName(name, mapType="raster") - elif nvizType == "vector": + if nvizType == "vector": return self._getLayerPropertiesByName(name, mapType="vector") - elif nvizType == "volume": + if nvizType == "volume": return self._getLayerPropertiesByName(name, mapType="raster_3d") return None @@ -2857,7 +2857,7 @@ def OnSaveAnimation(self, event): if not prefix: GMessage(parent=self, message=_("No file prefix given.")) return - elif not os.path.exists(dir): + if not os.path.exists(dir): GMessage(parent=self, message=_("Directory %s does not exist.") % dir) return @@ -4576,7 +4576,7 @@ def OnVolumeSelect(self, event): selection = event.GetSelection() if selection == -1: return - elif selection == 0: + if selection == 0: winUp.Enable(False) if not winDown.IsEnabled(): winDown.Enable() diff --git a/gui/wxpython/nviz/wxnviz.py b/gui/wxpython/nviz/wxnviz.py index e80a9fc9382..47cf99c079d 100644 --- a/gui/wxpython/nviz/wxnviz.py +++ b/gui/wxpython/nviz/wxnviz.py @@ -246,8 +246,7 @@ def GetFocus(self): z = c_float() Nviz_get_focus(self.data, byref(x), byref(y), byref(z)) return x.value, y.value, z.value - else: - return -1, -1, -1 + return -1, -1, -1 def SetFocus(self, x, y, z): """Set focus""" @@ -1873,8 +1872,7 @@ def SetCPlaneInteractively(self, x, y): Nviz_draw_cplane(self.data, -1, -1) x, y, z = self.GetCPlaneTranslation() return x, y, z - else: - return None, None, None + return None, None, None def SelectCPlane(self, index): """Select cutting plane diff --git a/gui/wxpython/psmap/dialogs.py b/gui/wxpython/psmap/dialogs.py index 41493795893..66ac66d9348 100644 --- a/gui/wxpython/psmap/dialogs.py +++ b/gui/wxpython/psmap/dialogs.py @@ -526,8 +526,7 @@ def OnApply(self, event): if ok: self.parent.DialogDataChanged(id=self.id) return True - else: - return False + return False def OnOK(self, event): """Apply changes, close dialog""" diff --git a/gui/wxpython/psmap/frame.py b/gui/wxpython/psmap/frame.py index fa4a150aa3b..b3196d348fa 100644 --- a/gui/wxpython/psmap/frame.py +++ b/gui/wxpython/psmap/frame.py @@ -994,8 +994,7 @@ def getModifiedTextBounds(self, x, y, textExtent, rotation): Y = y - H if rotation == 0: return Rect(x, y, *textExtent) - else: - return Rect(X, Y, abs(W), abs(H)).Inflate(h, h) + return Rect(X, Y, abs(W), abs(H)).Inflate(h, h) def makePSFont(self, textDict): """creates a wx.Font object from selected postscript font. To be diff --git a/gui/wxpython/psmap/instructions.py b/gui/wxpython/psmap/instructions.py index 3c1b2e6d7bb..9db64679422 100644 --- a/gui/wxpython/psmap/instructions.py +++ b/gui/wxpython/psmap/instructions.py @@ -1217,9 +1217,9 @@ def GetImageOrigSize(self, imagePath): break file.close() return float(w), float(h) - else: # we can use wx.Image - img = wx.Image(fileName, type=wx.BITMAP_TYPE_ANY) - return img.GetWidth(), img.GetHeight() + # we can use wx.Image + img = wx.Image(fileName, type=wx.BITMAP_TYPE_ANY) + return img.GetWidth(), img.GetHeight() class NorthArrow(Image): diff --git a/gui/wxpython/psmap/utils.py b/gui/wxpython/psmap/utils.py index d54a45a52be..6bb53acd9f0 100644 --- a/gui/wxpython/psmap/utils.py +++ b/gui/wxpython/psmap/utils.py @@ -162,17 +162,15 @@ def convertRGB(rgb): return name return str(rgb.Red()) + ":" + str(rgb.Green()) + ":" + str(rgb.Blue()) # transform a GRASS named color or an r:g:b string into a wx.Colour tuple - else: - color = ( - int(gs.parse_color(rgb)[0] * 255), - int(gs.parse_color(rgb)[1] * 255), - int(gs.parse_color(rgb)[2] * 255), - ) - color = wx.Colour(*color) - if color.IsOk(): - return color - else: - return None + color = ( + int(gs.parse_color(rgb)[0] * 255), + int(gs.parse_color(rgb)[1] * 255), + int(gs.parse_color(rgb)[2] * 255), + ) + color = wx.Colour(*color) + if color.IsOk(): + return color + return None def PaperMapCoordinates(mapInstr, x, y, paperToMap=True, env=None): @@ -198,18 +196,16 @@ def PaperMapCoordinates(mapInstr, x, y, paperToMap=True, env=None): if projInfo()["proj"] == "ll": return e, n - else: - return int(e), int(n) + return int(e), int(n) - else: - diffEW = x - region["w"] - diffNS = region["n"] - y - diffX = mapWidthPaper * diffEW / mapWidthEN - diffY = mapHeightPaper * diffNS / mapHeightEN - xPaper = mapInstr["rect"].GetX() + diffX - yPaper = mapInstr["rect"].GetY() + diffY + diffEW = x - region["w"] + diffNS = region["n"] - y + diffX = mapWidthPaper * diffEW / mapWidthEN + diffY = mapHeightPaper * diffNS / mapHeightEN + xPaper = mapInstr["rect"].GetX() + diffX + yPaper = mapInstr["rect"].GetY() + diffY - return xPaper, yPaper + return xPaper, yPaper def AutoAdjust(self, scaleType, rect, env, map=None, mapType=None, region=None): @@ -403,8 +399,7 @@ def getRasterType(map): file = gs.find_file(name=map, element="cell") if file.get("file"): return gs.raster_info(map)["datatype"] - else: - return None + return None def BBoxAfterRotation(w, h, angle): diff --git a/gui/wxpython/rdigit/controller.py b/gui/wxpython/rdigit/controller.py index 2e98ca32ccc..8018ee04e5c 100644 --- a/gui/wxpython/rdigit/controller.py +++ b/gui/wxpython/rdigit/controller.py @@ -421,28 +421,26 @@ def SelectNewMap( ) return False return True - else: - dlg = NewRasterDialog(parent=self._mapWindow) - dlg.CenterOnParent() - if dlg.ShowModal() == wx.ID_OK: - try: - self._createNewMap( - mapName=dlg.GetMapName(), - backgroundMap=dlg.GetBackgroundMapName(), - mapType=dlg.GetMapType(), - ) - except ScriptError: - GError( - parent=self._mapWindow, - message=_("Failed to create new raster map."), - ) - return False - finally: - dlg.Destroy() - return True - else: - dlg.Destroy() + dlg = NewRasterDialog(parent=self._mapWindow) + dlg.CenterOnParent() + if dlg.ShowModal() == wx.ID_OK: + try: + self._createNewMap( + mapName=dlg.GetMapName(), + backgroundMap=dlg.GetBackgroundMapName(), + mapType=dlg.GetMapType(), + ) + except ScriptError: + GError( + parent=self._mapWindow, + message=_("Failed to create new raster map."), + ) return False + finally: + dlg.Destroy() + return True + dlg.Destroy() + return False def _createNewMap(self, mapName, backgroundMap, mapType): """Creates a new raster map based on specified background and type.""" diff --git a/gui/wxpython/rdigit/dialogs.py b/gui/wxpython/rdigit/dialogs.py index c632a84c0fe..fa9f57b8253 100644 --- a/gui/wxpython/rdigit/dialogs.py +++ b/gui/wxpython/rdigit/dialogs.py @@ -117,9 +117,8 @@ def OnOK(self, event): if dlgOverwrite.ShowModal() != wx.ID_YES: dlgOverwrite.Destroy() return - else: - dlgOverwrite.Destroy() - self.EndModal(wx.ID_OK) + dlgOverwrite.Destroy() + self.EndModal(wx.ID_OK) else: self.EndModal(wx.ID_OK) diff --git a/gui/wxpython/rlisetup/functions.py b/gui/wxpython/rlisetup/functions.py index 2ce0b747dcd..fc9d3c3d202 100644 --- a/gui/wxpython/rlisetup/functions.py +++ b/gui/wxpython/rlisetup/functions.py @@ -60,9 +60,8 @@ def retRLiPath(): rlipath = os.path.join(grass_config_dir, "r.li") if os.path.exists(rlipath): return rlipath - else: - os.mkdir(rlipath) - return rlipath + os.mkdir(rlipath) + return rlipath def checkMapExists(name, typ="raster") -> bool: diff --git a/gui/wxpython/rlisetup/sampling_frame.py b/gui/wxpython/rlisetup/sampling_frame.py index 860c0b91d8f..a379ef8c09c 100644 --- a/gui/wxpython/rlisetup/sampling_frame.py +++ b/gui/wxpython/rlisetup/sampling_frame.py @@ -550,33 +550,32 @@ def _toolbarData(self): ), ) ) - else: - return self._getToolbarData( + return self._getToolbarData( + ( + drawTool, + (None,), ( - drawTool, - (None,), - ( - ("pan", BaseIcons["pan"].label), - BaseIcons["pan"], - self.parent.OnPan, - wx.ITEM_CHECK, - ), - ( - ("zoomIn", BaseIcons["zoomIn"].label), - BaseIcons["zoomIn"], - self.parent.OnZoomIn, - wx.ITEM_CHECK, - ), - ( - ("zoomOut", BaseIcons["zoomOut"].label), - BaseIcons["zoomOut"], - self.parent.OnZoomOut, - wx.ITEM_CHECK, - ), - ( - ("zoomExtent", BaseIcons["zoomExtent"].label), - BaseIcons["zoomExtent"], - self.parent.OnZoomToMap, - ), - ) + ("pan", BaseIcons["pan"].label), + BaseIcons["pan"], + self.parent.OnPan, + wx.ITEM_CHECK, + ), + ( + ("zoomIn", BaseIcons["zoomIn"].label), + BaseIcons["zoomIn"], + self.parent.OnZoomIn, + wx.ITEM_CHECK, + ), + ( + ("zoomOut", BaseIcons["zoomOut"].label), + BaseIcons["zoomOut"], + self.parent.OnZoomOut, + wx.ITEM_CHECK, + ), + ( + ("zoomExtent", BaseIcons["zoomExtent"].label), + BaseIcons["zoomExtent"], + self.parent.OnZoomToMap, + ), ) + ) diff --git a/gui/wxpython/rlisetup/wizard.py b/gui/wxpython/rlisetup/wizard.py index 4ecbb60e188..6fb7988159d 100644 --- a/gui/wxpython/rlisetup/wizard.py +++ b/gui/wxpython/rlisetup/wizard.py @@ -720,13 +720,12 @@ def CheckVector(self, vector): % vector ) return False, [] - elif links > 0: + if links > 0: layers = [] for i in range(1, links + 1): layers.append(str(i)) return True, layers - else: - return False, [] + return False, [] def CheckInput(self): """Check input fields. @@ -1819,14 +1818,13 @@ def afterRegionDrawn(self): self.areaOK.Enable(False) self.areaNO.Enable(False) return True - else: - self.title.SetLabel( - _("Select sample area {areas_count} of {area_num}").format( - areas_count=self.areascount + 1, area_num=self.areanum - ) + self.title.SetLabel( + _("Select sample area {areas_count} of {area_num}").format( + areas_count=self.areascount + 1, area_num=self.areanum ) - wx.FindWindowById(wx.ID_FORWARD).Enable(False) - return False + ) + wx.FindWindowById(wx.ID_FORWARD).Enable(False) + return False def OnYes(self, event): """Function to create the string for the conf file if the area diff --git a/gui/wxpython/startup/locdownload.py b/gui/wxpython/startup/locdownload.py index 75eae560244..ccbeaa8fb0c 100644 --- a/gui/wxpython/startup/locdownload.py +++ b/gui/wxpython/startup/locdownload.py @@ -386,8 +386,7 @@ def CheckItem(self, item): ) self.parent.download_button.SetLabel(label=_("Download")) return - else: - self._clearMessage() + self._clearMessage() def GetLocation(self): """Get the name of the last location downloaded by the user""" @@ -496,9 +495,8 @@ def OnCancel(self, event=None): if ret == wx.ID_NO: return - else: - self.panel.thread.Terminate() - self.panel._change_download_btn_label() + self.panel.thread.Terminate() + self.panel._change_download_btn_label() if event: self.EndModal(wx.ID_CANCEL) diff --git a/gui/wxpython/vdigit/wxdigit.py b/gui/wxpython/vdigit/wxdigit.py index 154d5ab7c81..eda912284a0 100644 --- a/gui/wxpython/vdigit/wxdigit.py +++ b/gui/wxpython/vdigit/wxdigit.py @@ -294,10 +294,8 @@ def _getSnapMode(self): if threshold > 0.0: if UserSettings.Get(group="vdigit", key="snapToVertex", subkey="enabled"): return SNAPVERTEX - else: - return SNAP - else: - return NO_SNAP + return SNAP + return NO_SNAP def _getNewFeaturesLayer(self): """Returns layer of new feature (from settings)""" @@ -672,8 +670,7 @@ def _getLineAreaBboxCats(self, ln_id): # TODO centroid opttimization, can be edited also its area -> it # will appear two times in new_ lists return self._getCentroidAreaBboxCats(ln_id) - else: - return [self._getBbox(ln_id)], [self._getLineAreasCategories(ln_id)] + return [self._getBbox(ln_id)], [self._getLineAreasCategories(ln_id)] def _getCentroidAreaBboxCats(self, centroid): """Helper function @@ -688,8 +685,7 @@ def _getCentroidAreaBboxCats(self, centroid): area = Vect_get_centroid_area(self.poMapInfo, centroid) if area > 0: return self._getaAreaBboxCats(area) - else: - return None + return None def _getaAreaBboxCats(self, area): """Helper function @@ -1946,8 +1942,7 @@ def _addFeature(self, ftype, coords, layer, cat, snap, threshold): if newc < 0: self._error.WriteLine() return (len(fids), fids) - else: - fids.append(newc) + fids.append(newc) if right > 0 and Vect_get_area_centroid(self.poMapInfo, right) == 0: # if Vect_get_area_points(byref(self.poMapInfo), right, bpoints) > 0 and @@ -1964,8 +1959,7 @@ def _addFeature(self, ftype, coords, layer, cat, snap, threshold): if newc < 0: self._error.WriteLine() return len(fids, fids) - else: - fids.append(newc) + fids.append(newc) Vect_destroy_line_struct(bpoints) diff --git a/gui/wxpython/vnet/dialogs.py b/gui/wxpython/vnet/dialogs.py index a445b01e8eb..39cf2e4ae84 100644 --- a/gui/wxpython/vnet/dialogs.py +++ b/gui/wxpython/vnet/dialogs.py @@ -766,7 +766,7 @@ def OnVectSel(self, event): for sel in ["arc_column", "arc_backward_column", "node_column"]: self.inputData[sel].SetValue("") return - elif itemsLen == 1: + if itemsLen == 1: self.inputData["arc_layer"].SetSelection(0) self.inputData["node_layer"].SetSelection(0) elif itemsLen >= 1: @@ -1648,7 +1648,7 @@ def InputSel(self): self._updateInputDbMgrPage(show=False) self.inputData["arc_layer"].SetValue("") return - elif itemsLen == 1: + if itemsLen == 1: self.inputData["arc_layer"].SetSelection(0) elif itemsLen >= 1: if "1" in items: diff --git a/gui/wxpython/vnet/vnet_core.py b/gui/wxpython/vnet/vnet_core.py index ac7625a4c22..8dda73b6770 100644 --- a/gui/wxpython/vnet/vnet_core.py +++ b/gui/wxpython/vnet/vnet_core.py @@ -144,8 +144,7 @@ def RunAnalysis(self): ) if not ret: return -3 - else: - return 1 + return 1 def RunAnDone(self, cmd, returncode, results): self.results["analysis"] = cmd[0] @@ -307,19 +306,18 @@ def _createTtbDone(self, event): if event.returncode != 0: GMessage(parent=self.guiparent, message=_("Creation of turntable failed.")) return - else: - params = {} - for c in event.cmd: - spl_c = c.split("=") - if len(spl_c) != 2: - continue + params = {} + for c in event.cmd: + spl_c = c.split("=") + if len(spl_c) != 2: + continue - if spl_c[0] and spl_c != "input": - params[spl_c[0]] = spl_c[1] - if spl_c[0] == "output": - params["input"] = spl_c[1] + if spl_c[0] and spl_c != "input": + params[spl_c[0]] = spl_c[1] + if spl_c[0] == "output": + params["input"] = spl_c[1] - self.vnet_data.SetParams(params, {}) + self.vnet_data.SetParams(params, {}) self.ttbCreated.emit(returncode=event.returncode) @@ -1147,14 +1145,10 @@ def ComputeNodes(self, activate): return 0 # map is already created and up to date for input data - else: - self.snapPts.AddRenderLayer() - - self.giface.updateMap.emit(render=True, renderVector=True) - - self.snapping.emit(evt="computing_points_done") - - return 1 + self.snapPts.AddRenderLayer() + self.giface.updateMap.emit(render=True, renderVector=True) + self.snapping.emit(evt="computing_points_done") + return 1 def _onNodesDone(self, event): """Update map window, when map with nodes to snap is created""" diff --git a/gui/wxpython/vnet/vnet_data.py b/gui/wxpython/vnet/vnet_data.py index 9c5b592e043..3adc5fa64e9 100644 --- a/gui/wxpython/vnet/vnet_data.py +++ b/gui/wxpython/vnet/vnet_data.py @@ -78,16 +78,14 @@ def GetGlobalTurnsData(self): def GetRelevantParams(self, analysis=None): if analysis: return self.an_props.GetRelevantParams(analysis) - else: - analysis, valid = self.an_params.GetParam("analysis") - return self.an_props.GetRelevantParams(analysis) + analysis, valid = self.an_params.GetParam("analysis") + return self.an_props.GetRelevantParams(analysis) def GetAnalysisProperties(self, analysis=None): if analysis: return self.an_props[analysis] - else: - analysis, valid = self.an_params.GetParam("analysis") - return self.an_props[analysis] + analysis, valid = self.an_params.GetParam("analysis") + return self.an_props[analysis] def GetParam(self, param): return self.an_params.GetParam(param) diff --git a/gui/wxpython/web_services/cap_interface.py b/gui/wxpython/web_services/cap_interface.py index 4287375dd47..38cc7c3c72f 100644 --- a/gui/wxpython/web_services/cap_interface.py +++ b/gui/wxpython/web_services/cap_interface.py @@ -49,8 +49,7 @@ def GetRootLayer(self): """Get children layers""" if self.layers_by_id: return self.layers_by_id[0] - else: - return None + return None class LayerBase: @@ -137,15 +136,13 @@ def GetLayerData(self, param): title_node = self.layer_node.find(title) if title_node is not None: return title_node.text - else: - return None + return None if param == "name": name_node = self.layer_node.find(name) if name_node is not None: return name_node.text - else: - return None + return None if param == "format": return self.cap.GetFormats() @@ -228,22 +225,20 @@ def GetLayerData(self, param): if self.layer_node is None and param in {"title", "name"}: return None - elif self.layer_node is None: + if self.layer_node is None: return [] if param == "title": title_node = self.layer_node.find(title) if title_node is not None: return title_node.text - else: - return None + return None if param == "name": name_node = self.layer_node.find(name) if name_node is not None: return name_node.text - else: - return None + return None if param == "styles": styles = [] @@ -366,22 +361,20 @@ def GetLayerData(self, param): """Get layer data""" if self.layer_node is None and param in {"title", "name"}: return None - elif self.layer_node is None: + if self.layer_node is None: return [] if param == "title": title_node = self.layer_node.find("Title") if title_node is not None: return title_node.text - else: - return None + return None if param == "name": name_node = self.layer_node.find("Name") if name_node is not None: return name_node.text - else: - return None + return None if param == "styles": return [] diff --git a/gui/wxpython/wxplot/histogram.py b/gui/wxpython/wxplot/histogram.py index f8319747dab..ef252f0b052 100644 --- a/gui/wxpython/wxplot/histogram.py +++ b/gui/wxpython/wxplot/histogram.py @@ -243,8 +243,7 @@ def CreatePlotList(self): if len(self.plotlist) > 0: return self.plotlist - else: - return None + return None def Update(self): """Update histogram after changing options""" diff --git a/gui/wxpython/wxplot/profile.py b/gui/wxpython/wxplot/profile.py index 61efdcd67d0..240290e5b7b 100644 --- a/gui/wxpython/wxplot/profile.py +++ b/gui/wxpython/wxplot/profile.py @@ -398,8 +398,7 @@ def CreatePlotList(self): if len(self.plotlist) > 0: return self.plotlist - else: - return None + return None def Update(self): """Update profile after changing options""" diff --git a/lib/init/grass.py b/lib/init/grass.py index 233bcf53460..f489ad1fe53 100755 --- a/lib/init/grass.py +++ b/lib/init/grass.py @@ -737,22 +737,21 @@ def cannot_create_location_reason(gisdbase, location): " the project <{location}>" " already exists." ).format(**locals()) - elif os.path.isfile(path): + if os.path.isfile(path): return _( "Unable to create new project <{location}> because <{path}> is a file." ).format(**locals()) - elif os.path.isdir(path): + if os.path.isdir(path): return _( "Unable to create new project <{location}> because" " the directory <{path}>" " already exists." ).format(**locals()) - else: - return _( - "Unable to create new project in" - " the directory <{path}>" - " for an unknown reason." - ).format(**locals()) + return _( + "Unable to create new project in" + " the directory <{path}>" + " for an unknown reason." + ).format(**locals()) def set_mapset( @@ -1419,7 +1418,7 @@ def script_path(batch_job): if script_in_addon_path and os.path.exists(script_in_addon_path): batch_job[0] = script_in_addon_path return script_in_addon_path - elif os.path.exists(batch_job[0]): + if os.path.exists(batch_job[0]): return batch_job[0] try: diff --git a/locale/grass_po_stats.py b/locale/grass_po_stats.py index 0c49e44bc8d..bfae49629d3 100644 --- a/locale/grass_po_stats.py +++ b/locale/grass_po_stats.py @@ -81,10 +81,9 @@ def langDefinition(fil): f.close() if len(lang) == 2: return " ".join(lang) - elif len(lang) == 1: + if len(lang) == 1: return lang[0] - else: - return "" + return "" def get_stats(languages, directory): diff --git a/man/build_html.py b/man/build_html.py index c89df29e290..0ef860d42f3 100644 --- a/man/build_html.py +++ b/man/build_html.py @@ -494,8 +494,7 @@ def get_desc(cmd): sp = line.split("-", 1) if len(sp) > 1: return sp[1].strip() - else: - return None + return None return "" diff --git a/man/build_manual_gallery.py b/man/build_manual_gallery.py index 51a6fbcd312..a8b077be8f9 100755 --- a/man/build_manual_gallery.py +++ b/man/build_manual_gallery.py @@ -132,8 +132,7 @@ def title_from_names(module_name, img_name): img_name = img_name.strip() if img_name: return "{name} ({desc})".format(name=module_name, desc=img_name) - else: - return "{name}".format(name=module_name) + return "{name}".format(name=module_name) def get_module_name(filename): diff --git a/man/build_rest.py b/man/build_rest.py index 473827fcc86..a98a099a076 100644 --- a/man/build_rest.py +++ b/man/build_rest.py @@ -355,8 +355,7 @@ def get_desc(cmd): sp = line.split("-", 1) if len(sp) > 1: return sp[1].strip() - else: - return None + return None return "" diff --git a/pyproject.toml b/pyproject.toml index ce708210d5b..6147e2db897 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -200,7 +200,6 @@ ignore = [ "RET501", # unnecessary-return-none "RET502", # implicit-return-value "RET503", # implicit-return - "RET505", # superfluous-else-return "RET506", # superfluous-else-raise "RET507", # superfluous-else-continue "RET508", # superfluous-else-break diff --git a/python/grass/grassdb/checks.py b/python/grass/grassdb/checks.py index 6916ea2748a..369cdf60e62 100644 --- a/python/grass/grassdb/checks.py +++ b/python/grass/grassdb/checks.py @@ -222,13 +222,13 @@ def get_reason_id_mapset_not_usable(mapset_path): if not os.path.exists(mapset_path): return "non-existent" # Check whether mapset is valid - elif not is_mapset_valid(mapset_path): + if not is_mapset_valid(mapset_path): return "invalid" # Check whether mapset is owned by current user - elif not is_current_user_mapset_owner(mapset_path): + if not is_current_user_mapset_owner(mapset_path): return "different-owner" # Check whether mapset is locked - elif is_mapset_locked(mapset_path): + if is_mapset_locked(mapset_path): return "locked" return None diff --git a/python/grass/gunittest/case.py b/python/grass/gunittest/case.py index b5563e47e9e..1277ee542b5 100644 --- a/python/grass/gunittest/case.py +++ b/python/grass/gunittest/case.py @@ -736,10 +736,9 @@ def _get_unique_name(self, name): # and ensure uniqueness by add UUID if self.readable_names: return "tmp_" + self.id().replace(".", "_") + "_" + name - else: - # UUID might be overkill (and expensive) but it's safe and simple - # alternative is to create hash from the readable name - return "tmp_" + str(uuid.uuid4()).replace("-", "") + # UUID might be overkill (and expensive) but it's safe and simple + # alternative is to create hash from the readable name + return "tmp_" + str(uuid.uuid4()).replace("-", "") def _compute_difference_raster(self, first, second, name_part): """Compute difference of two rasters (first - second) diff --git a/python/grass/gunittest/multireport.py b/python/grass/gunittest/multireport.py index f183d4e0219..c5f36a6e0db 100644 --- a/python/grass/gunittest/multireport.py +++ b/python/grass/gunittest/multireport.py @@ -104,8 +104,7 @@ def median(values): sorted_values = sorted(values) if n % 2 == 0: return (sorted_values[n / 2 - 1] + sorted_values[n / 2]) / 2 - else: - return sorted_values[n / 2] + return sorted_values[n / 2] # this is useful for debugging or some other stat # cmeans = [] diff --git a/python/grass/gunittest/multirunner.py b/python/grass/gunittest/multirunner.py index fd648406332..ebb9e80049a 100644 --- a/python/grass/gunittest/multirunner.py +++ b/python/grass/gunittest/multirunner.py @@ -30,15 +30,13 @@ def _get_encoding(): def decode(bytes_, encoding=None): if isinstance(bytes_, bytes): return bytes_.decode(_get_encoding()) - else: - return bytes_ + return bytes_ def encode(string, encoding=None): if isinstance(string, str): return string.encode(_get_encoding()) - else: - return string + return string def text_to_string(text): diff --git a/python/grass/gunittest/reporters.py b/python/grass/gunittest/reporters.py index 9535e07f871..09f371dfffc 100644 --- a/python/grass/gunittest/reporters.py +++ b/python/grass/gunittest/reporters.py @@ -110,8 +110,7 @@ def get_source_url(path, revision, line=None): tracurl = "http://trac.osgeo.org/grass/browser/" if line: return "{tracurl}{path}?rev={revision}#L{line}".format(**locals()) - else: - return "{tracurl}{path}?rev={revision}".format(**locals()) + return "{tracurl}{path}?rev={revision}".format(**locals()) def html_escape(text): @@ -150,8 +149,7 @@ def to_web_path(path): """ if os.path.sep != "/": return path.replace(os.path.sep, "/") - else: - return path + return path def get_svn_revision(): @@ -174,8 +172,7 @@ def get_svn_revision(): # the first one is the one of source code stdout = stdout.split(":")[0] return stdout - else: - return None + return None def get_svn_info(): @@ -436,9 +433,9 @@ def end_file_test(self, returncode, **kwargs): def percent_to_html(percent): if percent is None: return 'unknown percentage' - elif percent > 100 or percent < 0: + if percent > 100 or percent < 0: return "? {:.2f}% ?".format(percent) - elif percent < 40: + if percent < 40: color = "red" elif percent < 70: color = "orange" @@ -495,9 +492,8 @@ def returncode_to_html_text(returncode, timed_out=None): else: extra = "" return f'FAILED{extra}' - else: - # alternatives: SUCCEEDED, passed, OK - return 'succeeded' + # alternatives: SUCCEEDED, passed, OK + return 'succeeded' # not used @@ -507,31 +503,28 @@ def returncode_to_html_sentence(returncode): '' " Test failed (return code %d)" % (returncode) ) - else: - return ( - '' - " Test succeeded (return code %d)" % (returncode) - ) + return ( + '' + " Test succeeded (return code %d)" % (returncode) + ) def returncode_to_success_html_par(returncode): if returncode: return '

    Test failed

    ' - else: - return '

    Test succeeded

    ' + return '

    Test succeeded

    ' def success_to_html_text(total, successes): if successes < total: return 'FAILED' - elif successes == total: + if successes == total: # alternatives: SUCCEEDED, passed, OK return 'succeeded' - else: - return ( - '' - "? more successes than total ?" - ) + return ( + '' + "? more successes than total ?" + ) UNKNOWN_NUMBER_HTML = 'unknown' @@ -627,8 +620,7 @@ def finish(self): def format_percentage(percentage): if percentage is not None: return "{nsper:.0f}%".format(nsper=percentage) - else: - return "unknown percentage" + return "unknown percentage" summary_sentence = ( "\nExecuted {nfiles} test files in {time:}." @@ -985,8 +977,7 @@ def finish(self): def format_percentage(percentage): if percentage is not None: return "{nsper:.0f}%".format(nsper=percentage) - else: - return "unknown percentage" + return "unknown percentage" summary_sentence = ( "\nExecuted {nfiles} test files in {time:}." diff --git a/python/grass/gunittest/runner.py b/python/grass/gunittest/runner.py index b99c6704c89..0bbd0f361b9 100644 --- a/python/grass/gunittest/runner.py +++ b/python/grass/gunittest/runner.py @@ -83,8 +83,7 @@ def getDescription(self, test): doc_first_line = test.shortDescription() if self.descriptions and doc_first_line: return "\n".join((str(test), doc_first_line)) - else: - return str(test) + return str(test) def startTest(self, test): super().startTest(test) diff --git a/python/grass/imaging/images2avi.py b/python/grass/imaging/images2avi.py index f0b1904823d..1b0d9d8cd20 100644 --- a/python/grass/imaging/images2avi.py +++ b/python/grass/imaging/images2avi.py @@ -137,11 +137,10 @@ def writeAvi( + "\n" + _("Could not write avi.") ) - else: - # An error occurred, show - print(gs.decode(outPut)) - print(gs.decode(S.stderr.read())) - raise RuntimeError(_("Could not write avi.")) + # An error occurred, show + print(gs.decode(outPut)) + print(gs.decode(S.stderr.read())) + raise RuntimeError(_("Could not write avi.")) else: try: # Copy avi @@ -151,8 +150,7 @@ def writeAvi( _cleanDir(tempDir) if bg_task: return str(err) - else: - raise + raise # Clean up _cleanDir(tempDir) diff --git a/python/grass/imaging/images2gif.py b/python/grass/imaging/images2gif.py index fdb6423eec3..4084dade509 100644 --- a/python/grass/imaging/images2gif.py +++ b/python/grass/imaging/images2gif.py @@ -1060,9 +1060,8 @@ def quantize(self, image): """ if get_cKDTree(): return self.quantize_with_scipy(image) - else: - print("Scipy not available, falling back to slower version.") - return self.quantize_without_scipy(image) + print("Scipy not available, falling back to slower version.") + return self.quantize_without_scipy(image) def quantize_with_scipy(self, image): w, h = image.size diff --git a/python/grass/imaging/images2ims.py b/python/grass/imaging/images2ims.py index 662e3eb46e0..a5cee9b49de 100644 --- a/python/grass/imaging/images2ims.py +++ b/python/grass/imaging/images2ims.py @@ -93,8 +93,7 @@ def checkImages(images): def _getFilenameParts(filename): if "*" in filename: return tuple(filename.split("*", 1)) - else: - return os.path.splitext(filename) + return os.path.splitext(filename) def _getFilenameWithFormatter(filename, N): diff --git a/python/grass/pydispatch/robustapply.py b/python/grass/pydispatch/robustapply.py index cd52f74c22e..2ec88443a1a 100644 --- a/python/grass/pydispatch/robustapply.py +++ b/python/grass/pydispatch/robustapply.py @@ -35,7 +35,7 @@ def function(receiver): if hasattr(receiver, im_func): # an instance-method... return receiver, getattr(getattr(receiver, im_func), func_code), 1 - elif not hasattr(receiver, func_code): + if not hasattr(receiver, func_code): raise ValueError("unknown receiver type %s %s" % (receiver, type(receiver))) return receiver, getattr(receiver, func_code), 0 diff --git a/python/grass/pydispatch/saferef.py b/python/grass/pydispatch/saferef.py index a802a1982c1..90769c46b36 100644 --- a/python/grass/pydispatch/saferef.py +++ b/python/grass/pydispatch/saferef.py @@ -35,8 +35,7 @@ def safeRef(target, onDelete=None): return BoundMethodWeakref(target=target, onDelete=onDelete) if onDelete is not None: return weakref.ref(target, onDelete) - else: - return weakref.ref(target) + return weakref.ref(target) class BoundMethodWeakref: @@ -92,11 +91,10 @@ def __new__(cls, target, onDelete=None, *arguments, **named): if current is not None: current.deletionMethods.append(onDelete) return current - else: - base = super().__new__(cls) - cls._allInstances[key] = base - base.__init__(target, onDelete, *arguments, **named) - return base + base = super().__new__(cls) + cls._allInstances[key] = base + base.__init__(target, onDelete, *arguments, **named) + return base def __init__(self, target, onDelete=None): """Return a weak-reference-like instance for a bound method diff --git a/python/grass/pygrass/errors.py b/python/grass/pygrass/errors.py index a63b300a862..a017b85b33f 100644 --- a/python/grass/pygrass/errors.py +++ b/python/grass/pygrass/errors.py @@ -11,9 +11,8 @@ def must_be_open(method): def wrapper(self, *args, **kargs): if self.is_open(): return method(self, *args, **kargs) - else: - msgr = get_msgr() - msgr.warning(_("The map is close!")) + msgr = get_msgr() + msgr.warning(_("The map is close!")) return wrapper @@ -23,10 +22,7 @@ def mapinfo_must_be_set(method): def wrapper(self, *args, **kargs): if self.c_mapinfo: return method(self, *args, **kargs) - else: - raise GrassError( - _("The self.c_mapinfo pointer must be correctly initiated") - ) + raise GrassError(_("The self.c_mapinfo pointer must be correctly initiated")) return wrapper @@ -36,9 +32,6 @@ def must_be_in_current_mapset(method): def wrapper(self, *args, **kargs): if self.mapset == libgis.G_mapset().decode(): return method(self, *args, **kargs) - else: - raise GrassError( - _("Map <{}> not found in current mapset").format(self.name) - ) + raise GrassError(_("Map <{}> not found in current mapset").format(self.name)) return wrapper diff --git a/python/grass/pygrass/gis/__init__.py b/python/grass/pygrass/gis/__init__.py index 61768859231..597c4bf75fa 100644 --- a/python/grass/pygrass/gis/__init__.py +++ b/python/grass/pygrass/gis/__init__.py @@ -162,8 +162,7 @@ def __getitem__(self, location): """ if location in self.locations(): return Location(location, self.name) - else: - raise KeyError("Location: %s does not exist" % location) + raise KeyError("Location: %s does not exist" % location) def __iter__(self): for loc in self.locations(): @@ -234,8 +233,7 @@ def _set_name(self, name): def __getitem__(self, mapset): if mapset in self.mapsets(): return Mapset(mapset) - else: - raise KeyError("Mapset: %s does not exist" % mapset) + raise KeyError("Mapset: %s does not exist" % mapset) def __iter__(self): lpath = self.path() diff --git a/python/grass/pygrass/modules/interface/docstring.py b/python/grass/pygrass/modules/interface/docstring.py index 2fde05f73de..397338fcbb8 100644 --- a/python/grass/pygrass/modules/interface/docstring.py +++ b/python/grass/pygrass/modules/interface/docstring.py @@ -44,8 +44,7 @@ def __init__(self, class_doc, fget): def __get__(self, obj, type=None): if obj is None: return self.class_doc - else: - return self.fget(obj) + return self.fget(obj) def __set__(self, obj, value): raise AttributeError("can't set attribute") diff --git a/python/grass/pygrass/modules/interface/flag.py b/python/grass/pygrass/modules/interface/flag.py index b2e78e30717..b6f00fdb744 100644 --- a/python/grass/pygrass/modules/interface/flag.py +++ b/python/grass/pygrass/modules/interface/flag.py @@ -52,10 +52,8 @@ def get_bash(self): if self.value: if self.special: return "--%s" % self.name[0] - else: - return "-%s" % self.name - else: - return "" + return "-%s" % self.name + return "" def get_python(self): """Return the python representation of a flag. diff --git a/python/grass/pygrass/modules/interface/module.py b/python/grass/pygrass/modules/interface/module.py index c8a1e723a05..392d7571015 100644 --- a/python/grass/pygrass/modules/interface/module.py +++ b/python/grass/pygrass/modules/interface/module.py @@ -720,12 +720,11 @@ def get_python(self): # pre name par flg special if flags and special: return "%s.%s(%s, flags=%r, %s)" % (prefix, name, params, flags, special) - elif flags: + if flags: return "%s.%s(%s, flags=%r)" % (prefix, name, params, flags) - elif special: + if special: return "%s.%s(%s, %s)" % (prefix, name, params, special) - else: - return "%s.%s(%s)" % (prefix, name, params) + return "%s.%s(%s)" % (prefix, name, params) def __str__(self): """Return the command string that can be executed in a shell""" @@ -1026,16 +1025,15 @@ def run(self): module.finish_ = True module.run() return None + if self.set_temp_region is True: + self.p = Process( + target=run_modules_in_temp_region, args=[self.module_list, self.q] + ) else: - if self.set_temp_region is True: - self.p = Process( - target=run_modules_in_temp_region, args=[self.module_list, self.q] - ) - else: - self.p = Process(target=run_modules, args=[self.module_list, self.q]) - self.p.start() + self.p = Process(target=run_modules, args=[self.module_list, self.q]) + self.p.start() - return self.p + return self.p def wait(self): """Wait for all processes to finish. Call this method diff --git a/python/grass/pygrass/raster/__init__.py b/python/grass/pygrass/raster/__init__.py index 0356513e017..61ec85841cb 100644 --- a/python/grass/pygrass/raster/__init__.py +++ b/python/grass/pygrass/raster/__init__.py @@ -327,17 +327,16 @@ def __setitem__(self, key, row): if isinstance(key, slice): # Get the start, stop, and step from the slice return [self.put_row(ii, row) for ii in range(*key.indices(len(self)))] - elif isinstance(key, tuple): + if isinstance(key, tuple): x, y = key return self.put(x, y, row) - elif isinstance(key, int): + if isinstance(key, int): if key < 0: # Handle negative indices key += self._rows if key >= self._rows: raise IndexError(_("Index out of range: %r.") % key) return self.put_row(key, row) - else: - raise TypeError("Invalid argument type.") + raise TypeError("Invalid argument type.") @must_be_open def map2segment(self): diff --git a/python/grass/pygrass/raster/abstract.py b/python/grass/pygrass/raster/abstract.py index edea42e7bdd..68b5e501ed4 100644 --- a/python/grass/pygrass/raster/abstract.py +++ b/python/grass/pygrass/raster/abstract.py @@ -393,10 +393,10 @@ def __getitem__(self, key): if isinstance(key, slice): # Get the start, stop, and step from the slice return (self.get_row(ii) for ii in range(*key.indices(len(self)))) - elif isinstance(key, tuple): + if isinstance(key, tuple): x, y = key return self.get(x, y) - elif isinstance(key, int): + if isinstance(key, int): if not self.is_open(): raise IndexError("Can not operate on a closed map. Call open() first.") if key < 0: # Handle negative indices @@ -408,8 +408,7 @@ def __getitem__(self, key): ) ) return self.get_row(key) - else: - fatal("Invalid argument type.") + fatal("Invalid argument type.") def __iter__(self): """Return a constructor of the class""" @@ -434,8 +433,7 @@ def exist(self): self.mapset = mapset or "" return bool(mapset) return bool(utils.get_mapset_raster(self.name, self.mapset)) - else: - return False + return False def is_open(self): """Return True if the map is open False otherwise. @@ -485,8 +483,7 @@ def name_mapset(self, name=None, mapset=None): if mapset and mapset != gis_env["MAPSET"]: return "{name}@{mapset}".format(name=name, mapset=mapset) - else: - return name + return name def rename(self, newname): """Rename the map""" diff --git a/python/grass/pygrass/raster/buffer.py b/python/grass/pygrass/raster/buffer.py index 51420f16610..0e34ac84159 100644 --- a/python/grass/pygrass/raster/buffer.py +++ b/python/grass/pygrass/raster/buffer.py @@ -20,13 +20,12 @@ class Buffer(np.ndarray): def mtype(self): if self.dtype in CELL: return "CELL" - elif self.dtype in FCELL: + if self.dtype in FCELL: return "FCELL" - elif self.dtype in DCELL: + if self.dtype in DCELL: return DCELL - else: - err = "Raster type: %r not supported by GRASS." - raise TypeError(err % self.dtype) + err = "Raster type: %r not supported by GRASS." + raise TypeError(err % self.dtype) def __new__( cls, shape, mtype="FCELL", buffer=None, offset=0, strides=None, order=None diff --git a/python/grass/pygrass/raster/category.py b/python/grass/pygrass/raster/category.py index f439d51dcdf..fa4e8d37b12 100644 --- a/python/grass/pygrass/raster/category.py +++ b/python/grass/pygrass/raster/category.py @@ -187,7 +187,7 @@ def _set_c_cat(self, label, min_cat, max_cat=None): # Manage C function Errors if err == 1: return None - elif err == 0: + if err == 0: raise GrassError(_("Null value detected")) elif err == -1: raise GrassError(_("Error executing: Rast_set_cat")) diff --git a/python/grass/pygrass/utils.py b/python/grass/pygrass/utils.py index a04e3936a64..d8804665b1e 100644 --- a/python/grass/pygrass/utils.py +++ b/python/grass/pygrass/utils.py @@ -83,24 +83,23 @@ def find_in_gisdbase(type, pattern, gisdbase): (m, mset.name, mset.location, mset.gisdbase) for m in mset.glist(type, pattern) ] - elif gisdbase and location: + if gisdbase and location: loc = Location(location, gisdbase) return find_in_location(type, pattern, loc) - elif gisdbase: + if gisdbase: gis = Gisdbase(gisdbase) return find_in_gisdbase(type, pattern, gis) - elif location: + if location: loc = Location(location) return find_in_location(type, pattern, loc) - elif mapset: + if mapset: mset = Mapset(mapset) return [ (m, mset.name, mset.location, mset.gisdbase) for m in mset.glist(type, pattern) ] - else: - gis = Gisdbase() - return find_in_gisdbase(type, pattern, gis) + gis = Gisdbase() + return find_in_gisdbase(type, pattern, gis) def remove(oldname, maptype): @@ -136,11 +135,10 @@ def decode(obj, encoding=None): """ if isinstance(obj, String): return grassutils.decode(obj.data, encoding=encoding) - elif isinstance(obj, bytes): + if isinstance(obj, bytes): return grassutils.decode(obj) - else: - # eg None - return obj + # eg None + return obj def getenv(env): @@ -337,9 +335,8 @@ def get_raster_for_points(poi_vector, raster, column=None, region=None): result.append((poi.id, poi.x, poi.y, None)) if not column: return result - else: - poi.attrs.commit() - return True + poi.attrs.commit() + return True def r_export(rast, output="", fmt="png", **kargs): @@ -355,8 +352,7 @@ def r_export(rast, output="", fmt="png", **kargs): **kargs, ) return output - else: - raise ValueError("Raster map does not exist.") + raise ValueError("Raster map does not exist.") def get_lib_path(modname, libname=None): diff --git a/python/grass/pygrass/vector/__init__.py b/python/grass/pygrass/vector/__init__.py index b5a24e8d9c1..461050807b0 100644 --- a/python/grass/pygrass/vector/__init__.py +++ b/python/grass/pygrass/vector/__init__.py @@ -70,8 +70,7 @@ def __init__(self, name, mapset="", *args, **kwargs): def __repr__(self): if self.exist(): return "%s(%r, %r)" % (self._class_name, self.name, self.mapset) - else: - return "%s(%r)" % (self._class_name, self.name) + return "%s(%r)" % (self._class_name, self.name) def __iter__(self): """:: @@ -315,10 +314,9 @@ def __getitem__(self, key): key.step or 1, ) ] - elif isinstance(key, int): + if isinstance(key, int): return self.read(key) - else: - raise ValueError("Invalid argument type: %r." % key) + raise ValueError("Invalid argument type: %r." % key) @must_be_open def num_primitive_of(self, primitive): @@ -392,11 +390,9 @@ def number_of(self, vtype): if isinstance(_NUMOF[vtype], tuple): fn, ptype = _NUMOF[vtype] return fn(self.c_mapinfo, ptype) - else: - return _NUMOF[vtype](self.c_mapinfo) - else: - keys = "', '".join(sorted(_NUMOF.keys())) - raise ValueError("vtype not supported, use one of: '%s'" % keys) + return _NUMOF[vtype](self.c_mapinfo) + keys = "', '".join(sorted(_NUMOF.keys())) + raise ValueError("vtype not supported, use one of: '%s'" % keys) @must_be_open def num_primitives(self): @@ -526,17 +522,16 @@ def cat(self, cat_id, vtype, layer=None, generator=False, geo=None): ) for v_id in ilist ) - else: - return [ - read_line( - feature_id=v_id, - c_mapinfo=self.c_mapinfo, - table=self.table, - writeable=self.writeable, - is2D=is2D, - ) - for v_id in ilist - ] + return [ + read_line( + feature_id=v_id, + c_mapinfo=self.c_mapinfo, + table=self.table, + writeable=self.writeable, + is2D=is2D, + ) + for v_id in ilist + ] @must_be_open def read(self, feature_id): diff --git a/python/grass/pygrass/vector/abstract.py b/python/grass/pygrass/vector/abstract.py index 3929006ef25..6700dca8e4c 100644 --- a/python/grass/pygrass/vector/abstract.py +++ b/python/grass/pygrass/vector/abstract.py @@ -299,8 +299,7 @@ def exist(self): self.mapset = mapset or "" return bool(mapset) return bool(utils.get_mapset_vector(self.name, self.mapset)) - else: - return False + return False def is_open(self): """Return if the Vector is open""" diff --git a/python/grass/pygrass/vector/basic.py b/python/grass/pygrass/vector/basic.py index b86ae3edb4e..f031e23ff98 100644 --- a/python/grass/pygrass/vector/basic.py +++ b/python/grass/pygrass/vector/basic.py @@ -149,8 +149,7 @@ def nsewtb(self, tb=True): """ if tb: return (self.north, self.south, self.east, self.west, self.top, self.bottom) - else: - return (self.north, self.south, self.east, self.west) + return (self.north, self.south, self.east, self.west) class BoxList: @@ -308,14 +307,13 @@ def __getitem__(self, key): self.c_ilist.contents.value[indx] for indx in range(*key.indices(len(self))) ] - elif isinstance(key, int): + if isinstance(key, int): if key < 0: # Handle negative indices key += self.c_ilist.contents.n_values if key >= self.c_ilist.contents.n_values: raise IndexError("Index out of range") return self.c_ilist.contents.value[key] - else: - raise ValueError("Invalid argument type: %r." % key) + raise ValueError("Invalid argument type: %r." % key) def __setitem__(self, key, value): if self.contains(value): diff --git a/python/grass/pygrass/vector/find.py b/python/grass/pygrass/vector/find.py index 9333497f110..25ce9aca8f4 100644 --- a/python/grass/pygrass/vector/find.py +++ b/python/grass/pygrass/vector/find.py @@ -490,11 +490,10 @@ def geos(self, bbox, type="all", bboxlist_only=False): ): if bboxlist_only: return found - else: - return ( - read_line(f_id, self.c_mapinfo, self.table, self.writeable) - for f_id in found.ids - ) + return ( + read_line(f_id, self.c_mapinfo, self.table, self.writeable) + for f_id in found.ids + ) @must_be_open def nodes(self, bbox): @@ -592,16 +591,15 @@ def areas(self, bbox, boxlist=None, bboxlist_only=False): ): if bboxlist_only: return boxlist - else: - return ( - Area( - v_id=a_id, - c_mapinfo=self.c_mapinfo, - table=self.table, - writeable=self.writeable, - ) - for a_id in boxlist.ids + return ( + Area( + v_id=a_id, + c_mapinfo=self.c_mapinfo, + table=self.table, + writeable=self.writeable, ) + for a_id in boxlist.ids + ) @must_be_open def islands(self, bbox, bboxlist_only=False): @@ -652,16 +650,15 @@ def islands(self, bbox, bboxlist_only=False): ): if bboxlist_only: return found - else: - return ( - Isle( - v_id=i_id, - c_mapinfo=self.c_mapinfo, - table=self.table, - writeable=self.writeable, - ) - for i_id in found.ids + return ( + Isle( + v_id=i_id, + c_mapinfo=self.c_mapinfo, + table=self.table, + writeable=self.writeable, ) + for i_id in found.ids + ) class PolygonFinder(AbstractFinder): diff --git a/python/grass/pygrass/vector/geometry.py b/python/grass/pygrass/vector/geometry.py index 2ff4cb1bb8f..10ecb1d2d13 100644 --- a/python/grass/pygrass/vector/geometry.py +++ b/python/grass/pygrass/vector/geometry.py @@ -94,8 +94,7 @@ def intersects(lineA, lineB, with_z=False): lineA.c_points, lineB.c_points, line.c_points, int(with_z) ): return line - else: - return [] + return [] # ============================================= @@ -370,8 +369,7 @@ def cat(self): def has_topology(self): if self.c_mapinfo is not None: return self.c_mapinfo.contents.level == 2 - else: - return False + return False @mapinfo_must_be_set def read(self): @@ -539,8 +537,7 @@ def coords(self): """ if self.is2D: return self.x, self.y - else: - return self.x, self.y, self.z + return self.x, self.y, self.z def to_wkt_p(self): """Return a "well know text" (WKT) geometry string Python implementation. :: @@ -577,10 +574,9 @@ def distance(self, pnt): """ if self.is2D or pnt.is2D: return libvect.Vect_points_distance(self.x, self.y, 0, pnt.x, pnt.y, 0, 0) - else: - return libvect.Vect_points_distance( - self.x, self.y, self.z, pnt.x, pnt.y, pnt.z, 1 - ) + return libvect.Vect_points_distance( + self.x, self.y, self.z, pnt.x, pnt.y, pnt.z, 1 + ) def buffer( self, dist=None, dist_x=None, dist_y=None, angle=0, round_=True, tol=0.1 @@ -675,7 +671,7 @@ def __getitem__(self, key): ) for indx in range(*key.indices(len(self))) ] - elif isinstance(key, int): + if isinstance(key, int): if key < 0: # Handle negative indices key += self.c_points.contents.n_points if key >= self.c_points.contents.n_points: @@ -685,8 +681,7 @@ def __getitem__(self, key): self.c_points.contents.y[key], None if self.is2D else self.c_points.contents.z[key], ) - else: - raise ValueError("Invalid argument type: %r." % key) + raise ValueError("Invalid argument type: %r." % key) def __setitem__(self, indx, pnt): """Change the coordinate of point. :: @@ -1373,8 +1368,7 @@ def _centroid(self, side, idonly=False): v_id = v_id or None if idonly: return v_id - else: - return Centroid(v_id=v_id, c_mapinfo=self.c_mapinfo) + return Centroid(v_id=v_id, c_mapinfo=self.c_mapinfo) def left_centroid(self, idonly=False): """Return left centroid @@ -1914,8 +1908,7 @@ def c_read_line(feature_id, c_mapinfo, c_points, c_cats): if feature_id > 0: ftype = libvect.Vect_read_line(c_mapinfo, c_points, c_cats, feature_id) return feature_id, ftype, c_points, c_cats - else: - raise ValueError("The index must be >0, %r given." % feature_id) + raise ValueError("The index must be >0, %r given." % feature_id) def read_line( diff --git a/python/grass/pygrass/vector/table.py b/python/grass/pygrass/vector/table.py index 78c1c8709b4..9c8d0c3f9a2 100644 --- a/python/grass/pygrass/vector/table.py +++ b/python/grass/pygrass/vector/table.py @@ -65,14 +65,13 @@ def get_path(path, vect_name=None): """ if "$" not in path: return path - else: - mapset = Mapset() - path = path.replace("$GISDBASE", mapset.gisdbase) - path = path.replace("$LOCATION_NAME", mapset.location) - path = path.replace("$MAPSET", mapset.name) - if vect_name is not None: - path = path.replace("$MAP", vect_name) - return path + mapset = Mapset() + path = path.replace("$GISDBASE", mapset.gisdbase) + path = path.replace("$LOCATION_NAME", mapset.location) + path = path.replace("$MAPSET", mapset.name) + if vect_name is not None: + path = path.replace("$MAP", vect_name) + return path class Filters: @@ -326,8 +325,7 @@ def sql_descr(self, remove=None): return ", ".join( ["%s %s" % (key, val) for key, val in self.items() if key != remove] ) - else: - return ", ".join(["%s %s" % (key, val) for key, val in self.items()]) + return ", ".join(["%s %s" % (key, val) for key, val in self.items()]) def types(self): """Return a list with the column types. @@ -372,8 +370,7 @@ def names(self, remove=None, unicod=True): nams = list(self.odict.keys()) if unicod: return nams - else: - return [str(name) for name in nams] + return [str(name) for name in nams] def items(self): """Return a list of tuple with column name and column type. @@ -847,7 +844,7 @@ def connection(self): if not os.path.exists(dbdirpath): os.mkdir(dbdirpath) return sqlite3.connect(dbpath) - elif driver == "pg": + if driver == "pg": try: import psycopg2 @@ -940,8 +937,7 @@ def __iter__(self): def __getitem__(self, item): if isinstance(item, int): return self.by_index(item) - else: - return self.by_name(item) + return self.by_name(item) def __repr__(self): return "DBlinks(%r)" % list(self.__iter__()) diff --git a/python/grass/script/core.py b/python/grass/script/core.py index ee90a41d4ef..4f70f525133 100644 --- a/python/grass/script/core.py +++ b/python/grass/script/core.py @@ -127,8 +127,7 @@ def _make_unicode(val, enc): if enc == "default": return decode(val) - else: - return decode(val, encoding=enc) + return decode(val, encoding=enc) def get_commands(*, env=None): @@ -349,7 +348,7 @@ def get_module_and_code(args, kwargs): return result if handler.lower() == "ignore": return result - elif handler.lower() == "fatal": + if handler.lower() == "fatal": module, code = get_module_and_code(args, kwargs) fatal( _( @@ -1650,8 +1649,7 @@ def verbosity(): vbstr = os.getenv("GRASS_VERBOSE") if vbstr: return int(vbstr) - else: - return 2 + return 2 # Various utilities, not specific to GRASS diff --git a/python/grass/script/db.py b/python/grass/script/db.py index 2725ff06ebe..60813379501 100644 --- a/python/grass/script/db.py +++ b/python/grass/script/db.py @@ -229,8 +229,7 @@ def db_table_in_vector(table, mapset=".", env=None): break if len(used) > 0: return used - else: - return None + return None def db_begin_transaction(driver): diff --git a/python/grass/script/raster.py b/python/grass/script/raster.py index 32c0639b32f..bcaab7ef596 100644 --- a/python/grass/script/raster.py +++ b/python/grass/script/raster.py @@ -88,8 +88,7 @@ def raster_info(map, env=None): def float_or_null(s): if s == "NULL": return None - else: - return float(s) + return float(s) s = read_command("r.info", flags="gre", map=map, env=env) kv = parse_key_val(s) diff --git a/python/grass/script/raster3d.py b/python/grass/script/raster3d.py index 3b89b4fb707..e3db5398158 100644 --- a/python/grass/script/raster3d.py +++ b/python/grass/script/raster3d.py @@ -47,8 +47,7 @@ def raster3d_info(map, env=None): def float_or_null(s): if s == "NULL": return None - else: - return float(s) + return float(s) s = read_command("r3.info", flags="rg", map=map, env=env) kv = parse_key_val(s) diff --git a/python/grass/script/task.py b/python/grass/script/task.py index 8e1f0a12844..cf398c59214 100644 --- a/python/grass/script/task.py +++ b/python/grass/script/task.py @@ -90,8 +90,7 @@ def get_name(self): name, ext = os.path.splitext(self.name) if ext in {".py", ".sh"}: return name - else: - return self.name + return self.name return self.name @@ -103,10 +102,8 @@ def get_description(self, full=True): if self.label: if full: return self.label + " " + self.description - else: - return self.label - else: - return self.description + return self.label + return self.description def get_keywords(self): """Get module's keywords""" diff --git a/python/grass/script/utils.py b/python/grass/script/utils.py index aa7d53375dd..448289b57aa 100644 --- a/python/grass/script/utils.py +++ b/python/grass/script/utils.py @@ -69,13 +69,13 @@ def separator(sep): """ if sep == "pipe": return "|" - elif sep == "comma": + if sep == "comma": return "," - elif sep == "space": + if sep == "space": return " " - elif sep in {"tab", "\\t"}: + if sep in {"tab", "\\t"}: return "\t" - elif sep in {"newline", "\\n"}: + if sep in {"newline", "\\n"}: return "\n" return sep diff --git a/python/grass/temporal/abstract_dataset.py b/python/grass/temporal/abstract_dataset.py index cfe3c83a841..bf30afa21dd 100644 --- a/python/grass/temporal/abstract_dataset.py +++ b/python/grass/temporal/abstract_dataset.py @@ -76,7 +76,7 @@ def get_number_of_relations(self): """ if self.is_temporal_topology_build() and not self.is_spatial_topology_build(): return self.get_number_of_temporal_relations() - elif self.is_spatial_topology_build() and not self.is_temporal_topology_build(): + if self.is_spatial_topology_build() and not self.is_temporal_topology_build(): self.get_number_of_spatial_relations() else: return ( @@ -511,8 +511,7 @@ def is_time_absolute(self): """ if "temporal_type" in self.base.D: return self.base.get_ttype() == "absolute" - else: - return None + return None def is_time_relative(self): """Return True in case the temporal type is relative @@ -521,8 +520,7 @@ def is_time_relative(self): """ if "temporal_type" in self.base.D: return self.base.get_ttype() == "relative" - else: - return None + return None def get_temporal_extent(self): """Return the temporal extent of the correct internal type""" diff --git a/python/grass/temporal/abstract_map_dataset.py b/python/grass/temporal/abstract_map_dataset.py index f58bcb82d66..ecbf761d139 100644 --- a/python/grass/temporal/abstract_map_dataset.py +++ b/python/grass/temporal/abstract_map_dataset.py @@ -232,8 +232,7 @@ def build_id_from_search_path(name, element): if layer is not None: return f"{name}:{layer}@{mapset}" - else: - return f"{name}@{mapset}" + return f"{name}@{mapset}" @staticmethod def build_id(name, mapset, layer=None): @@ -258,8 +257,7 @@ def build_id(name, mapset, layer=None): if layer is not None: return f"{name}:{layer}@{mapset}" - else: - return f"{name}@{mapset}" + return f"{name}@{mapset}" def get_layer(self): """Return the layer of the map @@ -443,15 +441,11 @@ def set_absolute_time(self, start_time, end_time=None): } ) return False - else: - self.msgr.error( - _( - "Start time must be of type datetime for " - "%(type)s map <%(id)s>" - ) - % {"type": self.get_type(), "id": self.get_map_id()} - ) - return False + self.msgr.error( + _("Start time must be of type datetime for %(type)s map <%(id)s>") + % {"type": self.get_type(), "id": self.get_map_id()} + ) + return False if end_time and not isinstance(end_time, datetime): if self.get_layer(): @@ -467,12 +461,11 @@ def set_absolute_time(self, start_time, end_time=None): } ) return False - else: - self.msgr.error( - _("End time must be of type datetime for %(type)s map <%(id)s>") - % {"type": self.get_type(), "id": self.get_map_id()} - ) - return False + self.msgr.error( + _("End time must be of type datetime for %(type)s map <%(id)s>") + % {"type": self.get_type(), "id": self.get_map_id()} + ) + return False if start_time is not None and end_time is not None: if start_time > end_time: @@ -490,17 +483,16 @@ def set_absolute_time(self, start_time, end_time=None): } ) return False - else: - self.msgr.error( - _( - "End time must be greater than start " - "time for %(type)s map <%(id)s>" - ) - % {"type": self.get_type(), "id": self.get_map_id()} + self.msgr.error( + _( + "End time must be greater than start " + "time for %(type)s map <%(id)s>" ) - return False + % {"type": self.get_type(), "id": self.get_map_id()} + ) + return False # Do not create an interval in case start and end time are equal - elif start_time == end_time: + if start_time == end_time: end_time = None self.base.set_ttype("absolute") @@ -618,7 +610,7 @@ def set_relative_time(self, start_time, end_time, unit): ) return False # Do not create an interval in case start and end time are equal - elif start_time == end_time: + if start_time == end_time: end_time = None self.base.set_ttype("relative") diff --git a/python/grass/temporal/abstract_space_time_dataset.py b/python/grass/temporal/abstract_space_time_dataset.py index fc154270f59..d17baaab6ae 100644 --- a/python/grass/temporal/abstract_space_time_dataset.py +++ b/python/grass/temporal/abstract_space_time_dataset.py @@ -1626,8 +1626,7 @@ def leading_zero(value): try: if value.startswith("0"): return value.lstrip("0") - else: - return "{0:02d}".format(int(value)) + return "{0:02d}".format(int(value)) except ValueError: return None diff --git a/python/grass/temporal/base.py b/python/grass/temporal/base.py index 9296a22dfa1..ebb4b715e29 100644 --- a/python/grass/temporal/base.py +++ b/python/grass/temporal/base.py @@ -466,10 +466,9 @@ def get_update_statement(self, ident=None): return self.serialize( "UPDATE", self.get_table_name(), "WHERE id = '" + str(ident) + "'" ) - else: - return self.serialize( - "UPDATE", self.get_table_name(), "WHERE id = '" + str(self.ident) + "'" - ) + return self.serialize( + "UPDATE", self.get_table_name(), "WHERE id = '" + str(self.ident) + "'" + ) def get_update_statement_mogrified(self, dbif=None, ident=None): """Return the update statement as mogrified string @@ -529,12 +528,11 @@ def get_update_all_statement(self, ident=None): return self.serialize( "UPDATE ALL", self.get_table_name(), "WHERE id = '" + str(ident) + "'" ) - else: - return self.serialize( - "UPDATE ALL", - self.get_table_name(), - "WHERE id = '" + str(self.ident) + "'", - ) + return self.serialize( + "UPDATE ALL", + self.get_table_name(), + "WHERE id = '" + str(self.ident) + "'", + ) def get_update_all_statement_mogrified(self, dbif=None, ident=None): """Return the update all statement as mogrified string @@ -749,8 +747,7 @@ def get_id(self): """ if "id" in self.D: return self.D["id"] - else: - return None + return None def get_map_id(self): """Convenient method to get the unique map identifier @@ -763,10 +760,8 @@ def get_map_id(self): if self.id.find(":") >= 0: # Remove the layer identifier from the id return self.id.split("@")[0].split(":")[0] + "@" + self.id.split("@")[1] - else: - return self.id - else: - return None + return self.id + return None def get_layer(self): """Convenient method to get the layer of the map (part of primary key) @@ -777,48 +772,42 @@ def get_layer(self): """ if "layer" in self.D: return self.D["layer"] - else: - return None + return None def get_name(self): """Get the name of the dataset :return: None if not found""" if "name" in self.D: return self.D["name"] - else: - return None + return None def get_mapset(self): """Get the name of mapset of this dataset :return: None if not found""" if "mapset" in self.D: return self.D["mapset"] - else: - return None + return None def get_creator(self): """Get the creator of the dataset :return: None if not found""" if "creator" in self.D: return self.D["creator"] - else: - return None + return None def get_ctime(self): """Get the creation time of the dataset, datatype is datetime :return: None if not found""" if "creation_time" in self.D: return self.D["creation_time"] - else: - return None + return None def get_ttype(self): """Get the temporal type of the map :return: None if not found""" if "temporal_type" in self.D: return self.D["temporal_type"] - else: - return None + return None # Properties of this class id = property(fget=get_id, fset=set_id) @@ -1027,8 +1016,7 @@ def get_semantic_type(self): """ if "semantic_type" in self.D: return self.D["semantic_type"] - else: - return None + return None def get_mtime(self): """Get the modification time of the space time dataset, datatype is @@ -1038,8 +1026,7 @@ def get_mtime(self): """ if "modification_time" in self.D: return self.D["modification_time"] - else: - return None + return None semantic_type = property(fget=get_semantic_type, fset=set_semantic_type) @@ -1201,8 +1188,7 @@ def get_id(self): """ if "id" in self.D: return self.D["id"] - else: - return None + return None def get_registered_stds(self): """Get the comma separated list of space time datasets ids @@ -1212,8 +1198,7 @@ def get_registered_stds(self): """ if "registered_stds" in self.D: return self.D["registered_stds"] - else: - return None + return None # Properties of this class id = property(fget=get_id, fset=set_id) diff --git a/python/grass/temporal/c_libraries_interface.py b/python/grass/temporal/c_libraries_interface.py index 27e0ff62238..85a19a3ec6e 100644 --- a/python/grass/temporal/c_libraries_interface.py +++ b/python/grass/temporal/c_libraries_interface.py @@ -1011,13 +1011,12 @@ def _read_raster_history(name, mapset): if ret < 0: logging.warning(_("Unable to read history file")) return None - else: - kvp["creation_time"] = decode( - libraster.Rast_get_history(byref(hist), libraster.HIST_MAPID) - ) - kvp["creator"] = decode( - libraster.Rast_get_history(byref(hist), libraster.HIST_CREATOR) - ) + kvp["creation_time"] = decode( + libraster.Rast_get_history(byref(hist), libraster.HIST_MAPID) + ) + kvp["creator"] = decode( + libraster.Rast_get_history(byref(hist), libraster.HIST_CREATOR) + ) return kvp @@ -1049,13 +1048,12 @@ def _read_raster3d_history(name, mapset): if ret < 0: logging.warning(_("Unable to read history file")) return None - else: - kvp["creation_time"] = decode( - libraster.Rast_get_history(byref(hist), libraster3d.HIST_MAPID) - ) - kvp["creator"] = decode( - libraster.Rast_get_history(byref(hist), libraster3d.HIST_CREATOR) - ) + kvp["creation_time"] = decode( + libraster.Rast_get_history(byref(hist), libraster3d.HIST_MAPID) + ) + kvp["creator"] = decode( + libraster.Rast_get_history(byref(hist), libraster3d.HIST_CREATOR) + ) return kvp @@ -1142,43 +1140,42 @@ def _convert_timestamp_from_grass(ts): # ATTENTION: We ignore the time zone # TODO: Write time zone support return (pdt1, pdt2) - else: - unit = None - start = None - end = None - if count.value >= 1: - if dt1.year > 0: - unit = "years" - start = dt1.year - elif dt1.month > 0: - unit = "months" - start = dt1.month - elif dt1.day > 0: - unit = "days" - start = dt1.day - elif dt1.hour > 0: - unit = "hours" - start = dt1.hour - elif dt1.minute > 0: - unit = "minutes" - start = dt1.minute - elif dt1.second > 0: - unit = "seconds" - start = dt1.second - if count.value == 2: - if dt2.year > 0: - end = dt2.year - elif dt2.month > 0: - end = dt2.month - elif dt2.day > 0: - end = dt2.day - elif dt2.hour > 0: - end = dt2.hour - elif dt2.minute > 0: - end = dt2.minute - elif dt2.second > 0: - end = dt2.second - return (start, end, unit) + unit = None + start = None + end = None + if count.value >= 1: + if dt1.year > 0: + unit = "years" + start = dt1.year + elif dt1.month > 0: + unit = "months" + start = dt1.month + elif dt1.day > 0: + unit = "days" + start = dt1.day + elif dt1.hour > 0: + unit = "hours" + start = dt1.hour + elif dt1.minute > 0: + unit = "minutes" + start = dt1.minute + elif dt1.second > 0: + unit = "seconds" + start = dt1.second + if count.value == 2: + if dt2.year > 0: + end = dt2.year + elif dt2.month > 0: + end = dt2.month + elif dt2.day > 0: + end = dt2.day + elif dt2.hour > 0: + end = dt2.hour + elif dt2.minute > 0: + end = dt2.minute + elif dt2.second > 0: + end = dt2.second + return (start, end, unit) ############################################################################### diff --git a/python/grass/temporal/core.py b/python/grass/temporal/core.py index 0448ff2371d..321c73d5408 100644 --- a/python/grass/temporal/core.py +++ b/python/grass/temporal/core.py @@ -1376,7 +1376,7 @@ def mogrify_sql_statement(self, content): if self.dbmi.__name__ == "psycopg2": if len(args) == 0: return sql - elif self.connected: + if self.connected: try: return self.cursor.mogrify(sql, args) except Exception as exc: @@ -1391,57 +1391,56 @@ def mogrify_sql_statement(self, content): elif self.dbmi.__name__ == "sqlite3": if len(args) == 0: return sql - else: - # Unfortunately as sqlite does not support - # the transformation of sql strings and qmarked or - # named arguments we must make our hands dirty - # and do it by ourself. :( - # Doors are open for SQL injection because of the - # limited python sqlite3 implementation!!! - pos = 0 - count = 0 - maxcount = 100 - statement = sql - - while count < maxcount: - pos = statement.find("?", pos + 1) - if pos == -1: - break - - if args[count] is None: - statement = "%sNULL%s" % ( - statement[0:pos], - statement[pos + 1 :], - ) - elif isinstance(args[count], int): - statement = "%s%d%s" % ( - statement[0:pos], - args[count], - statement[pos + 1 :], - ) - elif isinstance(args[count], float): - statement = "%s%f%s" % ( - statement[0:pos], - args[count], - statement[pos + 1 :], - ) - elif isinstance(args[count], datetime): - statement = "%s'%s'%s" % ( - statement[0:pos], - str(args[count]), - statement[pos + 1 :], - ) - else: - # Default is a string, this works for datetime - # objects too - statement = "%s'%s'%s" % ( - statement[0:pos], - str(args[count]), - statement[pos + 1 :], - ) - count += 1 + # Unfortunately as sqlite does not support + # the transformation of sql strings and qmarked or + # named arguments we must make our hands dirty + # and do it by ourself. :( + # Doors are open for SQL injection because of the + # limited python sqlite3 implementation!!! + pos = 0 + count = 0 + maxcount = 100 + statement = sql + + while count < maxcount: + pos = statement.find("?", pos + 1) + if pos == -1: + break + + if args[count] is None: + statement = "%sNULL%s" % ( + statement[0:pos], + statement[pos + 1 :], + ) + elif isinstance(args[count], int): + statement = "%s%d%s" % ( + statement[0:pos], + args[count], + statement[pos + 1 :], + ) + elif isinstance(args[count], float): + statement = "%s%f%s" % ( + statement[0:pos], + args[count], + statement[pos + 1 :], + ) + elif isinstance(args[count], datetime): + statement = "%s'%s'%s" % ( + statement[0:pos], + str(args[count]), + statement[pos + 1 :], + ) + else: + # Default is a string, this works for datetime + # objects too + statement = "%s'%s'%s" % ( + statement[0:pos], + str(args[count]), + statement[pos + 1 :], + ) + count += 1 - return statement + return statement def check_table(self, table_name): """Check if a table exists in the temporal database diff --git a/python/grass/temporal/metadata.py b/python/grass/temporal/metadata.py index 369fadc03cf..4be4c568a76 100644 --- a/python/grass/temporal/metadata.py +++ b/python/grass/temporal/metadata.py @@ -174,72 +174,63 @@ def get_id(self): """ if "id" in self.D: return self.D["id"] - else: - return None + return None def get_datatype(self): """Get the map type :return: None if not found""" if "datatype" in self.D: return self.D["datatype"] - else: - return None + return None def get_cols(self): """Get number of cols :return: None if not found""" if "cols" in self.D: return self.D["cols"] - else: - return None + return None def get_rows(self): """Get number of rows :return: None if not found""" if "rows" in self.D: return self.D["rows"] - else: - return None + return None def get_number_of_cells(self): """Get number of cells :return: None if not found""" if "number_of_cells" in self.D: return self.D["number_of_cells"] - else: - return None + return None def get_nsres(self): """Get the north-south resolution :return: None if not found""" if "nsres" in self.D: return self.D["nsres"] - else: - return None + return None def get_ewres(self): """Get east-west resolution :return: None if not found""" if "ewres" in self.D: return self.D["ewres"] - else: - return None + return None def get_min(self): """Get the minimum cell value :return: None if not found""" if "min" in self.D: return self.D["min"] - else: - return None + return None def get_max(self): """Get the maximum cell value :return: None if not found""" if "max" in self.D: return self.D["max"] - else: - return None + return None # Properties datatype = property(fget=get_datatype, fset=set_datatype) @@ -401,8 +392,7 @@ def get_semantic_label(self): :return: None if not found""" if "semantic_label" in self.D: return self.D["semantic_label"] - else: - return None + return None semantic_label = property(fget=get_semantic_label, fset=set_semantic_label) @@ -549,16 +539,14 @@ def get_depths(self): :return: None if not found""" if "depths" in self.D: return self.D["depths"] - else: - return None + return None def get_tbres(self): """Get top-bottom resolution :return: None if not found""" if "tbres" in self.D: return self.D["tbres"] - else: - return None + return None depths = property(fget=get_depths, fset=set_depths) tbres = property(fget=get_tbres, fset=set_tbres) @@ -766,112 +754,98 @@ def get_id(self): """ if "id" in self.D: return self.D["id"] - else: - return None + return None def get_3d_info(self): """Return True if the map is three dimensional, False if not and None if not info was found""" if "is_3d" in self.D: return self.D["is_3d"] - else: - return None + return None def get_number_of_points(self): """Get the number of points of the vector map :return: None if not found""" if "points" in self.D: return self.D["points"] - else: - return None + return None def get_number_of_lines(self): """Get the number of lines of the vector map :return: None if not found""" if "lines" in self.D: return self.D["lines"] - else: - return None + return None def get_number_of_boundaries(self): """Get the number of boundaries of the vector map :return: None if not found""" if "boundaries" in self.D: return self.D["boundaries"] - else: - return None + return None def get_number_of_centroids(self): """Get the number of centroids of the vector map :return: None if not found""" if "centroids" in self.D: return self.D["centroids"] - else: - return None + return None def get_number_of_faces(self): """Get the number of faces of the vector map :return: None if not found""" if "faces" in self.D: return self.D["faces"] - else: - return None + return None def get_number_of_kernels(self): """Get the number of kernels of the vector map :return: None if not found""" if "kernels" in self.D: return self.D["kernels"] - else: - return None + return None def get_number_of_primitives(self): """Get the number of primitives of the vector map :return: None if not found""" if "primitives" in self.D: return self.D["primitives"] - else: - return None + return None def get_number_of_nodes(self): """Get the number of nodes of the vector map :return: None if not found""" if "nodes" in self.D: return self.D["nodes"] - else: - return None + return None def get_number_of_areas(self): """Get the number of areas of the vector map :return: None if not found""" if "areas" in self.D: return self.D["areas"] - else: - return None + return None def get_number_of_islands(self): """Get the number of islands of the vector map :return: None if not found""" if "islands" in self.D: return self.D["islands"] - else: - return None + return None def get_number_of_holes(self): """Get the number of holes of the vector map :return: None if not found""" if "holes" in self.D: return self.D["holes"] - else: - return None + return None def get_number_of_volumes(self): """Get the number of volumes of the vector map :return: None if not found""" if "volumes" in self.D: return self.D["volumes"] - else: - return None + return None # Set the properties id = property(fget=get_id, fset=set_id) @@ -1002,32 +976,28 @@ def get_id(self): """ if "id" in self.D: return self.D["id"] - else: - return None + return None def get_title(self): """Get the title :return: None if not found""" if "title" in self.D: return self.D["title"] - else: - return None + return None def get_description(self): """Get description :return: None if not found""" if "description" in self.D: return self.D["description"] - else: - return None + return None def get_command(self): """Get command :return: None if not found""" if "command" in self.D: return self.D["command"] - else: - return None + return None def get_number_of_maps(self): """Get the number of registered maps, @@ -1036,8 +1006,7 @@ def get_number_of_maps(self): :return: None if not found""" if "number_of_maps" in self.D: return self.D["number_of_maps"] - else: - return None + return None id = property(fget=get_id, fset=set_id) title = property(fget=get_title, fset=set_title) @@ -1220,8 +1189,7 @@ def get_aggregation_type(self): """ if "aggregation_type" in self.D: return self.D["aggregation_type"] - else: - return None + return None def get_max_min(self): """Get the minimal maximum of all registered maps, @@ -1230,8 +1198,7 @@ def get_max_min(self): :return: None if not found""" if "max_min" in self.D: return self.D["max_min"] - else: - return None + return None def get_min_min(self): """Get the minimal minimum of all registered maps, @@ -1240,8 +1207,7 @@ def get_min_min(self): :return: None if not found""" if "min_min" in self.D: return self.D["min_min"] - else: - return None + return None def get_max_max(self): """Get the maximal maximum of all registered maps, @@ -1250,8 +1216,7 @@ def get_max_max(self): :return: None if not found""" if "max_max" in self.D: return self.D["max_max"] - else: - return None + return None def get_min_max(self): """Get the maximal minimum of all registered maps, @@ -1260,8 +1225,7 @@ def get_min_max(self): :return: None if not found""" if "min_max" in self.D: return self.D["min_max"] - else: - return None + return None def get_nsres_min(self): """Get the minimal north-south resolution of all registered maps, @@ -1270,8 +1234,7 @@ def get_nsres_min(self): :return: None if not found""" if "nsres_min" in self.D: return self.D["nsres_min"] - else: - return None + return None def get_nsres_max(self): """Get the maximal north-south resolution of all registered maps, @@ -1280,8 +1243,7 @@ def get_nsres_max(self): :return: None if not found""" if "nsres_max" in self.D: return self.D["nsres_max"] - else: - return None + return None def get_ewres_min(self): """Get the minimal east-west resolution of all registered maps, @@ -1290,8 +1252,7 @@ def get_ewres_min(self): :return: None if not found""" if "ewres_min" in self.D: return self.D["ewres_min"] - else: - return None + return None def get_ewres_max(self): """Get the maximal east-west resolution of all registered maps, @@ -1300,8 +1261,7 @@ def get_ewres_max(self): :return: None if not found""" if "ewres_max" in self.D: return self.D["ewres_max"] - else: - return None + return None nsres_min = property(fget=get_nsres_min) nsres_max = property(fget=get_nsres_max) @@ -1435,8 +1395,7 @@ def get_raster_register(self): :return: None if not found""" if "raster_register" in self.D: return self.D["raster_register"] - else: - return None + return None def get_number_of_semantic_labels(self): """Get the number of registered semantic labels @@ -1444,8 +1403,7 @@ def get_number_of_semantic_labels(self): """ if "number_of_semantic_labels" in self.D: return self.D["number_of_semantic_labels"] - else: - return None + return None def get_semantic_labels(self): """Get the distinct semantic labels of registered maps @@ -1483,10 +1441,8 @@ def get_semantic_labels(self): if count > 0: return string - else: - return None - else: return None + return None raster_register = property(fget=get_raster_register, fset=set_raster_register) number_of_semantic_labels = property(fget=get_number_of_semantic_labels) @@ -1623,8 +1579,7 @@ def get_raster3d_register(self): :return: None if not found""" if "raster3d_register" in self.D: return self.D["raster3d_register"] - else: - return None + return None def get_tbres_min(self): """Get the minimal top-bottom resolution of all registered maps, @@ -1633,8 +1588,7 @@ def get_tbres_min(self): :return: None if not found""" if "tbres_min" in self.D: return self.D["tbres_min"] - else: - return None + return None def get_tbres_max(self): """Get the maximal top-bottom resolution of all registered maps, @@ -1643,8 +1597,7 @@ def get_tbres_max(self): :return: None if not found""" if "tbres_max" in self.D: return self.D["tbres_max"] - else: - return None + return None raster3d_register = property(fget=get_raster3d_register, fset=set_raster3d_register) tbres_min = property(fget=get_tbres_min) @@ -1787,8 +1740,7 @@ def get_vector_register(self): :return: None if not found""" if "vector_register" in self.D: return self.D["vector_register"] - else: - return None + return None def get_number_of_points(self): """Get the number of points of all registered maps, @@ -1797,8 +1749,7 @@ def get_number_of_points(self): :return: None if not found""" if "points" in self.D: return self.D["points"] - else: - return None + return None def get_number_of_lines(self): """Get the number of lines of all registered maps, @@ -1807,8 +1758,7 @@ def get_number_of_lines(self): :return: None if not found""" if "lines" in self.D: return self.D["lines"] - else: - return None + return None def get_number_of_boundaries(self): """Get the number of boundaries of all registered maps, @@ -1817,8 +1767,7 @@ def get_number_of_boundaries(self): :return: None if not found""" if "boundaries" in self.D: return self.D["boundaries"] - else: - return None + return None def get_number_of_centroids(self): """Get the number of centroids of all registered maps, @@ -1827,8 +1776,7 @@ def get_number_of_centroids(self): :return: None if not found""" if "centroids" in self.D: return self.D["centroids"] - else: - return None + return None def get_number_of_faces(self): """Get the number of faces of all registered maps, @@ -1837,8 +1785,7 @@ def get_number_of_faces(self): :return: None if not found""" if "faces" in self.D: return self.D["faces"] - else: - return None + return None def get_number_of_kernels(self): """Get the number of kernels of all registered maps, @@ -1847,8 +1794,7 @@ def get_number_of_kernels(self): :return: None if not found""" if "kernels" in self.D: return self.D["kernels"] - else: - return None + return None def get_number_of_primitives(self): """Get the number of primitives of all registered maps, @@ -1857,8 +1803,7 @@ def get_number_of_primitives(self): :return: None if not found""" if "primitives" in self.D: return self.D["primitives"] - else: - return None + return None def get_number_of_nodes(self): """Get the number of nodes of all registered maps, @@ -1867,8 +1812,7 @@ def get_number_of_nodes(self): :return: None if not found""" if "nodes" in self.D: return self.D["nodes"] - else: - return None + return None def get_number_of_areas(self): """Get the number of areas of all registered maps, @@ -1877,8 +1821,7 @@ def get_number_of_areas(self): :return: None if not found""" if "areas" in self.D: return self.D["areas"] - else: - return None + return None def get_number_of_islands(self): """Get the number of islands of all registered maps, @@ -1887,8 +1830,7 @@ def get_number_of_islands(self): :return: None if not found""" if "islands" in self.D: return self.D["islands"] - else: - return None + return None def get_number_of_holes(self): """Get the number of holes of all registered maps, @@ -1897,8 +1839,7 @@ def get_number_of_holes(self): :return: None if not found""" if "holes" in self.D: return self.D["holes"] - else: - return None + return None def get_number_of_volumes(self): """Get the number of volumes of all registered maps, @@ -1907,8 +1848,7 @@ def get_number_of_volumes(self): :return: None if not found""" if "volumes" in self.D: return self.D["volumes"] - else: - return None + return None # Set the properties vector_register = property(fget=get_vector_register, fset=set_vector_register) diff --git a/python/grass/temporal/space_time_datasets.py b/python/grass/temporal/space_time_datasets.py index ec01bf45baa..92721277c9e 100644 --- a/python/grass/temporal/space_time_datasets.py +++ b/python/grass/temporal/space_time_datasets.py @@ -633,15 +633,13 @@ def spatial_overlapping(self, dataset): """Return True if the spatial extents overlap""" if self.get_type() == dataset.get_type() or dataset.get_type() == "str3ds": return self.spatial_extent.overlapping(dataset.spatial_extent) - else: - return self.spatial_extent.overlapping_2d(dataset.spatial_extent) + return self.spatial_extent.overlapping_2d(dataset.spatial_extent) def spatial_relation(self, dataset): """Return the two or three dimensional spatial relation""" if self.get_type() == dataset.get_type() or dataset.get_type() == "str3ds": return self.spatial_extent.spatial_relation(dataset.spatial_extent) - else: - return self.spatial_extent.spatial_relation_2d(dataset.spatial_extent) + return self.spatial_extent.spatial_relation_2d(dataset.spatial_extent) def spatial_intersection(self, dataset): """Return the three or two dimensional intersection as spatial_extent @@ -652,8 +650,7 @@ def spatial_intersection(self, dataset): """ if self.get_type() == dataset.get_type() or dataset.get_type() == "str3ds": return self.spatial_extent.intersect(dataset.spatial_extent) - else: - return self.spatial_extent.intersect_2d(dataset.spatial_extent) + return self.spatial_extent.intersect_2d(dataset.spatial_extent) def spatial_union(self, dataset): """Return the three or two dimensional union as spatial_extent @@ -664,8 +661,7 @@ def spatial_union(self, dataset): """ if self.get_type() == dataset.get_type() or dataset.get_type() == "str3ds": return self.spatial_extent.union(dataset.spatial_extent) - else: - return self.spatial_extent.union_2d(dataset.spatial_extent) + return self.spatial_extent.union_2d(dataset.spatial_extent) def spatial_disjoint_union(self, dataset): """Return the three or two dimensional union as spatial_extent object. @@ -675,8 +671,7 @@ def spatial_disjoint_union(self, dataset): """ if self.get_type() == dataset.get_type() or dataset.get_type() == "str3ds": return self.spatial_extent.disjoint_union(dataset.spatial_extent) - else: - return self.spatial_extent.disjoint_union_2d(dataset.spatial_extent) + return self.spatial_extent.disjoint_union_2d(dataset.spatial_extent) def get_np_array(self): """Return this 3D raster map as memmap numpy style array to access the @@ -1386,16 +1381,14 @@ def spatial_overlapping(self, dataset): if self.get_type() == dataset.get_type() or dataset.get_type() == "str3ds": return self.spatial_extent.overlapping(dataset.spatial_extent) - else: - return self.spatial_extent.overlapping_2d(dataset.spatial_extent) + return self.spatial_extent.overlapping_2d(dataset.spatial_extent) def spatial_relation(self, dataset): """Return the two or three dimensional spatial relation""" if self.get_type() == dataset.get_type() or dataset.get_type() == "str3ds": return self.spatial_extent.spatial_relation(dataset.spatial_extent) - else: - return self.spatial_extent.spatial_relation_2d(dataset.spatial_extent) + return self.spatial_extent.spatial_relation_2d(dataset.spatial_extent) def spatial_intersection(self, dataset): """Return the three or two dimensional intersection as spatial_extent @@ -1406,8 +1399,7 @@ def spatial_intersection(self, dataset): """ if self.get_type() == dataset.get_type() or dataset.get_type() == "raster3d": return self.spatial_extent.intersect(dataset.spatial_extent) - else: - return self.spatial_extent.intersect_2d(dataset.spatial_extent) + return self.spatial_extent.intersect_2d(dataset.spatial_extent) def spatial_union(self, dataset): """Return the three or two dimensional union as spatial_extent @@ -1418,8 +1410,7 @@ def spatial_union(self, dataset): """ if self.get_type() == dataset.get_type() or dataset.get_type() == "raster3d": return self.spatial_extent.union(dataset.spatial_extent) - else: - return self.spatial_extent.union_2d(dataset.spatial_extent) + return self.spatial_extent.union_2d(dataset.spatial_extent) def spatial_disjoint_union(self, dataset): """Return the three or two dimensional union as spatial_extent object. @@ -1429,8 +1420,7 @@ def spatial_disjoint_union(self, dataset): """ if self.get_type() == dataset.get_type() or dataset.get_type() == "raster3d": return self.spatial_extent.disjoint_union(dataset.spatial_extent) - else: - return self.spatial_extent.disjoint_union_2d(dataset.spatial_extent) + return self.spatial_extent.disjoint_union_2d(dataset.spatial_extent) def reset(self, ident): """Reset the internal structure and set the identifier""" diff --git a/python/grass/temporal/spatial_extent.py b/python/grass/temporal/spatial_extent.py index 26498e0385e..7bec3bc35e7 100644 --- a/python/grass/temporal/spatial_extent.py +++ b/python/grass/temporal/spatial_extent.py @@ -1787,8 +1787,7 @@ def get_id(self): """ if "id" in self.D: return self.D["id"] - else: - return None + return None def get_projection(self): """Get the projection of the spatial extent""" @@ -1840,48 +1839,42 @@ def get_north(self): :return: None if not found""" if "north" in self.D: return self.D["north"] - else: - return None + return None def get_south(self): """Get the southern edge of the map :return: None if not found""" if "south" in self.D: return self.D["south"] - else: - return None + return None def get_east(self): """Get the eastern edge of the map :return: None if not found""" if "east" in self.D: return self.D["east"] - else: - return None + return None def get_west(self): """Get the western edge of the map :return: None if not found""" if "west" in self.D: return self.D["west"] - else: - return None + return None def get_top(self): """Get the top edge of the map :return: None if not found""" if "top" in self.D: return self.D["top"] - else: - return None + return None def get_bottom(self): """Get the bottom edge of the map :return: None if not found""" if "bottom" in self.D: return self.D["bottom"] - else: - return None + return None id = property(fget=get_id, fset=set_id) north = property(fget=get_north, fset=set_north) diff --git a/python/grass/temporal/temporal_algebra.py b/python/grass/temporal/temporal_algebra.py index 1cf1cfd7366..e4717eb3326 100644 --- a/python/grass/temporal/temporal_algebra.py +++ b/python/grass/temporal/temporal_algebra.py @@ -727,11 +727,11 @@ def get_type(self): and self.value is not None ): return "global" - elif self.boolean is not None: + if self.boolean is not None: return "boolean" - elif self.relationop is not None and self.topology != []: + if self.relationop is not None and self.topology != []: return "operator" - elif self.td is not None: + if self.td is not None: return "timediff" def get_type_value(self): @@ -2336,8 +2336,7 @@ def recurse_compare(conditionlist): inverselist.append(map_i) if inverse: return inverselist - else: - return resultlist + return resultlist def p_statement_assign(self, t): # The expression should always return a list of maps diff --git a/python/grass/temporal/temporal_extent.py b/python/grass/temporal/temporal_extent.py index 42cce4599b1..29594ea8b72 100644 --- a/python/grass/temporal/temporal_extent.py +++ b/python/grass/temporal/temporal_extent.py @@ -202,9 +202,9 @@ def intersect(self, extent): return RelativeTemporalExtent( start_time=start, end_time=end, unit=self.get_unit() ) - elif issubclass(type(self), AbsoluteTemporalExtent): + if issubclass(type(self), AbsoluteTemporalExtent): return AbsoluteTemporalExtent(start_time=start, end_time=end) - elif issubclass(type(self), TemporalExtent): + if issubclass(type(self), TemporalExtent): return TemporalExtent(start_time=start, end_time=end) def disjoint_union(self, extent): @@ -391,9 +391,9 @@ def disjoint_union(self, extent): return RelativeTemporalExtent( start_time=start, end_time=end, unit=self.get_unit() ) - elif issubclass(type(self), AbsoluteTemporalExtent): + if issubclass(type(self), AbsoluteTemporalExtent): return AbsoluteTemporalExtent(start_time=start, end_time=end) - elif issubclass(type(self), TemporalExtent): + if issubclass(type(self), TemporalExtent): return TemporalExtent(start_time=start, end_time=end) def union(self, extent): @@ -986,24 +986,21 @@ def get_id(self): """ if "id" in self.D: return self.D["id"] - else: - return None + return None def get_start_time(self): """Get the valid start time of the extent :return: None if not found""" if "start_time" in self.D: return self.D["start_time"] - else: - return None + return None def get_end_time(self): """Get the valid end time of the extent :return: None if not found""" if "end_time" in self.D: return self.D["end_time"] - else: - return None + return None # Set the properties id = property(fget=get_id, fset=set_id) @@ -1153,8 +1150,7 @@ def get_granularity(self): :return: None if not found""" if "granularity" in self.D: return self.D["granularity"] - else: - return None + return None def get_map_time(self): """Get the type of the map time @@ -1169,8 +1165,7 @@ def get_map_time(self): """ if "map_time" in self.D: return self.D["map_time"] - else: - return None + return None # Properties granularity = property(fget=get_granularity, fset=set_granularity) @@ -1277,8 +1272,7 @@ def get_unit(self): :return: None if not found""" if "unit" in self.D: return self.D["unit"] - else: - return None + return None def temporal_relation(self, map): """Returns the temporal relation between temporal objects @@ -1427,8 +1421,7 @@ def get_granularity(self): :return: None if not found""" if "granularity" in self.D: return self.D["granularity"] - else: - return None + return None def get_map_time(self): """Get the type of the map time @@ -1443,8 +1436,7 @@ def get_map_time(self): """ if "map_time" in self.D: return self.D["map_time"] - else: - return None + return None # Properties granularity = property(fget=get_granularity, fset=set_granularity) diff --git a/python/grass/temporal/temporal_granularity.py b/python/grass/temporal/temporal_granularity.py index c3920b8b00b..b4043054520 100644 --- a/python/grass/temporal/temporal_granularity.py +++ b/python/grass/temporal/temporal_granularity.py @@ -146,8 +146,7 @@ def _get_row_time_tuple(db_table_row): # Check if input is list of MapDataset objects or SQLite rows if issubclass(maps[0].__class__, AbstractMapDataset): return _get_map_time_tuple - else: - return _get_row_time_tuple + return _get_row_time_tuple def _is_after(start, start1, end1) -> bool: @@ -777,8 +776,7 @@ def compute_common_absolute_time_granularity(gran_list, start_date_list=None): if int(num) > 60: if int(num) % 60 == 0: return "60 seconds" - else: - return "1 second" + return "1 second" if granule in {"minutes", "minute"}: # If the start minutes are different between the start dates @@ -790,8 +788,7 @@ def compute_common_absolute_time_granularity(gran_list, start_date_list=None): if int(num) > 60: if int(num) % 60 == 0: return "60 minutes" - else: - return "1 minute" + return "1 minute" if granule in {"hours", "hour"}: # If the start hours are different between the start dates @@ -803,8 +800,7 @@ def compute_common_absolute_time_granularity(gran_list, start_date_list=None): if int(num) > 24: if int(num) % 24 == 0: return "24 hours" - else: - return "1 hour" + return "1 hour" if granule in {"days", "day"}: # If the start days are different between the start dates @@ -816,8 +812,7 @@ def compute_common_absolute_time_granularity(gran_list, start_date_list=None): if int(num) > 365: if int(num) % 365 == 0: return "365 days" - else: - return "1 day" + return "1 day" if granule in {"months", "month"}: # If the start months are different between the start dates @@ -829,8 +824,7 @@ def compute_common_absolute_time_granularity(gran_list, start_date_list=None): if int(num) > 12: if int(num) % 12 == 0: return "12 months" - else: - return "1 month" + return "1 month" return common_granule @@ -1125,20 +1119,18 @@ def gran_singular_unit(gran): output, unit = gran.split(" ") if unit in PLURAL_GRAN: return unit[:-1] - elif unit in SINGULAR_GRAN: + if unit in SINGULAR_GRAN: return unit - else: - lists = "{gr}".format(gr=SUPPORTED_GRAN).replace("[", "").replace("]", "") - print( - _( - "Output granularity seems not to be valid. Please use " - "one of the following values : {gr}" - ).format(gr=lists) - ) - return False - else: - print(_("Invalid absolute granularity")) + lists = "{gr}".format(gr=SUPPORTED_GRAN).replace("[", "").replace("]", "") + print( + _( + "Output granularity seems not to be valid. Please use " + "one of the following values : {gr}" + ).format(gr=lists) + ) return False + print(_("Invalid absolute granularity")) + return False ####################################################################### @@ -1170,16 +1162,15 @@ def gran_plural_unit(gran): output, unit = gran.split(" ") if unit in PLURAL_GRAN: return unit - elif unit in SINGULAR_GRAN: + if unit in SINGULAR_GRAN: return f"{unit}s" - else: - lists = ", ".join(SUPPORTED_GRAN) - print( - _( - "Output granularity seems not to be valid. Please use " - "one of the following values : {gr}" - ).format(gr=lists) - ) + lists = ", ".join(SUPPORTED_GRAN) + print( + _( + "Output granularity seems not to be valid. Please use " + "one of the following values : {gr}" + ).format(gr=lists) + ) else: print(_("Invalid absolute granularity")) return False @@ -1234,8 +1225,7 @@ def _return(output, tounit, shell): if output == 1: return f"{output} {tounit}" - else: - return f"{output} {tounit}s" + return f"{output} {tounit}s" # TODO check the leap second if check_granularity_string(from_gran, "absolute"): @@ -1256,9 +1246,8 @@ def _return(output, tounit, shell): return _return(output, tounit, shell) print(_("Probably you need to invert 'from_gran' and 'to_gran'")) return False - else: - print(_("Invalid absolute granularity")) - return False + print(_("Invalid absolute granularity")) + return False ############################################################################### diff --git a/python/grass/temporal/temporal_raster_base_algebra.py b/python/grass/temporal/temporal_raster_base_algebra.py index 3424110803f..8ff156cbed5 100644 --- a/python/grass/temporal/temporal_raster_base_algebra.py +++ b/python/grass/temporal/temporal_raster_base_algebra.py @@ -727,7 +727,7 @@ def build_condition_cmd_list( # Append map to result map list. resultlist.append(map_i) return resultlist - elif isinstance(conclusionlist, list): + if isinstance(conclusionlist, list): # Build result command map list between conditions and conclusions. if self.debug: print("build_condition_cmd_list", condition_topolist) diff --git a/raster/r.topidx/arc_to_gridatb.py b/raster/r.topidx/arc_to_gridatb.py index 17e49365697..3eba7114430 100755 --- a/raster/r.topidx/arc_to_gridatb.py +++ b/raster/r.topidx/arc_to_gridatb.py @@ -9,9 +9,8 @@ def match(pattern, string): if m: match.value = m.group(1) return True - else: - match.value = None - return False + match.value = None + return False if len(sys.argv) != 3 or re.match("^-*help", sys.argv[1]): diff --git a/scripts/g.extension/g.extension.py b/scripts/g.extension/g.extension.py index ed0544ee2c4..194f498d49c 100644 --- a/scripts/g.extension/g.extension.py +++ b/scripts/g.extension/g.extension.py @@ -508,24 +508,23 @@ def get_version_branch(major_version): version_branch = f"grass{major_version}" if sys.platform == "win32": return version_branch - else: - branch = gs.Popen( - ["git", "ls-remote", "--heads", GIT_URL, f"refs/heads/{version_branch}"], - stdout=PIPE, - stderr=PIPE, - ) - branch, stderr = branch.communicate() - if stderr: - gs.fatal( - _( - "Failed to get branch from the Git repository <{repo_path}>.\n" - "{error}" - ).format( - repo_path=GIT_URL, - error=gs.decode(stderr), - ) + branch = gs.Popen( + ["git", "ls-remote", "--heads", GIT_URL, f"refs/heads/{version_branch}"], + stdout=PIPE, + stderr=PIPE, + ) + branch, stderr = branch.communicate() + if stderr: + gs.fatal( + _( + "Failed to get branch from the Git repository <{repo_path}>.\n" + "{error}" + ).format( + repo_path=GIT_URL, + error=gs.decode(stderr), ) - branch = gs.decode(branch) + ) + branch = gs.decode(branch) if version_branch not in branch: version_branch = "grass{}".format(int(major_version) - 1) return version_branch @@ -2595,8 +2594,7 @@ def resolve_known_host_service(url, name, branch): ) gs.verbose(_("Will use the following URL for download: {0}").format(url)) return "remote_zip", url - else: - return None, None + return None, None def validate_url(url): @@ -2720,7 +2718,7 @@ def resolve_source_code(url=None, name=None, branch=None, fork=False): # Handle local URLs if os.path.isdir(url): return "dir", os.path.abspath(url) - elif os.path.exists(url): + if os.path.exists(url): if url.endswith(".zip"): return "zip", os.path.abspath(url) for suffix in extract_tar.supported_formats: @@ -2823,7 +2821,7 @@ def main(): xmlurl = resolve_xmlurl_prefix(original_url, source=source) list_available_extensions(xmlurl) return 0 - elif flags["a"]: + if flags["a"]: list_installed_extensions(toolboxes=flags["t"]) return 0 diff --git a/scripts/g.search.modules/g.search.modules.py b/scripts/g.search.modules/g.search.modules.py index cb40cbdced5..5777b4bd033 100755 --- a/scripts/g.search.modules/g.search.modules.py +++ b/scripts/g.search.modules/g.search.modules.py @@ -184,8 +184,7 @@ def colored(pattern, attrs): if pattern: return text.replace(pattern, colored(pattern, attrs=attrs)) - else: - return colored(text, attrs=attrs) + return colored(text, attrs=attrs) def _search_module( diff --git a/scripts/i.image.mosaic/i.image.mosaic.py b/scripts/i.image.mosaic/i.image.mosaic.py index df194c49c8f..fc342a3ed96 100755 --- a/scripts/i.image.mosaic/i.image.mosaic.py +++ b/scripts/i.image.mosaic/i.image.mosaic.py @@ -56,9 +56,8 @@ def get_limit(map): def make_expression(i, count): if i > count: return "null()" - else: - e = make_expression(i + 1, count) - return "if(isnull($image%d),%s,$image%d+$offset%d)" % (i, e, i, i) + e = make_expression(i + 1, count) + return "if(isnull($image%d),%s,$image%d+$offset%d)" % (i, e, i, i) def main(): diff --git a/scripts/r.in.wms/wms_base.py b/scripts/r.in.wms/wms_base.py index 234456f1518..2d959289b23 100644 --- a/scripts/r.in.wms/wms_base.py +++ b/scripts/r.in.wms/wms_base.py @@ -782,8 +782,7 @@ def GetSRSParamVal(srs): if srs in {84, 83, 27}: return "OGC:CRS{}".format(srs) - else: - return "EPSG:{}".format(srs) + return "EPSG:{}".format(srs) def GetEpsg(srs): diff --git a/scripts/r.in.wms/wms_drv.py b/scripts/r.in.wms/wms_drv.py index dd666573cfe..531112a1a6c 100644 --- a/scripts/r.in.wms/wms_drv.py +++ b/scripts/r.in.wms/wms_drv.py @@ -587,7 +587,7 @@ def _getQueryBbox(self, bbox, proj, srs_param, version): # CRS:84 and CRS:83 are exception (CRS:83 and CRS:27 need to be tested) if srs_param in {84, 83} or version != "1.3.0": return bbox - elif Srs(GetSRSParamVal(srs_param)).axisorder == "yx": + if Srs(GetSRSParamVal(srs_param)).axisorder == "yx": return self._flipBbox(bbox) return bbox diff --git a/temporal/t.info/t.info.py b/temporal/t.info/t.info.py index 2f0b0d528ec..575446030a1 100755 --- a/temporal/t.info/t.info.py +++ b/temporal/t.info/t.info.py @@ -91,7 +91,7 @@ def main(): " +----------------------------------------------------------------------------+" # noqa: E501 ) return - elif system and not history: + if system and not history: print("dbmi_python_interface='" + str(dbif.get_dbmi().__name__) + "'") print("dbmi_string='" + str(tgis.get_tgis_database_string()) + "'") print("sql_template_path='" + str(tgis.get_sql_template_path()) + "'") diff --git a/utils/g.html2man/g.html2man.py b/utils/g.html2man/g.html2man.py index 498ce34dfe1..58ffb2f7bca 100755 --- a/utils/g.html2man/g.html2man.py +++ b/utils/g.html2man/g.html2man.py @@ -17,12 +17,10 @@ def fix(content): tag, attrs, body = content if tag == "div" and ("class", "toc") in attrs: return None - else: - return (tag, attrs, fix(body)) - elif isinstance(content, list): + return (tag, attrs, fix(body)) + if isinstance(content, list): return [fixed for item in content for fixed in [fix(item)] if fixed is not None] - else: - return content + return content def main(): diff --git a/utils/g.html2man/ggroff.py b/utils/g.html2man/ggroff.py index 49c16f19f44..e62c5ec2b1c 100644 --- a/utils/g.html2man/ggroff.py +++ b/utils/g.html2man/ggroff.py @@ -253,8 +253,7 @@ def pp_text(self, content): for line in lines: self.pp_text(line) return - else: - content = lines[0] + content = lines[0] if self.at_bol and not self.get("preformat"): content = self.strip_re.sub("", content) self.pp_string(content) diff --git a/utils/g.html2man/ghtml.py b/utils/g.html2man/ghtml.py index 1a36fd3af06..4b30950ff0e 100644 --- a/utils/g.html2man/ghtml.py +++ b/utils/g.html2man/ghtml.py @@ -225,8 +225,7 @@ def __init__(self, entities=None): def top(self): if self.tag_stack == []: return None - else: - return self.tag_stack[-1][0] + return self.tag_stack[-1][0] def pop(self): self.excluded = self.excluded_stack.pop() diff --git a/utils/mkhtml.py b/utils/mkhtml.py index f64f6d7d97a..39a5e6e26eb 100644 --- a/utils/mkhtml.py +++ b/utils/mkhtml.py @@ -406,15 +406,14 @@ def get_last_git_commit(src_dir, addon_path, is_addon): commit=process_result.stdout.decode(), src_dir=src_dir, ) - elif gs: + if gs: # Addons installation return get_git_commit_from_rest_api_for_addon_repo( addon_path=addon_path, src_dir=src_dir, ) # During GRASS GIS compilation from source code without Git - else: - return get_git_commit_from_file(src_dir=src_dir) + return get_git_commit_from_file(src_dir=src_dir) html_page_footer_pages_path = os.getenv("HTML_PAGE_FOOTER_PAGES_PATH") or "" @@ -849,10 +848,9 @@ def to_title(name): """Convert name of command class/family to form suitable for title""" if name == "raster3d": return "3D raster" - elif name == "postscript": + if name == "postscript": return "PostScript" - else: - return name.capitalize() + return name.capitalize() index_titles = {} From 5e5f30e3ac67c24f8aa0cb6df0448002dc390533 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 12:33:11 -0400 Subject: [PATCH 353/471] CI(deps): Lock file maintenance (#4465) From 56ba44c512682774d2c586b9df66aa5ba5e41343 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 18:01:40 +0000 Subject: [PATCH 354/471] CI(deps): Update actions/upload-artifact action to v4.4.1 (#4466) --- .github/actions/create-upload-suggestions/action.yml | 4 ++-- .github/workflows/macos.yml | 2 +- .github/workflows/osgeo4w.yml | 2 +- .github/workflows/pytest.yml | 2 +- .github/workflows/python-code-quality.yml | 4 ++-- .github/workflows/ubuntu.yml | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/actions/create-upload-suggestions/action.yml b/.github/actions/create-upload-suggestions/action.yml index 2493fba2573..3bc93a41a92 100644 --- a/.github/actions/create-upload-suggestions/action.yml +++ b/.github/actions/create-upload-suggestions/action.yml @@ -177,7 +177,7 @@ runs: echo "diff-file-name=${INPUT_DIFF_FILE_NAME}" >> "${GITHUB_OUTPUT}" env: INPUT_DIFF_FILE_NAME: ${{ steps.tool-name-safe.outputs.diff-file-name }} - - uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 + - uses: actions/upload-artifact@604373da6381bf24206979c74d06a550515601b9 # v4.4.1 id: upload-diff if: >- ${{ (steps.files_changed.outputs.files_changed == 'true') && @@ -200,7 +200,7 @@ runs: echo 'Suggestions can only be added near to lines changed in this PR.' echo 'If any fixes can be added as code suggestions, they will be added shortly from another workflow.' } >> "${GITHUB_STEP_SUMMARY}" - - uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 + - uses: actions/upload-artifact@604373da6381bf24206979c74d06a550515601b9 # v4.4.1 id: upload-changes if: >- ${{ always() && diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index a90cdcd7ad5..b3f7aa0954e 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -107,7 +107,7 @@ jobs: nc_spm_full_v2alpha2.tar.gz" - name: Make HTML test report available if: ${{ !cancelled() }} - uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 + uses: actions/upload-artifact@604373da6381bf24206979c74d06a550515601b9 # v4.4.1 with: name: testreport-macOS path: testreport diff --git a/.github/workflows/osgeo4w.yml b/.github/workflows/osgeo4w.yml index d9c67cc37c9..54f2d3c7bdb 100644 --- a/.github/workflows/osgeo4w.yml +++ b/.github/workflows/osgeo4w.yml @@ -120,7 +120,7 @@ jobs: - name: Make HTML test report available if: ${{ always() }} - uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 + uses: actions/upload-artifact@604373da6381bf24206979c74d06a550515601b9 # v4.4.1 with: name: testreport-${{ matrix.os }} path: testreport diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index f0cda6a95f5..62f097b6621 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -116,7 +116,7 @@ jobs: token: ${{ secrets.CODECOV_TOKEN }} - name: Make python-only code coverage test report available if: ${{ !cancelled() }} - uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 + uses: actions/upload-artifact@604373da6381bf24206979c74d06a550515601b9 # v4.4.1 with: name: python-codecoverage-report-${{ matrix.os }}-${{ matrix.python-version }} path: coverage_html_report diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index ac9fa5ea4cc..5a740498c2f 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -129,7 +129,7 @@ jobs: bandit -c pyproject.toml -iii -r . -f sarif -o bandit.sarif --exit-zero - name: Upload Bandit Scan Results - uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 + uses: actions/upload-artifact@604373da6381bf24206979c74d06a550515601b9 # v4.4.1 with: name: bandit.sarif path: bandit.sarif @@ -201,7 +201,7 @@ jobs: cp -rp dist.$ARCH/docs/html/libpython sphinx-grass - name: Make Sphinx documentation available - uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 + uses: actions/upload-artifact@604373da6381bf24206979c74d06a550515601b9 # v4.4.1 with: name: sphinx-grass path: sphinx-grass diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 25ce2afdd67..2b51971518f 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -149,7 +149,7 @@ jobs: - name: Make HTML test report available if: ${{ always() }} - uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 + uses: actions/upload-artifact@604373da6381bf24206979c74d06a550515601b9 # v4.4.1 with: name: testreport-${{ matrix.os }}-${{ matrix.config }}-${{ matrix.extra-include }} path: testreport From 368f0eb4ca748c4e3aa4490223d4739e78db8d9c Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Mon, 7 Oct 2024 15:49:40 -0400 Subject: [PATCH 355/471] v.to.db: Fix resource Leak issue in update.c (#4461) --- vector/v.to.db/update.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/vector/v.to.db/update.c b/vector/v.to.db/update.c index dd63ef78d35..972fd6be93e 100644 --- a/vector/v.to.db/update.c +++ b/vector/v.to.db/update.c @@ -3,6 +3,7 @@ #include #include #include +#include #include "global.h" static int srch(const void *, const void *); @@ -297,6 +298,8 @@ int update(struct Map_info *Map) db_close_database_shutdown_driver(driver); db_free_string(&stmt); + Vect_destroy_field_info(Fi); + Vect_destroy_field_info(qFi); return 0; } From 573d54ae4a43f390334780d060a4191fc006acb2 Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Mon, 7 Oct 2024 15:50:42 -0400 Subject: [PATCH 356/471] ps.map: Fix Resource Leak issue in ps_vareas.c (#4464) --- ps/ps.map/ps_vareas.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ps/ps.map/ps_vareas.c b/ps/ps.map/ps_vareas.c index 5cd3471a6a8..3968140bd25 100644 --- a/ps/ps.map/ps_vareas.c +++ b/ps/ps.map/ps_vareas.c @@ -65,6 +65,7 @@ static int plot_area(struct Map_info *P_map, int area, double shift) if (0 > (ret = Vect_get_area_points(P_map, area, Points))) { if (ret == -1) G_warning(_("Read error in vector map")); + Vect_destroy_line_struct(Points); return 0; } construct_path(Points, shift, WHOLE_PATH); @@ -76,10 +77,12 @@ static int plot_area(struct Map_info *P_map, int area, double shift) if (0 > (ret = Vect_get_isle_points(P_map, island, Points))) { if (ret == -1) G_warning(_("Read error in vector map")); + Vect_destroy_line_struct(Points); return -1; } construct_path(Points, shift, WHOLE_PATH); } + Vect_destroy_line_struct(Points); return 1; } From 3f6a6e42057e1eb8116a23c5fb30c795a0c4d3b1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 21:17:34 +0000 Subject: [PATCH 357/471] CI(deps): Update actions/checkout action to v4.2.1 (#4468) --- .github/workflows/additional_checks.yml | 2 +- .github/workflows/clang-format-check.yml | 2 +- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/coverity.yml | 2 +- .github/workflows/create_release_draft.yml | 2 +- .github/workflows/docker.yml | 2 +- .github/workflows/gcc.yml | 2 +- .github/workflows/macos.yml | 2 +- .github/workflows/osgeo4w.yml | 2 +- .github/workflows/periodic_update.yml | 2 +- .github/workflows/pytest.yml | 2 +- .github/workflows/python-code-quality.yml | 2 +- .github/workflows/super-linter.yml | 2 +- .github/workflows/test-nix.yml | 2 +- .github/workflows/titles.yml | 2 +- .github/workflows/ubuntu.yml | 2 +- 16 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/workflows/additional_checks.yml b/.github/workflows/additional_checks.yml index 1f8e355b487..a91d221f1e4 100644 --- a/.github/workflows/additional_checks.yml +++ b/.github/workflows/additional_checks.yml @@ -24,7 +24,7 @@ jobs: steps: - name: Checkout repository contents - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 with: fetch-depth: 31 diff --git a/.github/workflows/clang-format-check.yml b/.github/workflows/clang-format-check.yml index f68483dea20..13ba5bb3cd9 100644 --- a/.github/workflows/clang-format-check.yml +++ b/.github/workflows/clang-format-check.yml @@ -16,7 +16,7 @@ jobs: name: Formatting Check runs-on: ubuntu-latest steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 with: persist-credentials: false - uses: DoozyX/clang-format-lint-action@c71d0bf4e21876ebec3e5647491186f8797fde31 # v0.18.2 diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 64994f93535..a8237e5838d 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -40,7 +40,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 - name: Set up Python uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 with: diff --git a/.github/workflows/coverity.yml b/.github/workflows/coverity.yml index 24c98c96601..22a6b7a42a8 100644 --- a/.github/workflows/coverity.yml +++ b/.github/workflows/coverity.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-22.04 if: github.repository == 'OSGeo/grass' steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 - name: Get dependencies run: | diff --git a/.github/workflows/create_release_draft.yml b/.github/workflows/create_release_draft.yml index 4751c3c43a0..3112fb8b33f 100644 --- a/.github/workflows/create_release_draft.yml +++ b/.github/workflows/create_release_draft.yml @@ -30,7 +30,7 @@ jobs: contents: write steps: - name: Checks-out repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 with: ref: ${{ github.ref }} fetch-depth: 0 diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 8a23d8e200b..4de674ee9a2 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -49,7 +49,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 with: fetch-depth: 0 - name: Docker meta diff --git a/.github/workflows/gcc.yml b/.github/workflows/gcc.yml index 24b9dcb4043..651cb272fba 100644 --- a/.github/workflows/gcc.yml +++ b/.github/workflows/gcc.yml @@ -26,7 +26,7 @@ jobs: fail-fast: false steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 - name: Get dependencies run: | sudo apt-get update -y diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index b3f7aa0954e..71453cf1e5e 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -44,7 +44,7 @@ jobs: -mindepth 1 -maxdepth 1 -type f -print -delete # Rehash to forget about the deleted files hash -r - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 - name: Get current date cache key segment id: date # Year and week of year so cache key changes weekly diff --git a/.github/workflows/osgeo4w.yml b/.github/workflows/osgeo4w.yml index 54f2d3c7bdb..36395262a92 100644 --- a/.github/workflows/osgeo4w.yml +++ b/.github/workflows/osgeo4w.yml @@ -31,7 +31,7 @@ jobs: run: | git config --global core.autocrlf false git config --global core.eol lf - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 - uses: msys2/setup-msys2@ddf331adaebd714795f1042345e6ca57bd66cea8 # v2.24.1 with: path-type: inherit diff --git a/.github/workflows/periodic_update.yml b/.github/workflows/periodic_update.yml index f5441297250..17855d6149e 100644 --- a/.github/workflows/periodic_update.yml +++ b/.github/workflows/periodic_update.yml @@ -21,7 +21,7 @@ jobs: - name: Create URL to the run output id: vars run: echo "run-url=https://github.com/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" >> $GITHUB_OUTPUT - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 - name: "Check that autoconf scripts are up-to-date:" run: | rm -f config.guess config.sub diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 62f097b6621..bc66914dad8 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -32,7 +32,7 @@ jobs: PYTHONWARNINGS: always steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 - name: Set up Python uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index 5a740498c2f..7e7626e3108 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -54,7 +54,7 @@ jobs: echo Bandit: ${{ env.BANDIT_VERSION }} echo Ruff: ${{ env.RUFF_VERSION }} - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 - name: Set up Python uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 diff --git a/.github/workflows/super-linter.yml b/.github/workflows/super-linter.yml index 117f5cd43fb..62328723f77 100644 --- a/.github/workflows/super-linter.yml +++ b/.github/workflows/super-linter.yml @@ -25,7 +25,7 @@ jobs: statuses: write steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 with: # super-linter needs the full git history to get the # list of files that changed across commits diff --git a/.github/workflows/test-nix.yml b/.github/workflows/test-nix.yml index 03f767730a9..ba14e0747ab 100644 --- a/.github/workflows/test-nix.yml +++ b/.github/workflows/test-nix.yml @@ -28,7 +28,7 @@ jobs: contents: read steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 - name: Install nix uses: DeterminateSystems/nix-installer-action@da36cb69b1c3247ad7a1f931ebfd954a1105ef14 # v14 diff --git a/.github/workflows/titles.yml b/.github/workflows/titles.yml index f90e7275a2e..f202fdafe02 100644 --- a/.github/workflows/titles.yml +++ b/.github/workflows/titles.yml @@ -21,7 +21,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout base repository (doesn't include the PR changes) - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 - name: Call PR title validation function run: python utils/generate_release_notes.py check "${PR_TITLE}" "" "" env: diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 2b51971518f..05488c1936b 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -59,7 +59,7 @@ jobs: fail-fast: false steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 - name: Invert inclusion list to an exclusion list id: get-exclude From 3ce2a0fe4885334c41a6de1f33e1877a670249dd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 21:43:30 +0000 Subject: [PATCH 358/471] CI(deps): Update github/codeql-action action to v3.26.12 (#4469) --- .github/workflows/codeql-analysis.yml | 4 ++-- .github/workflows/python-code-quality.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index a8237e5838d..88158341f13 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -56,7 +56,7 @@ jobs: if: ${{ matrix.language == 'c-cpp' }} - name: Initialize CodeQL - uses: github/codeql-action/init@6db8d6351fd0be61f9ed8ebd12ccd35dcec51fea # v3.26.11 + uses: github/codeql-action/init@c36620d31ac7c881962c3d9dd939c40ec9434f2b # v3.26.12 with: languages: ${{ matrix.language }} config-file: ./.github/codeql/codeql-config.yml @@ -81,6 +81,6 @@ jobs: run: .github/workflows/build_ubuntu-22.04.sh "${HOME}/install" - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@6db8d6351fd0be61f9ed8ebd12ccd35dcec51fea # v3.26.11 + uses: github/codeql-action/analyze@c36620d31ac7c881962c3d9dd939c40ec9434f2b # v3.26.12 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index 7e7626e3108..a71c579e7de 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -135,7 +135,7 @@ jobs: path: bandit.sarif - name: Upload SARIF File into Security Tab - uses: github/codeql-action/upload-sarif@6db8d6351fd0be61f9ed8ebd12ccd35dcec51fea # v3.26.11 + uses: github/codeql-action/upload-sarif@c36620d31ac7c881962c3d9dd939c40ec9434f2b # v3.26.12 with: sarif_file: bandit.sarif From b4187339ee5bcf91db6425931d0774dd0f48ad33 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 22:43:02 +0000 Subject: [PATCH 359/471] CI(deps): Update black to v24.10.0 (#4473) --- .github/workflows/python-code-quality.yml | 2 +- .pre-commit-config.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index a71c579e7de..5103166b353 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -28,7 +28,7 @@ jobs: PYTHON_VERSION: "3.10" MIN_PYTHON_VERSION: "3.8" # renovate: datasource=pypi depName=black - BLACK_VERSION: "24.8.0" + BLACK_VERSION: "24.10.0" # renovate: datasource=pypi depName=flake8 FLAKE8_VERSION: "7.1.1" # renovate: datasource=pypi depName=pylint diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 60865cd3432..2c136da40ff 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -48,7 +48,7 @@ repos: - id: markdownlint-fix # Using this mirror lets us use mypyc-compiled black, which is about 2x faster - repo: https://github.com/psf/black-pre-commit-mirror - rev: 24.8.0 + rev: 24.10.0 hooks: - id: black-jupyter exclude: | From 498676c5266d15bcad76727ce087671502a03104 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 9 Oct 2024 06:40:59 -0400 Subject: [PATCH 360/471] CI(deps): Update actions/upload-artifact action to v4.4.2 (#4477) --- .github/actions/create-upload-suggestions/action.yml | 4 ++-- .github/workflows/macos.yml | 2 +- .github/workflows/osgeo4w.yml | 2 +- .github/workflows/pytest.yml | 2 +- .github/workflows/python-code-quality.yml | 4 ++-- .github/workflows/ubuntu.yml | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/actions/create-upload-suggestions/action.yml b/.github/actions/create-upload-suggestions/action.yml index 3bc93a41a92..181babe11e9 100644 --- a/.github/actions/create-upload-suggestions/action.yml +++ b/.github/actions/create-upload-suggestions/action.yml @@ -177,7 +177,7 @@ runs: echo "diff-file-name=${INPUT_DIFF_FILE_NAME}" >> "${GITHUB_OUTPUT}" env: INPUT_DIFF_FILE_NAME: ${{ steps.tool-name-safe.outputs.diff-file-name }} - - uses: actions/upload-artifact@604373da6381bf24206979c74d06a550515601b9 # v4.4.1 + - uses: actions/upload-artifact@84480863f228bb9747b473957fcc9e309aa96097 # v4.4.2 id: upload-diff if: >- ${{ (steps.files_changed.outputs.files_changed == 'true') && @@ -200,7 +200,7 @@ runs: echo 'Suggestions can only be added near to lines changed in this PR.' echo 'If any fixes can be added as code suggestions, they will be added shortly from another workflow.' } >> "${GITHUB_STEP_SUMMARY}" - - uses: actions/upload-artifact@604373da6381bf24206979c74d06a550515601b9 # v4.4.1 + - uses: actions/upload-artifact@84480863f228bb9747b473957fcc9e309aa96097 # v4.4.2 id: upload-changes if: >- ${{ always() && diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 71453cf1e5e..7b4e7eb18e2 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -107,7 +107,7 @@ jobs: nc_spm_full_v2alpha2.tar.gz" - name: Make HTML test report available if: ${{ !cancelled() }} - uses: actions/upload-artifact@604373da6381bf24206979c74d06a550515601b9 # v4.4.1 + uses: actions/upload-artifact@84480863f228bb9747b473957fcc9e309aa96097 # v4.4.2 with: name: testreport-macOS path: testreport diff --git a/.github/workflows/osgeo4w.yml b/.github/workflows/osgeo4w.yml index 36395262a92..180cc79ec08 100644 --- a/.github/workflows/osgeo4w.yml +++ b/.github/workflows/osgeo4w.yml @@ -120,7 +120,7 @@ jobs: - name: Make HTML test report available if: ${{ always() }} - uses: actions/upload-artifact@604373da6381bf24206979c74d06a550515601b9 # v4.4.1 + uses: actions/upload-artifact@84480863f228bb9747b473957fcc9e309aa96097 # v4.4.2 with: name: testreport-${{ matrix.os }} path: testreport diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index bc66914dad8..60ddb223074 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -116,7 +116,7 @@ jobs: token: ${{ secrets.CODECOV_TOKEN }} - name: Make python-only code coverage test report available if: ${{ !cancelled() }} - uses: actions/upload-artifact@604373da6381bf24206979c74d06a550515601b9 # v4.4.1 + uses: actions/upload-artifact@84480863f228bb9747b473957fcc9e309aa96097 # v4.4.2 with: name: python-codecoverage-report-${{ matrix.os }}-${{ matrix.python-version }} path: coverage_html_report diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index 5103166b353..5da86464470 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -129,7 +129,7 @@ jobs: bandit -c pyproject.toml -iii -r . -f sarif -o bandit.sarif --exit-zero - name: Upload Bandit Scan Results - uses: actions/upload-artifact@604373da6381bf24206979c74d06a550515601b9 # v4.4.1 + uses: actions/upload-artifact@84480863f228bb9747b473957fcc9e309aa96097 # v4.4.2 with: name: bandit.sarif path: bandit.sarif @@ -201,7 +201,7 @@ jobs: cp -rp dist.$ARCH/docs/html/libpython sphinx-grass - name: Make Sphinx documentation available - uses: actions/upload-artifact@604373da6381bf24206979c74d06a550515601b9 # v4.4.1 + uses: actions/upload-artifact@84480863f228bb9747b473957fcc9e309aa96097 # v4.4.2 with: name: sphinx-grass path: sphinx-grass diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 05488c1936b..88ddd0d31b2 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -149,7 +149,7 @@ jobs: - name: Make HTML test report available if: ${{ always() }} - uses: actions/upload-artifact@604373da6381bf24206979c74d06a550515601b9 # v4.4.1 + uses: actions/upload-artifact@84480863f228bb9747b473957fcc9e309aa96097 # v4.4.2 with: name: testreport-${{ matrix.os }}-${{ matrix.config }}-${{ matrix.extra-include }} path: testreport From cbe2b9eaa9c8d5614c1170a7da508221361d6aef Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Thu, 10 Oct 2024 12:23:44 -0400 Subject: [PATCH 361/471] wxGUI: Fixed E722 in frame.py (#4440) --- .flake8 | 3 --- gui/wxpython/lmgr/frame.py | 12 ++++++------ gui/wxpython/lmgr/layertree.py | 11 ++++------- 3 files changed, 10 insertions(+), 16 deletions(-) diff --git a/.flake8 b/.flake8 index 5126c042439..313241f2d9d 100644 --- a/.flake8 +++ b/.flake8 @@ -27,9 +27,6 @@ per-file-ignores = gui/scripts/d.wms.py: E501 gui/wxpython/image2target/*: F841, E722 gui/wxpython/image2target/g.gui.image2target.py: E501, F841 - gui/wxpython/lmgr/frame.py: E722 - # layertree still includes some formatting issues (it is ignored by Black) - gui/wxpython/lmgr/layertree.py: E722 gui/wxpython/modules/*: F841, E722 gui/wxpython/nviz/*: F841, E266, E722, F403, F405 gui/wxpython/photo2image/*: F841, E722, E265 diff --git a/gui/wxpython/lmgr/frame.py b/gui/wxpython/lmgr/frame.py index 80f373239f8..2413d3b7c4a 100644 --- a/gui/wxpython/lmgr/frame.py +++ b/gui/wxpython/lmgr/frame.py @@ -271,7 +271,7 @@ def show_menu_errors(messages): y = client_disp[1] self.SetPosition((x, y)) self.SetSize((w, h)) - except: + except (ValueError, IndexError): pass else: # does center (of screen) make sense for lmgr? @@ -884,7 +884,7 @@ def OnCBPageChanged(self, event): try: self.GetMapDisplay().SetFocus() self.GetMapDisplay().Raise() - except: + except AttributeError: pass event.Skip() @@ -1123,7 +1123,7 @@ def GetMenuCmd(self, event): try: cmdlist = cmd.split(" ") - except: # already list? + except AttributeError: # already list? cmdlist = cmd # check list of dummy commands for GUI modules that do not have GRASS @@ -1135,7 +1135,7 @@ def GetMenuCmd(self, event): layer = self.GetLayerTree().layer_selected name = self.GetLayerTree().GetLayerInfo(layer, key="maplayer").name type = self.GetLayerTree().GetLayerInfo(layer, key="type") - except: + except AttributeError: layer = None if layer and len(cmdlist) == 1: # only if no parameters given @@ -1183,7 +1183,7 @@ def OnVDigit(self, event): # available only for vector map layers try: mapLayer = tree.GetLayerInfo(layer, key="maplayer") - except: + except AttributeError: mapLayer = None if not mapLayer or mapLayer.GetType() != "vector": @@ -1860,7 +1860,7 @@ def OnShowAttributeTable(self, event, selection=None): # available only for vector map layers try: maptype = tree.GetLayerInfo(layer, key="maplayer").type - except: + except AttributeError: maptype = None if not maptype or maptype != "vector": diff --git a/gui/wxpython/lmgr/layertree.py b/gui/wxpython/lmgr/layertree.py index daefab44223..86531bcb5ad 100644 --- a/gui/wxpython/lmgr/layertree.py +++ b/gui/wxpython/lmgr/layertree.py @@ -1742,7 +1742,7 @@ def OnDeleteLayer(self, event): try: item.properties.Close(True) - except: + except AttributeError: pass if item != self.root: @@ -1758,7 +1758,7 @@ def OnDeleteLayer(self, event): try: if self.GetLayerInfo(item, key="type") != "group": self.Map.DeleteLayer(self.GetLayerInfo(item, key="maplayer")) - except: + except AttributeError: pass # redraw map if auto-rendering is enabled @@ -1997,10 +1997,7 @@ def OnEndDrag(self, event): def OnDrop(self, dropTarget, dragItem): # save everything associated with item to drag - try: - old = dragItem # make sure this member exists - except: - return + old = dragItem # make sure this member exists Debug.msg(4, "LayerTree.OnDrop(): layer=%s" % (self.GetItemText(dragItem))) @@ -2046,7 +2043,7 @@ def RecreateItem(self, dragItem, dropTarget, parent=None): newctrl.SetValue( self.GetLayerInfo(dragItem, key="maplayer").GetCmd(string=True) ) - except: + except Exception: pass newctrl.Bind(wx.EVT_TEXT_ENTER, self.OnCmdChanged) data = self.GetPyData(dragItem) From 400b0df4a7823b1f767294dd567f16d226e80dcc Mon Sep 17 00:00:00 2001 From: Markus Neteler Date: Thu, 10 Oct 2024 20:41:09 +0200 Subject: [PATCH 362/471] docs: minor fixes to g.gisenv, i.atcorr, v.centroids, v.overlay, v.type (#4484) - g.gisenv.html: add EXAMPLES section (MEMORYMB, NPROCS) - i.atcorr.html: explain missing acronyms - v.centroids.html: start intro with what a centroid means - v.out.svg: fix GRASS_NS URL (fixes #4474) - v.overlay figures: colorize selected polygon in yellow color, not grey (sync to v.select) - v.type.html: explain vector object types in intro Note that the v.overlay figures should be redone to improve their quality (in a different PR). Co-authored-by: Anna Petrasova --- general/g.gisenv/g.gisenv.html | 29 +++++++++++++++++++++++++- imagery/i.atcorr/i.atcorr.html | 12 ++++++++++- scripts/v.centroids/v.centroids.html | 12 ++++++----- vector/v.out.svg/main.c | 2 +- vector/v.overlay/v.overlay.html | 8 +++---- vector/v.overlay/v_overlay_op_and.png | Bin 48720 -> 56006 bytes vector/v.overlay/v_overlay_op_not.png | Bin 33159 -> 40911 bytes vector/v.overlay/v_overlay_op_or.png | Bin 53214 -> 63574 bytes vector/v.overlay/v_overlay_op_xor.png | Bin 42262 -> 50253 bytes vector/v.type/v.type.html | 21 +++++++++++++++++++ 10 files changed, 72 insertions(+), 12 deletions(-) diff --git a/general/g.gisenv/g.gisenv.html b/general/g.gisenv/g.gisenv.html index 833e5051b36..4f5d9e165b3 100644 --- a/general/g.gisenv/g.gisenv.html +++ b/general/g.gisenv/g.gisenv.html @@ -149,6 +149,32 @@

    NOTES

    variables are stored in <gisdbase>/<project>/<mapset>/VAR after the current GRASS session is closed. +

    EXAMPLES

    + +

    Cache for raster operations

    + +The maximum memory to be used, i.e. the cache size for raster rows, is set +to 300 MB by default (GRASS variable MEMORYMB). To speed up +raster operations, it is recommended to increase this setting if enough RAM +is available. It is important to note that parallel processes will each +consume this amount of RAM. + +Set the maximum memory to be used (in MB), i.e. the cache size for raster rows: + +
    +# set to 6 GB (default: 300 MB)
    +g.gisenv set="MEMORYMB=6000"
    +
    + +

    Number of threads for parallel computing

    + +Set the number of threads for parallel computing: + +
    +# set to use 12 threads (default: 1)
    +g.gisenv set="NPROCS=12"
    +
    +

    GRASS Debugging

    To print debugging messages, the variable DEBUG must be set to level @@ -189,7 +215,8 @@

    SEE ALSO

    -See also variables list +See also +list of selected GRASS gisenv variables

    AUTHOR

    diff --git a/imagery/i.atcorr/i.atcorr.html b/imagery/i.atcorr/i.atcorr.html index 7c0b772e089..779ca7c7ef7 100644 --- a/imagery/i.atcorr/i.atcorr.html +++ b/imagery/i.atcorr/i.atcorr.html @@ -4,7 +4,7 @@

    DESCRIPTION

    map using the 6S algorithm (Second Simulation of Satellite Signal in the Solar Spectrum). A detailed algorithm description is available at the -Land Surface +Land Surface Reflectance Science Computing Facility website.

    Important: Current region settings are ignored! The @@ -507,6 +507,16 @@

    F. Sensor band

    Define your own spectral conditions: +Note that "wlinf" and "wlsup" refer to the limits of the wavelength range +defined by the user for a given simulation. Specifically: + +

      +
    • wlinf: This represents the lower wavelength limit (or minimum wavelength) + of the spectral band for which the simulation is being performed.
    • +
    • wlsup: This represents the upper wavelength limit (or maximum wavelength) + of the spectral band for the simulation.
    • +
    +
    FailedPercent successful
    207Worldview4 Red band (639nm - 711nm)
    208Worldview4 NIR1 band (732nm - 962nm)
    208AVIRIS b1 band (365nm)
    209AVIRIS b2 band (375nm)
    209AVIRIS b1 band (365nm)
    210AVIRIS b2 band (375nm)
    .AVIRIS b. band (+10nm)
    431AVIRIS b223 band (2486nm)
    432AVIRIS b224 band (2496nm)
    433Hyperion VNIR b8 band (427nm)
    433Hyperion VNIR b8 band (427nm)
    434Hyperion VNIR b9 band (437.16326nm)
    .Hyperion VNIR b. band (+10.16326nm)
    480Hyperion VNIR b56 band (914.83648nm)
    diff --git a/scripts/v.centroids/v.centroids.html b/scripts/v.centroids/v.centroids.html index 2bebed24e97..b814b702bab 100644 --- a/scripts/v.centroids/v.centroids.html +++ b/scripts/v.centroids/v.centroids.html @@ -1,11 +1,13 @@

    DESCRIPTION

    -GRASS defines vector areas as composite entities consisting of a set of +In GRASS GIS, a centroid is a point within a closed ring of boundaries. +A vector area is defined as composite entity consisting of a set of closed boundaries and a centroid. The attribute information associated -with that area is linked to the centroid. The v.centroids module -adds centroids to closed boundaries in the input file and assigns a -category number to them. The starting value as well as the increment size -may be set using optional parameters. +with this area is linked to the centroid. + +The v.centroids module adds centroids to closed boundaries in +the input file and assigns a category number to them. The starting +value as well as the increment size may be set using optional parameters.

    Multiple attributes may be linked to a single vector entity through numbered fields referred to as layers. Refer to v.category for more details, as v.centroids is simply a frontend to that diff --git a/vector/v.out.svg/main.c b/vector/v.out.svg/main.c index 4c99d148d04..4d29f845b29 100644 --- a/vector/v.out.svg/main.c +++ b/vector/v.out.svg/main.c @@ -25,7 +25,7 @@ #define SVG_NS "http://www.w3.org/2000/svg" #define XLINK_NS "http://www.w3.org/1999/xlink" -#define GRASS_NS "http:/grass.itc.it/2006/gg" +#define GRASS_NS "http://grass.itc.it/2006/gg" #define RADIUS_SCALE .003 #define WIDTH_SCALE .001 #define G_Areas "G_Areas" diff --git a/vector/v.overlay/v.overlay.html b/vector/v.overlay/v.overlay.html index 2b2a35be8c8..0077f2915f5 100644 --- a/vector/v.overlay/v.overlay.html +++ b/vector/v.overlay/v.overlay.html @@ -97,7 +97,7 @@

    AND operator

    v.overlay with AND operator
    -Figure: v.overlay with AND operator (selected polygons in grey color) +Figure: v.overlay with AND operator (selected polygons in yellow color)

    OR operator

    @@ -113,7 +113,7 @@

    OR operator

    v.overlay with OR operator
    -Figure: v.overlay with OR operator (selected polygons in grey color) +Figure: v.overlay with OR operator (selected polygons in yellow color)

    XOR operator

    @@ -129,7 +129,7 @@

    XOR operator

    v.overlay with XOR operator
    -Figure: v.overlay with XOR operator (selected polygons in grey color) +Figure: v.overlay with XOR operator (selected polygons in yellow color)

    NOT operator

    @@ -145,7 +145,7 @@

    NOT operator

    v.overlay with NOT operator
    -Figure: v.overlay with NOT operator (selected polygon in grey color) +Figure: v.overlay with NOT operator (selected polygon in yellow color)

    Overlay operations: AND, OR, NOT, XOR

    diff --git a/vector/v.overlay/v_overlay_op_and.png b/vector/v.overlay/v_overlay_op_and.png index 3d4bb3a07ffa2b09a6a6e1191549f12d994f925c..c545f2140ad461fc5d436123d2a902b9d336713b 100644 GIT binary patch literal 56006 zcmeFYbx@p7vp9UXQpSSr>DC=-Tk~(RhGpBlY-&k;4tOoq}1Wy5Om?-;1fXz zKnwPyKnU<(x38wIyE@d1+S%2~(#GC`+TF+5g4)8{#u5(Bd&WUk4Xc|Pbhn8wgn+0jco5y683ri{25j67z0AjjYpSR@Vnr((l4o@C^o-~^*?QGy~pccd;3*Rt!-TV zms}sMzBy5?w_+J3k+$OeH-8@A$g24WdP@0@xD^^oiui?dh%NN{nglrPV6p3Wtx-o6 zg?H3-sJMRPaKe!n`*>l4m0#3bEakYLgFBc{tMUZU(A8Gr_Z1gp6p0_N%7p0LqcdK}!;gvv~eimM0P zw3)Y1m&3o$bd*05!I_xULj3Gz@<;g#7qu|h(C#R6$v@kIgUg=%g_SrK;_LSKI^Ky% z2fQ^jlU@ib;+kd{CX4`PS^br7@g*;YdHK3@K4RGSbgK{~FsTI?-{q}prv!{fKF%8T zL2N!ZFHR;IK@jX_Z}Ms4E?V?5yZk*Fm7OA3D*xukiAZF=6_Kq_&PBkUPaWQoaChqX zv6Cir2hU%K)nN-Hq3`T6|GQSWaW3+{=$&nuM;~f$6M1W{I0Q4GVaeELZK8Ga6#fGf zPDeUgv3u&Siu0~{Qdz6~H(x=M|Im=Ka5HnYadx+Ha-@Ew2{m={a2KYf1)fv?$NC(c zm6ZOIyrbK{xB&2j-5cu6&cOy@cW_|;_YrRH(w+d3e=X?$IKoX6xSs6l7H&=+u4Wd} zo)(VobpKAm-0VNcJA1g=KW)d{jNQWC!U5>&2F%Lw-=>t6S5p1Y5sxgevT<;J8U--> zzx8ytvHUM){kOF}K6%>Czb^zB{-1RJTkrqa`%`D2my(j8l#`jqS$*1 zND3g%W&_Z%N_tlR<|rmXyY76PoimS*N=y!=q$k>$UUP;s>Zq!McX z@2h$wWe$)s2SyW##7Ng|M1(aR{*T^YELS3J5?r1^D=%NST`n$~d_? zK!M}5ae!J`usb_iJw142xS+VIyf7^%8|1%QRPCYemcRgET16X25AXkaplRb^q2UgF z&e{4>!LJ#7nDH$V~}X>vf=xc+(acr1dzW&p-QALSGv@I)5a zi=d>d1=QWiRny7IUYPcg1oh*{|9q?jTqkp=J5&nlZUGR5aB>TBK!6`j;5Rp)ASZ+w z!YK&(xBgD%HkLmBU%ekM5A~~mj$F>h4Vd5Osp+2^rD5Un&$oZR+S@!`C2H!YOCbm~ z`)3PoP)`ftx)SJaG82lev5uf@yS;_(y! zhOu%$fFDhW0KhFl4xWE8jQ#)q76I`vwcz9BU^R!DSpw3|W68>I&dI?FwY0S0=I7$% z~_J2D3n;r#N^G_R~p8<8x{-65(UpxcW z`+xKEFLC?7IR!QK|4#Bh;_v^m>wnqxKjOgusPq3y*Z;EXf5d_RQRn}auK(ZI1^zF@ zXyFJrdR_n?%qp4I0k9;Rv)mgu0B(MN{0E=Pj70`CqPoi~NuzFo!60_R8v-IMI5=uJ zc`0#C@0q=9A8*a|=?76OOU`Kqb2=Jyv=A|EkUog|hQ65PlUA8_nf7c$N4s`_zJqaj zUH$k(b8Ved9orvm7Z`;_auwc(a2}Rvx!(4}S@Zgn+k22C9!-+PH`qy2W21@Xuz*_* z*XC8uP2hnvt^{?mTqLUms9vB` zE-bPNJYH)0L)r!H7&8N3*akL-{GMLv`0rmdG&B+p({OT!xA+oor8u9fYSp#2wtoHk zwZ6Xo%a<=zRY$5b`DP?NU|XNTB@h7uktxp?w8`P^1@iZ{d?{KIesv9vhwB|< z?9Wt;JZYqDaWPS!8W~HAVyL`5T)hP;;aQWSHnQl|-x_e*^>sy{J2ft&v>CBi#{M&$ zdcpf7p5gvdYQIrW0*0Ce4OZfWk5`SMm*pB3bR0evINAL5aNyO!ENSyxheS{S=~>^% z=lu5kzVf$nM8IXJau6=B3O4p&na}6OMGXsWZARZ!UjYPmgO${eAH3T!ALZhrA`6&)b>8xe5 zj5YaA3his~_gNX(7oBT2y^hg$Y`56?Q&J$C_+ga$86e7U!`t!IxSEMwOyao+nDP*6 zO@dj9;o!mPP~LDcf<~A)C{#cdj>MUK&g7MW%w(5Z*m&Wm!o+W)Gv}xEyF~^a&i7qk z6w**oQ3njrpTzgKnVz2BYXyafmd+vEYaKKm2nxb7w|OuFC>l}NTLLR9lxqZTm{D8$ zDG>wY62Y z%9+^WDJ^tbX0V(>;u*n!$HB^3La6B^3l&GzBSw3XhiHWZA`*wd>4D&lM?CyWog+a~ zNyVkb*Hv5H6b}t%hYM{d8$&It2|~Abck8LCPe(HUsl2?rzG4F2%L=^KZR#9Z$9(EA^Wo<`aM3xFotRHEwMlJke3eu8GaB8}73&O!1WdstjfE4Sbe8<{iz;<8L(> z?w1pTic3p5Gl!1#6h7m;e*R7YJzu$~AWKxEcLC8l~#g}7?F2J?L*m7OVe@Od`9hw+?*ac|`JX4dVBl(REZ zQr1>i>4e~(RyHX}8ZT3{pJ`VM$AZI~GEO-MC9L<=zn(sQpPNg5*SD3qF<)zPA4=xb zs^$sz;qgQ&JZS#xo&^v{M9OT&F!$&4DavxkcCB=EP+ZSW%eX5&*VNS9 zLgmc=6ic~cz6l>59xe{hz-LXt_;qp-4pN-VJSN(3DxbSQUnV2T){(U1*S-*vRW+c-$jwM&hLq@9$Kls=a5)$vPcPjxEtw{g= zHd*fRNOhWUd>j2vZ6Xedqv5|&j|IgP2V?Jmwc2!sxYo-3P1ak#oGb+1>;q;kGBQ%c zC-KFzHPU=3PR-HXvz(lqfM;D`Vu2+B^XpXHVrEU!UjD4V5BH}JKifIZhsV8!v$=o> z-o&q8DxShAPYV@{9qC^6pR!{AK8PR_-C@r>4~-#Wk#bOX<8;fJK2qFb$S)(R$l>*v z$=clrkBS;FPIy%8UE}XnE61S7c9pL1u`p1+PMWkFYETLMGu+Wn#Z_DEB|(3u_iFf| zE~cg{UC8Ko&z_l|=RXc~tsAkU8AQb7H~u2g8x*P_{ndn}?n@n&R#5E~Rc6QaPTBYE zx6ImK(*@iLzber^9va4jYk?MNo!%Fs?3)Z~^F|Atb5f${U`U1x-+t*rYBP_=7+>Z} z!Kzcse*KSFqL!AHfyH-gG1ftMyT$`*Npi%#1K|-LBYcET!dp6982-*nJ0slEfKELN zMnDcab=A9}6dBi&%+jmmd>yx4XMgS5w5d!I#FC65ZqKVP zDcP;S-f=pkUpbyoLNJMD(BkX?R)t|qTPNCN?__5YSmoo&#E!a#MYyPjZ@9&0~86P zmk)=Yo`*IMpY0WYA$OPz*ye|2Nzu6YcrE<(A4JCf_Ys@Bw>t(*h7i*%TTPF)^fopYi_v`&%3Nr)eVl`}=c6?@xw=cy*M&K7TfsPm-Yq z#;^3Rgj`fr?mxhLBxn=Rr z(cFA(J&soEZX?qv@OU714P_$+3C5$}I)n0q(F$KFg8rxI#=`6&CMG7bzkk4r=_&#O z!kUxflP@x(9Y3Ctk#T)K>ohqr{!^;w`HL4rAGvx42FiqHd3ku+j=Io;ZnqK(k?9dC z_g(N+LtK?{*Cp(JgX4+2(|dxhFA=d{QI~hy5za4nNAWq#dt9vo0~7! zE|Vz`@L%g)rjwm9#wO+2fuT~h4n_j^iVji6)`Qdg89qn~39MIDH-nQk>v3=v)M{jG z+~L0cJ3XDa$={n5ynKCjSm}3xc$fzgAbi$Wwe0PP>Y*w8=jQ-AjfpMS$1KI(!ayQt zRIjT1VXee0p?5cxbL<`WuBLn6^&Q*Ro2Mt`1zPLFt6E#qfj z?yrh_ca7~yElp-y5IQSTDE8Uv&V|;#Q0w8*`TT58WMaV5J*sxO-U~Y)W64wtcjaM9 zkq}pw=kJ!I$4v|U9JR9iw5s;?#IWy|Bxkqkzkc|dB`F{q=)d;SD(6j$=YBV~s6B*u z4McN;`}+B_$g7nVpIzB80Zij#*4+_AnNJIO+H-G`4?f(_cHHx_(Hv|Wi)N>#S#gtE zSm!@_v0X>y8fJBRzb={_if~KUI-R$zNm;$AKcJo5ZLD0bSWOBGSq!K21fJ{8EY}_% zyVgvzpvpfTt7UPiZJpl3$%E|_QmU&-qp7qLhjes{RQF(H$KmZw*0|gK&GN%S6|{SM ztcaB`mYmZ30x+JOap5DDHQ5g;B>h;@DIo2>ko*?r=)X9zS6{DI_Yy@P7JpgE47Q$} z3(?Wh0UD^at!*77|L7lMU{5lR1b@?c>k{8LVo}WInw*r=^y??xPc(&Oi^HZiR3kyi zg2V0aD}~k7)ihRpUfVy69~0RDqmzu`6AQrur?2t6>}6oAVbfrXJ5|T(#-Zs_cMu3T zaNqc;xQN-VUvG~F24`?t4G-}xvdGFk?hgx_7jS(V6?mEC5T}HN z9`KeO6OPK>%sy2smC7RE^Nn7E%~)Z;&H22~X0}F=aw^^Pr>R*_eeYNs=DwN!0E>!t zeE9I8xmj?dUv2y51Q{uqDjcfx&628*&8NkDvck}AvLc();x%yF8eLY`sGqhii4ZTd zy8HM+_dkN6mTzdw^s2dJd2FHORE2+d!^+^Qymo7x(5~ck#Cdy_Dq5*qEm8D-?5kpK zlH7C2w?q28e7=0Nh(hEET;2urr9&4yllN8i)Ss3<_&GH_gxvh&Vpwpt{{w2Q?d+J@ znChkUec8jct>Mea71*-lDuc03qXoLY%^e@!JtQc_1niU){T>@DD=Yg#{`v{K42~Aa z+L76!?RT^7nDAwV$RyQVuahJ;N~Ujoyl=hw6+6jxKUC8JV4K7g$w%8qOCnY8v}j~g zA|-ps+NI48KMf&Vq0uEgZhu#f8 z&akfafixkZ0m9@*3VmW~>gwv++Lq`whSQNX;&j{+=(R9@i2K%?W#qRe5eEEV3ka_1 zI-D^YU$i7ex#b5-%Odljnw!kZ_G{ZA0C1(Tr^RD`d35MPmPYa=BT^%z-067pP`hRNTX1Ib;0-$QxJ`no`4NQj;4EosI4kTad_&-QbcafZ`UP^{yFx6wBQXKT^db}) z{qsMpyi>5WwEXyGuRl-?USO2Te^8e6hAwKor$p|Nxda76cM~e`l}B~y_~zrO@`Tju?y&a^H`sdJ|Pr78RT01 z89%n|&Y6PpQG^BnR&p{jGTNRWK=_TGsyrH2@^(S;77cMsXVRZkwYh-zsqkqcgr<9-N-|R7BN0NIs%_34s-7dcWXj zD2it=mSu2JE7rUjxoMX5fb|Xku30iou{1YYo3fKV*ZpMT{lJ(;Lc!ye`t4?GXNL|3 ze=SW_?1ghmM|N?>|F-nJQ4ED&oPY%$-UaSb;+3^{gdQBNuj>*cM4ZVQD(Ky-FzWD0 ztw1Z-EGc68*X^}mC4;g`mXKG4HuI$6qtHF`C1-DTS`>Mqf;$vS936(6Cx)NW#WoU# zOD{?A9D(B5+N$`r1AhpDi%D2PUWm5L2Ai$(l@Z%1B*fgVi=2zOq{K3#x9F?me*|CoG+NJ`I!C|4hK1SNCV3%zoDA>edK6Opjo}-H*53{u&$8xU6HKe7b zEoVn*68=HP$UL^c?EF{h(~>XS?$g zJD+zJ+fQa2zc;2C1-|6tYpAQ!O8EFl=9mfyiq0fj{L`Ed13N z8th-Z3#os<A*4sx5wkg_h*$s4%XI51uq^=4|TY;gM)Bt&}*dF z1IN{IQMCd(@j4=w)KgN?bZpYb&o^VpF4@+K*0<)f44J$R-=^(dJ0Int7rAS2aFFmz z#E7F;?&L+H-5x+7qJPq;?~GY<6tBj zks%LexQ4Oz!|7peLVSESuR}&F(IcBCX@PUXesvlwDF4F`1j?@nTOc-JfkXJ3gdSyr zK1B^z*lq3XVt`3XMh=H}8HUZc1dmRQHUSgV1>s#6)fPV(oM;HMD!sHt<7Q%tchyGR)$!bafCsOJtmJ+D-TTE|+H zJ04&+qBps@+h?4y8jtvHC@C%NBG&lcG-x?+S80M$-@8ALm6j8^P>gvmyug{fu+y4& z6sGJDp=XV#AB7nv7BubTD8s?nrD8x^X@-K&*@|9O-ZU4Xp_jVYdYQRBR>*GHthqqS zX{$-wq%EhcywGChq_3;*q}_osAGCqOyhKloEK5TYvyX-yUq!^*?a7a_odhVMEig-4{OT7FwffJ{t-Imr!*OWE!{#OA|dvrYUD02qPKEG+tnSTr#5CQP!bcV1|#&ULI=KtzeW^se#UKrfVg-bdk{w5{8gYL#6%a< zW&~-Ytd`Ro#{&&e3FVo@qAsvdg zU9{I7l>c`5B4wb^w$uZYTtcYG)~76W#tCiFk)MM*8~Hx|hQt8giv^E0_weR+s9_=H ztnpDWb&9}*L`0Q-R`c+y@VKnLxs)R0@p4=#;+&11#k=KQ^46hR^Fk${qlk&J2I)uy z7`KX@QA6N70E21jR3GK8Zgg(0_5SYckiKK!`@PKxez3!tQSCPYp*Vorq*?Jbk1ADETD@2ANBoo5k zI6EZHj4#IN4SSa(g(N5`kY2H~7(@2`=*KeqL`O$^z8ddg>Z1U${uv){yWELgzh9@7 zIUR2hAdhfHc-|@Bi%1Y-f=#^Sdz5FTCh&84t~}}B15+KnuJOatS-F10#Kgqwmd8DG zX;leYbCX;U9az0?m=89n*Xgd3j8F#!tEnQtT*5*(k;1-MMVl$}iciAA`u#U0p3!eP zl_-Hii2jfoID@{b&ADWUM%oQDe+mxiddzMbevGCnBpC@3Vkl`F3FiFxJ7)>LtWFp5 zK7nHZiYx1dKjQUeB_*|q=`gZU@#rx7h<>OwR!+Da2p&&UjN;FEe=QI8tZP9VHC%<+ zMgBaX+uvOz1GdP#(bUmSAocc;L}P^_^*oRNWr`Tt=T7}2rdF(78aGg7R;n1D0ctQM zEe)KW@A|SmlFN`{j9|2Mybe{+%HNn6@T0S2ewjb>bzA>oyRgODe< zNw5tLg3=K(K9-i<%N|M$G;11i+RBk6_)c&kR?whHsNfN?HihrD8_(pIHSYB}uZIBo z_h@hIGy#0ZPB5r2Yg^hsTWdp_S4lQ znm}IHbozTS!^gLQmGY&i*m93oKX!}i@B1h~A z{Z#j3dc4V|Vey--`d(DR+?no(QK`<2zcWyw`I}LbeW!mGv#418%Mum`4m=Ixeay3p zB)GqllAvD_f>{x-7{lqW>r(9Y*JjSBDplm=n=BEnK1%~pE-t+trsG6^E*FE|SoZd{ zCBue`Q1%YS)1P>SlAKa7>quR;U|Q}^@4;Z}kop1nj4H6H$+-_Qfh4OHE%FWx25CO! zPYGiF5On?!!Z`{ZQ91PFtd{~OQAGD6BO^G(#Kfeeq&PTvW7|)TT7Gm>UCGgRHY$eI z!+RXDAn)ib>Kk>A;`hrqpRojJUL8v85nt}W3pnG`hX|z^QP+y&NVXXa+ZKl#Sl3P} zjZZ~5YQOeV?ZnUSCM3jY#g+PdziJ2MO zo1B`cNRYv)%&Urq-(FL8wP03PF4rk+VK3Yc811gvvg2JTHuC$|5nL1fI{C z6k+>hyb2Mi*aYdl;V2nRYj@8n4jhpI9BUGq6*)tAFyWk7VliP{WU_7sODb!sR$-~x zgjq^n`R``l7GHEHQN9ki4mfOVY!#JXd3kxgy}jY#;XpF7rMdYD3?~#-mof^i#};O& z=a*`0lo>J8|B!CGShx;+We!^NEJ-%4Ghw$6?@NMkD|Wv;A~1&ufI@7o>su6M#n_6e z^GLwd_;7gP*ztXpxM1i<;Ulf>rNcJGR869E_^AB$O^dZHLFGF5jQw%#vnBNQ=<_oT zvU*)i@OkJ?)j zKeps-FJCFJxiCV(lIPFltbIL@D*0;XHzXbxLSfSK=lVTL7Yth&Cc7ZV4F3ovHmp4o7pn{R zC}N!G;|+MLvmlHYoj8JLVaD3QUXx5g3hj(hI_5lFyF;D|7kByaP}FLe{87V%i)_eg zuIJj6_donq2|VhhOT?NB5r&~`nvka@6#fnWh=aJ9hcY;V5r2ef4g)(=B-MV^oy2|kc=(~nKgFf^ z+ErS(cLXD5L84G$$2!~cGt4s}jQN{~DcL~5Q(wPK`S6o9`Kt?krSfs)A_ip;nJPhR zp68VzXgT8Z8E*Fb17!T}w4)a+!NK>&fdf%vCWy$cG>dHBj!Ql-Uuf^ef4nuJnxgKb z6yoYk&%+-U<7YX57BKGNw)W(~(NdhIwwfUCMZO}QE%Tmnt0K_UO<*0;HZo#+%#~bkACiN$S1j*;_HSuR%26ADR>GRA4NEt8`o&-DzY;PD6&WSdOO=gLBvX~vWGlq4K| zOH`k{PTWt9xh|swn;OrCU(VYB~?Y)&!yP+!GtMbJB&$J46f=hUe)vT|UdCi-5co*iIcL zZ&q4X_WsG^Tcc7+NK8~xUbvrg>i~4|bcg7R2e}$!lcb69*>Q%cA8z4gMU^xrwJ=f5 z&9%8Z;*Ymxv$EEDR97E+ma?=!e?|sy{H=Ns>AWSlkuGL^mqFaS- zCqEfI#iGb2;w zpKUN_DBRW-MvM6Kh4&>=pj|F+>`S`~)YY!gpp!u-l>P4pFbru({^poG1PKub^=fl&fiI|RK2{OL@K|QZoCg7*6&ERwm*2=q#9_<$vQU%m51NPkaYCWwdG11+_gXg1S zdAU9i4~>^87_vm<)+>59Seq3_c2+@|(=JZeqYNhJ`!PKaH#JyTcwFNWdl+hnp^j_E z4U|XpOC?5B(Y06Mcus|(^CCX$r=X*TCMxU(zA3u(u`du%By*bG|jKFls>1)vbrtjmIb54 z6cl%ziOSN6Hz>WVaek{0j)0AjQ&OtaRT?v0DYdozb>Q=GTJM!*Al!!V)7oA6z|j$i zDmExkaKq5IxBi1ii|9H#M$i|7>MdvyEBYKa)> zObIlL9@`>`GbPyCi0Zmmp(FTx!pQr_g0zCb=Vlh!}{Gf-Q^WH~*gn>s* z{B1y z-4pLlavxfcURzkK8+y&3^)m+@+`=k59BORpqOd4t>g*<`s?8J>6kIMj^Cw&+yrG38 zsH=InoYX`jQ9Kaayoxtue27b95#Yz1yY>SWInbXfUem*e)bfqSt^zmv_b|4L^sbne=^28XV*RiWpF_DfQZX z$BBg6^T4+*Yn)I~XfO#5W+s$*FjUQRlZ=AgmsFO0weC9!|rixIFGu@zJ7xvm3 z9$B-hOCHi~eH5wMIKo(k;~ss5;N^UHy_p}3t{99qf^wzm)Ib!H40+Z?GiEfx*68+f zQURR*rLMFTOu|B!>^%NKi;GVyO*xkwdplrJKOi8$!{ZFdTv!dJR|8qYgg)Z5@t~En_&?dbg^pRDyUA?qOI3vY)Es$KBK7o_yg$!yM^Y z&M}#8$H~Q|r8REN{d8MJNSR~f>b{IJKcg^X(k({`gP!e-{}R6NS7~Tl-!O*PpugMo z4?0=q;kfKZU_>6L9Gb!a9AF&l-~v^U2F)NYVJU-DCzKzAX40rxoIsG#+3jLTA!z%K zOA|&Cy(6QRj=_)0I$|9p)d-P7k=w)ZFGFVa4n34E_#DF95{mVlk~_}E`!MVRh-@A< zt|m-PP35yc=4IaHjH>2{c1g1ssx2k8wcQ>wd(GX6t)y&jufE#tY|G(%!O!MIeEK-zQM3DaOMR}HW@!lB0T5!wRI2m=Ev&k1dVlK zX6;q8o}GV)AyOQd9KkSy+FHT1*>=~~XlcGi48pTh=gBImav5?N-|dZ;&(>EM)_89J z{vzac5Em2Eyj__2h@iSD%clPr=x{ig+OMSWQPioR?!D%}h8goMy}8|APzXS!GDJd> zK=~|zvIT=1@LSPa`*bg>osP-ojB&jDTrv2|`BENdaVQs$kbV7 zo3l0Db4j<&clppZ2NjnPUB<9EaWL-h5cp(9(n4PDXp}s*hJ)^Ss=(uQ1~eD64!?6R zpu~?#kmTa&RC(k`ZJe9W&fkd~*aqz+QU>jrJ3AktQu#+0WaKoQ*5|{1Y{X)(1q*r; z=nD4dD?~*eUsa*=h7$giz>kCTYRUY|x#7x@65-S02TFq^ z*Cr-0Tj*vF z{QJF>Ou4?c_SNZ#q>W8f&}BCjApya>o7F}v`+bR*kpHIfFVV9<@8Q@2bYJbhenS?u zW--z!r;Fm%^*SA{sS1sgZHIwn@2qne=B*0~nIG0ChIOLl!(lUq2UJn*KzC9%EYa~L z1=qyf(uP%}G5TzC>Eu}zl~h!y1SJ>^3!ceeY%pu_@J?L%>==9J^L?tb8F;wgPQ2OO zZD7-_S^~_mE&F(OPEN(x_Hd4H3y`?(q1=Tnv_IVW)qMK~RLp3N?^@Pe`KOw*M+w%k zNdBh1CEQ={XQHO4mt>Uc)@>20z^Iqx9^87Pk0PxVZiJEbFhcbqj4(?K{hIkl?^ND1 z^}JrAHx9IMJ9|A9k*}nxFI6;$N1#X#eb;G#7Z1srIjYxQ|00_J-uqFX2$Z&z>vw=b zAn(hfhWV2=h8g3z>YM5|zm9Ycv!8(B!bCyb+uB$Nyl7cGn7#P*Qll`AQdqBG)Z5$p zcISt2^3vAymBjjbz6u;muoz<~g(_O|61uE7wwo^{*Y-qRWu0jFNDYHvHg-`SRw)ez z{S+0#PZ-8coMQ+}Z%Fl$sGQb%qdlUPm&oN&*v`(*%|Q!0H}~mE@kbyL0u%%cW(!T4 zK)AWNFK=$>0Aof1mY7oiK)|z0e_1L^#-?A-E&6cs`SWM5g)1U|x8<{-cCC`JZOUu* zMS+^Y#R7`L&!45Fr2{UPWr>Wg#$)*x?uy~@x=UoVfg4-kjPMPH7-M4N#L4o51;#hB zq)Iod<}CrhO2%{qGitbRON_cO~*ReIsH{Igpq^bF*GUyKDjv{F#&Q$ArUTVn! zD;uQW=%YQM_xWp(2jm&JnIYWch(J>e8A<4q?5lZDD|c|Yq@l3Y_i_X>1Q!tut<|li zmrxy#5AkEz;}m$TjlN;&-xg0{STTpuYZX@@_jo2 zM=$0Vd9mwnr7YqO&wagk8MlMErr*DRfBW`rW@hI4+PBTfiM_bduwdE-LJSy&dT4hK zMa!|#yM_hd8&py6r7vkl$7g=asMrQgE1tkH$;p8bms(w=i%oTnSm7D+;AJj6BQ>Z? zcQInl4ZP8dM^N-Tkwil$^DV`~tAsm#pn`=io#uu7g}aj!v$py#macQ1*fzj4--BGK z>Tn{reaAM|hBOKln-1G=va+*p=rD~ReN7A7Shv2~?dvTudIPO#B1hKkoIJ`HlOfojs(&DikOb@}4!1l}E z1d1yE9uObxmlJ~+_f0+RBO^~77E>*yb&+zaepU!OAX=;0Gf(x?mHA3oZihc zA0w$-GT(H^206R9G~J!g&d<#~n)5I*$DP8)#>>Y!bI%|C_T;kY)gI;FoM2P7b} zc#gaG_$tbKRZAvRSFF~ygZ=jmDnK>rBU}kqPxh0jR0}+>$-TG;K|n;rrV{aAZ_?93 z?V1@8XRgI)DAPjwfipAvt*l73{w05RYivyDgcvDEZby*A#boXsQ%cuM&{p{ZwycJz zBDbRBX@{%j6Z0_bRpNBu%D9Nzx9Odn3q2+vMD z&#crR84&FaLx>KcmkZ$rvWQhA37u zc%dfX#$a4t7!5z1ahF@q<+^@lrIx)tp<|^dBST~XxEjG-{v$gy7C@QSWj(@8@c^+?yMV*_*FB3lc?+|MpNd z)Y=1uZqsv3ZV&et9hC+?V`;{Ydy5!g9S|Y_Sb0Bpt4G64F5n;S5fRu&nDe8fG`2}P z%^Kk#yh*}%yM*`yf}7%)yKO&(b{K{}7~Y6A|2$=&@Kt??@rC;j5N{*iPyXTW2H3+@ z_?|H3o3Mx(fe1}h!RS!!OAO1d(*+R+{+PeDgN|1wJ%Iao4@*3J_-lNBO!TyLwG1EY z@Rl)x_j5PO04U7hv0K8Xi0{B&jGbx^@ROH+YgBo0B$vZ&^SfW`B=D&7m&tphhu=WO z>q3WT{;*NIIpH8OfoG@q6!w_Oipd2xjwM_t@K z))^=V&&6VyMDba^7>x-PLK;v1zHfT^QRY{??Flu^ES7ISjxD=)P;R|tpsXytUQ1{> z3DGg~Y5MfZ6nH~KOUvf|{;EJG{`KqE*UKuL1w8?X4D9$bfosXK`uagPKzZ$g*W$%u zpdSSyUQrdO5Xc6C*&r^eT2&~X5@d5o;#kLjBE*RU`fliPX_u(~h+$@hSw^E6vsLeS z5|@fca7J$}*za%TaXCcZsiF?B_j8SQ_g1WXg4e{ZP0(r15$sWvEplg${zgip6CtCZ zoC}T`J*s${9cJ+0P+g7X;>aQ@P;Bh#;SsQ}b=cfIi$!tmW^HW^*v)vpg-akZWN2CT zaJR@Xv|-%#_a_;)$R(n8_g~`ygpJr8LmwD-afk+_-{mi<@E0skNoE$;EbvNW%oLx5 zur3WImL$p7y)0kr2n}bXXEexQF=XM!P#q#%KJfQWdO1WQ=g1L-ZRUBm>YQNOba)zJ z@-|;IzSN=K0 zOM$kNF*IM`fE_?qH0Sz(+0o49jPin#Inm~lD`W(rjj31>cL5)Evdt?g_|Um9}rz^mV!i6L*!DbfSAF~2i1 zt&FUV76SqwZZ;JQWsQB$USLU>I!*Qss!ldxRwASVi5jVGBr_O=It@YX5;`ntCV3kCZUaC64b`96w|Qs@A;=PWq^>aV0R;K9ihS_=L=b(*U|1*{j=nhR zS6Q+yCAnYS1+K0FSJV_rx#1Kj*yiAMtskj2=akkw0GFg-NfW_2%q|4x36&;4#+Z0yk7=QQ$x4 z*s^;E&p#1~NyR{~Y+Pe`W|bSTFktB2r9K6gNgwNeUL(_6o$0Qeji{bd!R%RINmNf| zjbblVawt!<=-L}0X|lI0p+i^O^$n_Jujj-LPuDq5nOM*w*BfD2Arj_Fj5Y>6{c_4e zumeM2pnldomMRy4aQQ6MMBE9zx*mNQdC%dl7z3-%`mLm_k@z&FuIo%#x2kKvc zGbonFzaWXS{2sCBcJU!Zz*KGYj*QRom5V7*T)N#hb^B4YQwawoyT7aGI4=j+5K1yE z7CCS0g4!?~RMB|JV1nM*C7dV5oE03Ogn)1>^vb^BqJ}m2*D`gA1e{{19x?iK5=O~V zpqjFmY�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 KgCc}!@c#iUi@)~( literal 48720 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 diff --git a/vector/v.overlay/v_overlay_op_not.png b/vector/v.overlay/v_overlay_op_not.png index ac7eb441bbcb8da3025e231801337d5c28c8337b..2c903f22d6071a1357a3ab3b90942bfc8ef99a5e 100644 GIT binary patch literal 40911 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 --> - + - +
    #=g&t)Ma9L%MJGmQ)@QbuTU0NV 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 KF?nW$#{559Z_b1O diff --git a/vector/v.overlay/v_overlay_op_or.png b/vector/v.overlay/v_overlay_op_or.png index c19bd32ce9f09244b0523d36220c0e9afbda6ff4..bbde7b06eb1eaa3f6e76eb190e805459bbcd6dc6 100644 GIT binary patch literal 63574 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

    2b;;H($19zc;m^mN#p>juk$~^5Qd?|N9jant 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 L&5iCG*oXZeHz)d< diff --git a/vector/v.overlay/v_overlay_op_xor.png b/vector/v.overlay/v_overlay_op_xor.png index 446624f36ec744c8ccc50c73eeb3da975955d58d..bf5bd5b8e4581c26e1c0ef2d6f66b5259f2dc647 100644 GIT binary patch literal 50253 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 literal 42262 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 diff --git a/vector/v.type/v.type.html b/vector/v.type/v.type.html index 8e6d5806db8..124f8f39fe0 100644 --- a/vector/v.type/v.type.html +++ b/vector/v.type/v.type.html @@ -2,6 +2,27 @@

    DESCRIPTION

    v.type changes the type of geometry primitives. +

    +The following vector object types are defined in GRASS GIS: + +

      +
    • point: a point;
    • +
    • line: a directed sequence of connected vertices with two endpoints called nodes;
    • +
    • boundary: the border line describing an area;
    • +
    • centroid: a point within a closed ring of boundaries;
    • +
    • area: the topological composition of a closed ring of boundaries and a centroid;
    • +
    • face: a 3D area;
    • +
    • kernel: a 3D centroid in a volume (not yet implemented);
    • +
    • volume: a 3D corpus, the topological composition of faces and kernel (not yet implemented).
    • +
    +

    +Lines and boundaries can be composed of multiple vertices. +

    +Area topology also holds information about isles. These isles are located +within that area, not touching the boundaries of the outer area. Isles +are holes inside the area, and can consist of one or more areas. They are +used internally to maintain correct topology for areas. +

    EXAMPLES

    Convert lines to area boundaries
    From 3a95b7a4e6412510d64b4a36a0a18bd7e7843d31 Mon Sep 17 00:00:00 2001 From: Mohan Yelugoti Date: Fri, 11 Oct 2024 02:32:11 -0400 Subject: [PATCH 363/471] raster/raster3d: Initialize Cell_head structure contents before use --- raster/r.in.lidar/main.c | 6 +++--- raster/r.in.pdal/main.cpp | 6 +++--- raster/r.in.xyz/main.c | 2 +- raster3d/r3.in.lidar/main.c | 7 +++---- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/raster/r.in.lidar/main.c b/raster/r.in.lidar/main.c index 68d3a0fcd1b..96f562167b6 100644 --- a/raster/r.in.lidar/main.c +++ b/raster/r.in.lidar/main.c @@ -51,8 +51,8 @@ int main(int argc, char *argv[]) struct PointBinning point_binning; void *base_array; void *raster_row; - struct Cell_head region; - struct Cell_head input_region; + struct Cell_head region = {0}; + struct Cell_head input_region = {0}; int rows, last_rows, row0, cols; /* scan box size */ int row; /* counters */ @@ -100,7 +100,7 @@ int main(int argc, char *argv[]) int return_filter; const char *projstr; - struct Cell_head cellhd, loc_wind; + struct Cell_head cellhd = {0}, loc_wind = {0}; unsigned int n_filtered; diff --git a/raster/r.in.pdal/main.cpp b/raster/r.in.pdal/main.cpp index da32dcb5484..36d09558eea 100644 --- a/raster/r.in.pdal/main.cpp +++ b/raster/r.in.pdal/main.cpp @@ -69,8 +69,8 @@ int main(int argc, char *argv[]) SEGMENT base_segment; struct PointBinning point_binning; void *raster_row; - struct Cell_head region; - struct Cell_head input_region; + struct Cell_head region = {}; + struct Cell_head input_region = {}; int rows, cols; /* scan box size */ char buff[BUFFSIZE]; @@ -81,7 +81,7 @@ int main(int argc, char *argv[]) bin_index_nodes.max_nodes = 0; bin_index_nodes.nodes = NULL; - struct Cell_head loc_wind; + struct Cell_head loc_wind = {}; G_gisinit(argv[0]); diff --git a/raster/r.in.xyz/main.c b/raster/r.in.xyz/main.c index a4c7dfde6ed..b3c3fb380cf 100644 --- a/raster/r.in.xyz/main.c +++ b/raster/r.in.xyz/main.c @@ -112,7 +112,7 @@ int main(int argc, char *argv[]) char *n_array, *min_array, *max_array, *sum_array, *sumsq_array, *index_array; void *raster_row, *ptr; - struct Cell_head region; + struct Cell_head region = {0}; int rows, last_rows, row0, cols; /* scan box size */ int row, col; /* counters */ diff --git a/raster3d/r3.in.lidar/main.c b/raster3d/r3.in.lidar/main.c index 6170a3f50ba..3cf91189d24 100644 --- a/raster3d/r3.in.lidar/main.c +++ b/raster3d/r3.in.lidar/main.c @@ -361,13 +361,12 @@ int main(int argc, char *argv[]) /* for the CRS info */ const char *projstr; - struct Cell_head current_region; - struct Cell_head file_region; - + struct Cell_head current_region = {0}; + struct Cell_head file_region = {0}; G_get_set_window(¤t_region); /* extent for all data */ - struct Cell_head data_region; + struct Cell_head data_region = {0}; long unsigned header_count = 0; int i; From b9a6b6883899e7611a2d65b63a1c02d6a50d59e4 Mon Sep 17 00:00:00 2001 From: Vaclav Petras Date: Fri, 11 Oct 2024 10:21:22 -0400 Subject: [PATCH 364/471] doc: Use lowercase for 2D raster mask (#4401) On many places (more than covered here), 2D raster mask is called MASK conflating the concept (mask) and the implementation (MASK raster). Users using the r.mask tool may not interact with the underlying raster directly, so there is no reason to use MASK over mask. This is changing MASK to mask and modifies related wording. However, this is also leaving many places as they are with MASK. Some will be better revisited with or after #2390 and #2392 when a more comprehensive solution is available. This fixes and keeps in sync wording in r.null and r.external, and moves r.circle comment documenting the interface to a flag description. --- display/d.histogram/d.histogram.html | 2 +- raster/r.circle/main.c | 6 ++++-- raster/r.external/r.external.html | 8 +++++--- raster/r.null/r.null.html | 10 ++++++---- raster/r.stream.extract/r.stream.extract.html | 4 +++- raster3d/raster3dintro.html | 2 +- scripts/r.out.xyz/r.out.xyz.html | 2 +- 7 files changed, 21 insertions(+), 13 deletions(-) diff --git a/display/d.histogram/d.histogram.html b/display/d.histogram/d.histogram.html index 6e99c940290..43cca2d7223 100644 --- a/display/d.histogram/d.histogram.html +++ b/display/d.histogram/d.histogram.html @@ -10,7 +10,7 @@

    DESCRIPTION

    NOTES

    d.histogram respects the current geographic region settings -and the current MASK (if one exists). +and the current raster mask (if mask is active).

    d.histogram uses the colors in the map's color look-up table (i.e., the map's colr or colr2 file). diff --git a/raster/r.circle/main.c b/raster/r.circle/main.c index 4b41a78c7c6..bd99a292f71 100644 --- a/raster/r.circle/main.c +++ b/raster/r.circle/main.c @@ -79,7 +79,9 @@ int main(int argc, char *argv[]) flag = G_define_flag(); flag->key = 'b'; - flag->description = _("Generate binary raster map"); + flag->label = _("Generate binary raster map"); + flag->description = + _("Generate binary pattern only (useful for creating mask)"); if (G_parser(argc, argv)) exit(EXIT_FAILURE); @@ -114,7 +116,7 @@ int main(int argc, char *argv[]) "using the binary flag")); if (flag->answer) - binary = 1; /* generate binary pattern only, useful for MASK */ + binary = 1; else binary = 0; diff --git a/raster/r.external/r.external.html b/raster/r.external/r.external.html index 797cc5ea605..f20c3e059d6 100644 --- a/raster/r.external/r.external.html +++ b/raster/r.external/r.external.html @@ -17,10 +17,12 @@

    NULL data handling

    NULL cells are those whose value matches the value reported by the GDALGetRasterNoDataValue() function. -To apply the GDAL-linked the user need to either create a MASK (e.g. -with r.mask) and then "apply" it using e.g. r.resample, +

    +To introduce additional NULL values to a computation based on a GDAL-linked +raster, the user needs to either create a mask with with r.mask and +then "apply" it using e.g. r.resample or r.mapcalc, or use r.mapcalc to create a copy with the appropriate categories -changed to NULL (if() condition). +changed to NULL (if() condition).

    EXAMPLES

    diff --git a/raster/r.null/r.null.html b/raster/r.null/r.null.html index 0a98907af5f..4d92b027d65 100644 --- a/raster/r.null/r.null.html +++ b/raster/r.null/r.null.html @@ -49,10 +49,12 @@

    External maps

    From the r.external documentation: GDAL-linked (r.external) maps do not have or use a NULL bitmap, hence r.null cannot manipulate them directly. Here NULL cells are those whose value matches -the value reported by the GDALGetRasterNoDataValue() function. To apply the -GDAL-linked the user need to either create a MASK (e.g. with r.mask) and -then "apply" it using e.g. r.resample, or use r.mapcalc to create a copy -with the appropriate categories changed to NULL (if() condition). +the value reported by the GDALGetRasterNoDataValue() function. +To introduce additional NULL values to a computation based on a GDAL-linked +raster, the user needs to either create a mask with with r.mask and +then "apply" it using e.g. r.resample or r.mapcalc, +or use r.mapcalc to create a copy with the appropriate categories +changed to NULL (if() condition).

    EXAMPLES

    diff --git a/raster/r.stream.extract/r.stream.extract.html b/raster/r.stream.extract/r.stream.extract.html index e1c427599e8..22877936c93 100644 --- a/raster/r.stream.extract/r.stream.extract.html +++ b/raster/r.stream.extract/r.stream.extract.html @@ -126,7 +126,9 @@

    Defining a region of interest

    The stream extraction procedure can be restricted to a certain region of interest, e.g. a subbasin, by setting the computational region with -g.region and/or creating a MASK. Such region of interest should +g.region and/or creating a mask +with r.mask. +Such region of interest should be a complete catchment area, complete in the sense that the complete area upstream of an outlet point is included and buffered with at least one cell. diff --git a/raster3d/raster3dintro.html b/raster3d/raster3dintro.html index ac476b974b2..b51748b4dd8 100644 --- a/raster3d/raster3dintro.html +++ b/raster3d/raster3dintro.html @@ -115,7 +115,7 @@

    Conversion from 2D raster maps

    2D rasters are considered as slices in this case and merged into one 3D raster map (r.to.rast3). -

    3D region settings and 3D MASK

    +

    3D region settings and 3D mask

    GRASS GIS 3D raster map processing is always performed in the current 3D region settings (see g.region, -p3 flags), i.e. diff --git a/scripts/r.out.xyz/r.out.xyz.html b/scripts/r.out.xyz/r.out.xyz.html index c9b2b6510be..e603c592e32 100644 --- a/scripts/r.out.xyz/r.out.xyz.html +++ b/scripts/r.out.xyz/r.out.xyz.html @@ -6,7 +6,7 @@

    DESCRIPTION

    NOTES

    This module will by default not export x,y coordinates for raster cells -containing a NULL value. This includes cells masked by a raster MASK. +containing a NULL value. This includes cells masked by a raster mask. Using the flag -i also these raster cells will be included in the exported data.

    From 2e0a8fa3359d062472fdf1a915be55f355e469d3 Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Fri, 11 Oct 2024 10:48:51 -0400 Subject: [PATCH 365/471] tools: FIxed unused variable in imagery/ (#4487) fixed 821 --- .flake8 | 1 - imagery/i.atcorr/create_iwave.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.flake8 b/.flake8 index 313241f2d9d..1f5f4c9c040 100644 --- a/.flake8 +++ b/.flake8 @@ -21,7 +21,6 @@ per-file-ignores = # E741 ambiguous variable name 'l' __init__.py: F401, F403 man/build_html.py: E501 - imagery/i.atcorr/create_iwave.py: F632, F821, W293 doc/python/m.distance.py: E501 doc/gui/wxpython/example/dialogs.py: F401 gui/scripts/d.wms.py: E501 diff --git a/imagery/i.atcorr/create_iwave.py b/imagery/i.atcorr/create_iwave.py index a575d8089cd..21faa21ac2c 100644 --- a/imagery/i.atcorr/create_iwave.py +++ b/imagery/i.atcorr/create_iwave.py @@ -235,7 +235,7 @@ def write_cpp(bands, values, sensor, folder): while c < len(fi) - 1 and fi[c + 1] > rthresh: c += 1 max_wavelength = np.floor(li[0] * 1000 + (2.5 * c)) - print(" %s (%inm - %inm)" % (bands[b], min_wavelength, max_wavelength)) + print(" %s (%inm - %inm)" % (bands[0], min_wavelength, max_wavelength)) else: filter_f = [] From 21606ad863060c018e8b1b26f0fef4f693ae97da Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Fri, 11 Oct 2024 10:50:31 -0400 Subject: [PATCH 366/471] doc: Ignore F401 in dialogs.py (#4488) dialogs.py has an instance of F401 error for unused import of grass library. However as mentioned in the comment and post discussion with @wenzeslaus I decided it would be better to ignore this error for now in case it breaks any underlying dependencies regarding gettext.install() --- .flake8 | 1 - doc/gui/wxpython/example/dialogs.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.flake8 b/.flake8 index 1f5f4c9c040..f0c1f634e81 100644 --- a/.flake8 +++ b/.flake8 @@ -22,7 +22,6 @@ per-file-ignores = __init__.py: F401, F403 man/build_html.py: E501 doc/python/m.distance.py: E501 - doc/gui/wxpython/example/dialogs.py: F401 gui/scripts/d.wms.py: E501 gui/wxpython/image2target/*: F841, E722 gui/wxpython/image2target/g.gui.image2target.py: E501, F841 diff --git a/doc/gui/wxpython/example/dialogs.py b/doc/gui/wxpython/example/dialogs.py index 425ea57a4b2..98760e7202d 100644 --- a/doc/gui/wxpython/example/dialogs.py +++ b/doc/gui/wxpython/example/dialogs.py @@ -20,7 +20,7 @@ # So we need to import it before any of the GUI code. # NOTE: in this particular case, we don't really need the grass library; # NOTE: we import it just for the side effects of gettext.install() -import grass +import grass # noqa: F401 from core import globalvar from gui_core.dialogs import SimpleDialog From 426bf742123680c4067ab930a758cdb1ddbc9624 Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Fri, 11 Oct 2024 10:51:20 -0400 Subject: [PATCH 367/471] checks: update .flake8 to reflect correct status in image2target (#4489) --- .flake8 | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.flake8 b/.flake8 index f0c1f634e81..c01dc46b255 100644 --- a/.flake8 +++ b/.flake8 @@ -23,8 +23,7 @@ per-file-ignores = man/build_html.py: E501 doc/python/m.distance.py: E501 gui/scripts/d.wms.py: E501 - gui/wxpython/image2target/*: F841, E722 - gui/wxpython/image2target/g.gui.image2target.py: E501, F841 + gui/wxpython/image2target/g.gui.image2target.py: E501 gui/wxpython/modules/*: F841, E722 gui/wxpython/nviz/*: F841, E266, E722, F403, F405 gui/wxpython/photo2image/*: F841, E722, E265 From cbc3ff4f349d258b875a23ce2c8a92f069a79226 Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Fri, 11 Oct 2024 10:51:40 -0400 Subject: [PATCH 368/471] wxGUI: FIxed F841 in colorrules.py (#4490) --- gui/wxpython/modules/colorrules.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/gui/wxpython/modules/colorrules.py b/gui/wxpython/modules/colorrules.py index cad74f38c4b..3dfdde8c42b 100644 --- a/gui/wxpython/modules/colorrules.py +++ b/gui/wxpython/modules/colorrules.py @@ -318,7 +318,9 @@ def LoadRules(self): int, self.ruleslines[item][self.attributeType].split(":") ) except ValueError as e: - message = _("Bad color format. Use color format '0:0:0'") + message = ( + _("Bad color format '%s'. Use color format '0:0:0'") % e + ) self.mainPanel.FindWindowById(item + 2000).SetValue((r, g, b)) else: value = float(self.ruleslines[item][self.attributeType]) @@ -410,7 +412,6 @@ def _initLayer(self): if layer: mapLayer = self.layerTree.GetLayerInfo(layer, key="maplayer") name = mapLayer.GetName() - type = mapLayer.GetType() self.selectionInput.SetValue(name) self.inmap = name @@ -1412,7 +1413,7 @@ def AddTemporaryColumn(self, type): modul = "v.db.addcolumn" else: modul = "v.db.addcol" - ret = RunCommand( + RunCommand( modul, parent=self, map=self.inmap, @@ -1430,7 +1431,7 @@ def DeleteTemporaryColumn(self): modul = "v.db.dropcolumn" else: modul = "v.db.dropcol" - ret = RunCommand( + RunCommand( modul, map=self.inmap, layer=self.properties["layer"], @@ -1505,7 +1506,7 @@ def OnAddColumn(self, event): modul = "v.db.addcolumn" else: modul = "v.db.addcol" - ret = RunCommand( + RunCommand( modul, map=self.inmap, layer=self.properties["layer"], From 1d42e580e25d62dee5f3e2653f7bcb5241855f71 Mon Sep 17 00:00:00 2001 From: Vaclav Petras Date: Fri, 11 Oct 2024 16:25:56 -0400 Subject: [PATCH 369/471] r.mask.status: Check mask status through a tool and function (#2390) Instead of using low-level test of file existence with hardcoded raster path and name, this offers a new tool to retrieve status of the raster mask. The new r.mask.status tool reports presence or absence of the 2D raster mask and provides additional details about the mask. There is one usage of this now and that's the one for a shell prompt. The prompt no longer relies on testing the file presence with the test program, but uses a GRASS tool to find out. The code goes out of its way to report both mask name (currently always MASK) and the underlying raster name if it is a reclassified (without rewriting the current C API). This is to mimic the existing C functions which are returning the underlying raster if MASK is a reclass. The tool and the new C API function return both preparing a way for using an arbitrary name for the mask while having the option to look at the underlying reclassified raster map. --- include/grass/defs/raster.h | 1 + lib/init/grass.py | 2 +- lib/raster/mask_info.c | 120 +++++++++--- raster/Makefile | 1 + raster/r.mask.status/Makefile | 10 + raster/r.mask.status/main.c | 184 ++++++++++++++++++ raster/r.mask.status/r.mask.status.html | 65 +++++++ raster/r.mask.status/tests/conftest.py | 25 +++ .../r.mask.status/tests/r_mask_status_test.py | 126 ++++++++++++ 9 files changed, 507 insertions(+), 27 deletions(-) create mode 100644 raster/r.mask.status/Makefile create mode 100644 raster/r.mask.status/main.c create mode 100644 raster/r.mask.status/r.mask.status.html create mode 100644 raster/r.mask.status/tests/conftest.py create mode 100644 raster/r.mask.status/tests/r_mask_status_test.py diff --git a/include/grass/defs/raster.h b/include/grass/defs/raster.h index c2d26ccdccc..7f358562c72 100644 --- a/include/grass/defs/raster.h +++ b/include/grass/defs/raster.h @@ -392,6 +392,7 @@ int Rast_option_to_interp_type(const struct Option *); /* mask_info.c */ char *Rast_mask_info(void); +bool Rast_mask_status(char *, char *, bool *, char *, char *); int Rast__mask_info(char *, char *); bool Rast_mask_is_present(void); diff --git a/lib/init/grass.py b/lib/init/grass.py index f489ad1fe53..d2d8301c52b 100755 --- a/lib/init/grass.py +++ b/lib/init/grass.py @@ -1667,8 +1667,8 @@ def sh_like_startup(location, location_name, grass_env_file, sh): ) ) + mask2d_test = "r.mask.status -t" # TODO: have a function and/or module to test this - mask2d_test = 'test -f "$MAPSET_PATH/cell/MASK"' mask3d_test = 'test -d "$MAPSET_PATH/grid3/RASTER3D_MASK"' specific_addition = "" diff --git a/lib/raster/mask_info.c b/lib/raster/mask_info.c index 1a11da972f8..42102de86dd 100644 --- a/lib/raster/mask_info.c +++ b/lib/raster/mask_info.c @@ -1,30 +1,17 @@ -/* - ************************************************************* - * char * Rast_mask_info () - * - * returns a printable text of mask information - * - ************************************************************ - * Rast__mask_info (name, mapset) - * - * char name[GNAME_MAX], mapset[GMAPSET_MAX]; - * - * function: - * determine the status off the automatic masking - * and the name of the cell file which forms the mask +/** + * \file lib/raster/mask_info.c * - * (the mask file is actually MASK in the current mapset, - * but is usually a reclassed cell file, and the reclass - * name and mapset are returned) + * \brief Raster Library - Get mask information * - * returns: - * -1 no masking (name, mapset undefined) - * name, mapset are undefined + * (C) 1999-2024 by Vaclav Petras and the GRASS Development Team * - * 1 mask file present, masking on - * name, mapset hold mask file name, mapset + * This program is free software under the GNU General Public + * License (>=v2). Read the file COPYING that comes with GRASS + * for details. * - ***************************************************************/ + * \author CERL + * \author Vaclav Petras, NC State University, Center for Geospatial Analytics + */ #include @@ -32,6 +19,15 @@ #include #include +/** + * @brief Get a printable text with information about raster mask + * + * Determines if 2D raster mask is present and returns textual information about + * the mask suitable for end-user display. The resulting text is translated. + * Caller is responsible for freeing the memory of the returned string. + * + * @return New string with textual information + */ char *Rast_mask_info(void) { char text[GNAME_MAX + GMAPSET_MAX + 16]; @@ -53,16 +49,88 @@ char *Rast_mask_info(void) return G_store(text); } +/** + * @brief Get raster mask status information + * + * _is_mask_reclass_ is a pointer to a bool variable which + * will be set to true if mask raster is a reclass and false otherwise. + * + * If you are not interested in the underlying reclassified raster map, + * pass NULL pointers for the three reclass parameters: + * + * ``` + * Rast_mask_status(name, mapset, NULL, NULL, NULL); + * ``` + * + * @param[out] name Name of the raster map used as mask + * @param[out] mapset Name of the mapset the raster is in + * @param[out] is_mask_reclass Will be set to true if mask raster is a reclass + * @param[out] reclass_name Name of the underlying reclassified raster map + * @param[out] reclass_mapset Name of the mapset the reclassified raster is in + * + * @return true if mask is present, false otherwise + */ +bool Rast_mask_status(char *name, char *mapset, bool *is_mask_reclass, + char *reclass_name, char *reclass_mapset) +{ + int present = Rast__mask_info(name, mapset); + + if (is_mask_reclass && reclass_name && reclass_mapset) { + if (present) { + *is_mask_reclass = Rast_is_reclass("MASK", G_mapset(), reclass_name, + reclass_mapset) > 0; + if (*is_mask_reclass) { + // The original mask values were overwritten in the initial + // info call. Put back the original values, so that we can + // report them to the caller. + strcpy(name, "MASK"); + strcpy(mapset, G_mapset()); + } + } + else { + *is_mask_reclass = false; + } + } + + if (present == 1) + return true; + else + return false; +} + +/** + * @brief Get information about the current mask + * + * Determines the status of the automatic masking and the name of the 2D + * raster which forms the mask. Typically, mask is raster called MASK in the + * current mapset, but when used with r.mask, it is usually a reclassed + * raster, and so when a MASK raster is present and it is a reclass raster, + * the name and mapset of the underlying reclassed raster are returned. + * + * The name and mapset is written to the parameter which need to be defined + * with a sufficient size, least as `char name[GNAME_MAX], mapset[GMAPSET_MAX]`. + * + * When the masking is not active, -1 is returned and name and mapset are + * undefined. When the masking is active, 1 is returned and name and mapset + * will hold the name and mapset of the underlying raster. + * + * @param[out] name Name of the raster map used as mask + * @param[out] mapset Name of the map's mapset + * + * @return 1 if mask is present, -1 otherwise + */ int Rast__mask_info(char *name, char *mapset) { char rname[GNAME_MAX], rmapset[GMAPSET_MAX]; - strcpy(name, "MASK"); - strcpy(mapset, G_mapset()); + strcpy(rname, "MASK"); + strcpy(rmapset, G_mapset()); - if (!G_find_raster(name, mapset)) + if (!G_find_raster(rname, rmapset)) return -1; + strcpy(name, rname); + strcpy(mapset, rmapset); if (Rast_is_reclass(name, mapset, rname, rmapset) > 0) { strcpy(name, rname); strcpy(mapset, rmapset); diff --git a/raster/Makefile b/raster/Makefile index bcd07660238..91ff54d0863 100644 --- a/raster/Makefile +++ b/raster/Makefile @@ -45,6 +45,7 @@ SUBDIRS = \ r.lake \ r.li \ r.mapcalc \ + r.mask.status \ r.mfilter \ r.mode \ r.neighbors \ diff --git a/raster/r.mask.status/Makefile b/raster/r.mask.status/Makefile new file mode 100644 index 00000000000..62c968d044e --- /dev/null +++ b/raster/r.mask.status/Makefile @@ -0,0 +1,10 @@ +MODULE_TOPDIR = ../.. + +PGM = r.mask.status + +LIBES = $(MANAGELIB) $(RASTERLIB) $(GISLIB) $(PARSONLIB) +DEPENDENCIES = $(MANAGEDEP) $(RASTERDEP) $(GISDEP) + +include $(MODULE_TOPDIR)/include/Make/Module.make + +default: cmd diff --git a/raster/r.mask.status/main.c b/raster/r.mask.status/main.c new file mode 100644 index 00000000000..5eb32300240 --- /dev/null +++ b/raster/r.mask.status/main.c @@ -0,0 +1,184 @@ +/**************************************************************************** + * + * MODULE: r.mask.status + * AUTHORS: Vaclav Petras + * PURPOSE: Report status of raster mask + * COPYRIGHT: (C) 2024 by Vaclav Petras and the GRASS Development Team + * + * This program is free software under the GNU General Public + * License (>=v2). Read the file COPYING that comes with GRASS + * for details. + * + *****************************************************************************/ + +#include +#include +#include +#include + +#include +#include +#include +#include + +struct Parameters { + struct Option *format; + struct Flag *like_test; +}; + +void parse_parameters(struct Parameters *params, int argc, char **argv) +{ + struct GModule *module; + + module = G_define_module(); + G_add_keyword(_("raster")); + G_add_keyword(_("mask")); + G_add_keyword(_("reclassification")); + module->label = _("Reports presence or absence of a raster mask"); + module->description = + _("Provides information about the presence of a 2D raster mask" + " as text output or return code"); + + params->format = G_define_option(); + params->format->key = "format"; + params->format->type = TYPE_STRING; + params->format->required = NO; + params->format->answer = "plain"; + params->format->options = "plain,json,shell,yaml"; + params->format->descriptions = + "plain;Plain text output;" + "json;JSON (JavaScript Object Notation);" + "shell;Shell script style output;" + "yaml;YAML (human-friendly data serialization language)"; + params->format->description = _("Format for reporting"); + + params->like_test = G_define_flag(); + params->like_test->key = 't'; + params->like_test->label = + _("Return code 0 when mask present, 1 otherwise"); + params->like_test->description = + _("Behave like the test utility, 0 for true, 1 for false, no output"); + // suppress_required is not required given the default value for format. + // Both no parameters and only -t work as expected. + + if (G_parser(argc, argv)) + exit(EXIT_FAILURE); +} + +int report_status(struct Parameters *params) +{ + + char name[GNAME_MAX]; + char mapset[GMAPSET_MAX]; + char reclass_name[GNAME_MAX]; + char reclass_mapset[GMAPSET_MAX]; + + bool is_mask_reclass; + bool present = Rast_mask_status(name, mapset, &is_mask_reclass, + reclass_name, reclass_mapset); + + // This does not have to be exclusive with the printing, but leaving this + // to a different boolean flag which could do the return code and printing. + // The current implementation really behaves like the test utility which + // facilitates the primary usage of this which is prompt building + // (and there any output would be noise). + if (params->like_test->answer) { + if (present) + return 0; + return 1; + } + + // Mask raster + char *full_mask = G_fully_qualified_name(name, mapset); + // Underlying raster if applicable + char *full_underlying = NULL; + if (is_mask_reclass) + full_underlying = G_fully_qualified_name(reclass_name, reclass_mapset); + + if (strcmp(params->format->answer, "json") == 0) { + JSON_Value *root_value = json_value_init_object(); + JSON_Object *root_object = json_object(root_value); + json_object_set_boolean(root_object, "present", present); + if (present) + json_object_set_string(root_object, "full_name", full_mask); + else + json_object_set_null(root_object, "full_name"); + if (is_mask_reclass) + json_object_set_string(root_object, "is_reclass_of", + full_underlying); + else + json_object_set_null(root_object, "is_reclass_of"); + char *serialized_string = json_serialize_to_string_pretty(root_value); + puts(serialized_string); + json_free_serialized_string(serialized_string); + json_value_free(root_value); + } + else if (strcmp(params->format->answer, "shell") == 0) { + printf("present="); + if (present) + printf("1"); + else + printf("0"); + printf("\nfull_name="); + if (present) + printf("%s", full_mask); + printf("\nis_reclass_of="); + if (is_mask_reclass) + printf("%s", full_underlying); + printf("\n"); + } + else if (strcmp(params->format->answer, "yaml") == 0) { + printf("present: "); + if (present) + printf("true"); + else + printf("false"); + printf("\nfull_name: "); + if (present) + printf("|-\n %s", full_mask); + else + printf("null"); + // Null values in YAML can be an empty (no) value (rather than null), + // so we could use that, but using the explicit null as a reasonable + // starting point. + printf("\nis_reclass_of: "); + // Using block scalar with |- to avoid need for escaping. + // Alternatively, we could check mapset naming limits against YAML + // escaping needs for different types of strings and do the necessary + // escaping here. + if (is_mask_reclass) + printf("|-\n %s", full_underlying); + else + printf("null"); + printf("\n"); + } + else { + if (present) + printf(_("Mask is active")); + else + printf(_("Mask is not present")); + if (present) { + printf("\n"); + printf(_("Mask name: %s"), full_mask); + } + if (is_mask_reclass) { + printf("\n"); + printf(_("Mask is a raster reclassified from: %s"), + full_underlying); + } + printf("\n"); + } + + G_free(full_mask); + G_free(full_underlying); + return EXIT_SUCCESS; +} + +int main(int argc, char **argv) +{ + struct Parameters params; + + G_gisinit(argv[0]); + parse_parameters(¶ms, argc, argv); + return report_status(¶ms); +} diff --git a/raster/r.mask.status/r.mask.status.html b/raster/r.mask.status/r.mask.status.html new file mode 100644 index 00000000000..248ee3ea317 --- /dev/null +++ b/raster/r.mask.status/r.mask.status.html @@ -0,0 +1,65 @@ +

    DESCRIPTION

    + +The r.mask.status reports information about the 2D raster mask and its +status. If the mask is present, the tool reports a full name of the raster (name +including the mapset) which represents the mask. It can also report full name of +the underlying raster if the mask is reclassified from another raster. + +

    +With the -t flag, no output is printed, instead a return code is used to +indicate presence or absence. The convention is the same same the POSIX +test utility, so r.mask.status returns 0 when the mask is +present and 1 otherwise. + +

    EXAMPLES

    + +

    Generate JSON output

    + +To generate JSON output in Bash, use the format option: + +
    +r.mask.status format=json
    +
    + +In Python, use: + +
    +import grass.script as gs
    +gs.parse_command("r.mask.status", format="json")
    +
    + +This returns a dictionary with keys present, +full_name, and is_reclass_of. + +

    Use as the test utility

    + +The POSIX test utility uses return code 0 to indicate presence +and 1 to indicate absence of a file, so testing existence of a file with +test -f gives return code 0 when the file exists. +r.mask.status can be used in the same with the the -t flag: + +
    +r.mask.status -t
    +
    + +In a Bash script: + +
    +# Bash
    +if r.mask.status -t; then
    +    echo "Masking is active"
    +else
    +    echo "Masking is not active"
    +fi
    +
    + +

    SEE ALSO

    + + +r.mask, +g.region + + +

    AUTHORS

    + +Vaclav Petras, NC State University, Center for Geospatial Analytics diff --git a/raster/r.mask.status/tests/conftest.py b/raster/r.mask.status/tests/conftest.py new file mode 100644 index 00000000000..e8e27315845 --- /dev/null +++ b/raster/r.mask.status/tests/conftest.py @@ -0,0 +1,25 @@ +"""Fixtures for simple sessions""" + +import os +import pytest +import grass.script as gs + + +@pytest.fixture +def session_no_data(tmp_path): + """Set up a GRASS session for the tests.""" + project = "test_project" + gs.create_project(tmp_path, project) + with gs.setup.init(tmp_path / project, env=os.environ.copy()) as session: + yield session + + +@pytest.fixture +def session_with_data(tmp_path): + """Set up a GRASS session for the tests.""" + project = tmp_path / "test_project" + gs.create_project(project) + with gs.setup.init(project, env=os.environ.copy()) as session: + gs.run_command("g.region", rows=2, cols=2, env=session.env) + gs.mapcalc("a = 1", env=session.env) + yield session diff --git a/raster/r.mask.status/tests/r_mask_status_test.py b/raster/r.mask.status/tests/r_mask_status_test.py new file mode 100644 index 00000000000..deafdfb145b --- /dev/null +++ b/raster/r.mask.status/tests/r_mask_status_test.py @@ -0,0 +1,126 @@ +"""Tests of r.mask.status""" + +import pytest + +try: + import yaml +except ImportError: + yaml = None + +import grass.script as gs + + +def test_json_no_mask(session_no_data): + """Check JSON format for no mask""" + session = session_no_data + data = gs.parse_command("r.mask.status", format="json", env=session.env) + assert "present" in data + assert "full_name" in data + assert "is_reclass_of" in data + assert data["present"] is False + assert not data["full_name"] + assert not data["is_reclass_of"] + + +def test_json_with_r_mask(session_with_data): + """Check JSON format for the r.mask case""" + session = session_with_data + gs.run_command("r.mask", raster="a", env=session.env) + data = gs.parse_command("r.mask.status", format="json", env=session.env) + assert data["present"] is True + assert data["full_name"] == "MASK@PERMANENT" + assert data["is_reclass_of"] == "a@PERMANENT" + # Now remove the mask. + gs.run_command("r.mask", flags="r", env=session.env) + data = gs.parse_command("r.mask.status", format="json", env=session.env) + assert data["present"] is False + assert not data["full_name"] + assert not data["is_reclass_of"] + + +def test_json_with_g_copy(session_with_data): + """Check JSON format for the low-level g.copy case""" + session = session_with_data + gs.run_command("g.copy", raster="a,MASK", env=session.env) + data = gs.parse_command("r.mask.status", format="json", env=session.env) + assert data["present"] is True + assert data["full_name"] == "MASK@PERMANENT" + assert not data["is_reclass_of"] + # Now remove the mask. + gs.run_command("g.remove", type="raster", name="MASK", flags="f", env=session.env) + data = gs.parse_command("r.mask.status", format="json", env=session.env) + assert data["present"] is False + assert not data["full_name"] + assert not data["is_reclass_of"] + + +def test_shell(session_with_data): + """Check shell format for the r.mask case""" + session = session_with_data + gs.run_command("r.mask", raster="a", env=session.env) + data = gs.parse_command("r.mask.status", format="shell", env=session.env) + assert int(data["present"]) + assert data["full_name"] == "MASK@PERMANENT" + assert data["is_reclass_of"] == "a@PERMANENT" + # Now remove the mask. + gs.run_command("r.mask", flags="r", env=session.env) + data = gs.parse_command("r.mask.status", format="shell", env=session.env) + assert not int(data["present"]) + assert not data["full_name"] + assert not data["is_reclass_of"] + + +@pytest.mark.skipif(yaml is None, reason="PyYAML package not available") +def test_yaml(session_with_data): + """Check YAML format for the r.mask case""" + session = session_with_data + gs.run_command("r.mask", raster="a", env=session.env) + text = gs.read_command("r.mask.status", format="yaml", env=session.env) + data = yaml.safe_load(text) + assert data["present"] is True + assert data["full_name"] == "MASK@PERMANENT" + assert data["is_reclass_of"] == "a@PERMANENT" + # Now remove the mask. + gs.run_command("r.mask", flags="r", env=session.env) + text = gs.read_command("r.mask.status", format="yaml", env=session.env) + data = yaml.safe_load(text) + assert data["present"] is False + assert not data["full_name"] + assert not data["is_reclass_of"] + + +def test_plain(session_with_data): + """Check plain text format for the r.mask case""" + session = session_with_data + gs.run_command("r.mask", raster="a", env=session.env) + text = gs.read_command("r.mask.status", format="plain", env=session.env) + assert text + assert "MASK@PERMANENT" in text + assert "a@PERMANENT" in text + # Now remove the mask. + gs.run_command("r.mask", flags="r", env=session.env) + text = gs.read_command("r.mask.status", format="plain", env=session.env) + assert text + + +def test_without_parameters(session_no_data): + """Check output is generated with no parameters""" + session = session_no_data + text = gs.read_command("r.mask.status", env=session.env) + assert text + + +def test_behavior_mimicking_test_program(session_with_data): + """Check test program like behavior for the r.mask case""" + session = session_with_data + gs.run_command("r.mask", raster="a", env=session.env) + returncode = gs.run_command( + "r.mask.status", flags="t", env=session.env, errors="status" + ) + assert returncode == 0 + # Now remove the mask. + gs.run_command("r.mask", flags="r", env=session.env) + returncode = gs.run_command( + "r.mask.status", flags="t", env=session.env, errors="status" + ) + assert returncode == 1 From 0feea315328915bfc1382097805c526708f1736e Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Sat, 12 Oct 2024 04:03:51 -0400 Subject: [PATCH 370/471] i.ortho.photo: Fix Resource Leak in find_init.c (#4388) --- imagery/i.ortho.photo/lib/find_init.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/imagery/i.ortho.photo/lib/find_init.c b/imagery/i.ortho.photo/lib/find_init.c index 906203a7f90..f8cea9b9387 100644 --- a/imagery/i.ortho.photo/lib/find_init.c +++ b/imagery/i.ortho.photo/lib/find_init.c @@ -4,15 +4,19 @@ * Find the a camera initial file in the current group (if it exists) **************************************************************/ #include +#include int I_find_initial(char *group) { - char *element; - - element = (char *)G_malloc(80 * sizeof(char)); + char element[GNAME_MAX + 6]; if (group == NULL || *group == 0) return 0; - sprintf(element, "group/%s", group); + + if (snprintf(element, GNAME_MAX, "group/%s", group) >= GNAME_MAX) { + G_warning(_("Group name <%s> is too long"), group); + return 0; + } + return G_find_file(element, "INIT_EXP", G_mapset()) != NULL; } From 9df8433d50a9552ebe53ea37c45d698739bdcf4f Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Sat, 12 Oct 2024 04:06:08 -0400 Subject: [PATCH 371/471] lib/vector/Vlib: Fix copy into buffer size issue in color_read.c (#4462) --- lib/vector/Vlib/color_read.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/vector/Vlib/color_read.c b/lib/vector/Vlib/color_read.c index 0a023a89a7d..4e0b3b04837 100644 --- a/lib/vector/Vlib/color_read.c +++ b/lib/vector/Vlib/color_read.c @@ -49,7 +49,10 @@ int Vect_read_colors(const char *name, const char *mapset, if (colors) Rast_init_colors(colors); - strcpy(xname, name); + if (G_strlcpy(xname, name, sizeof(xname)) >= sizeof(xname)) { + G_warning(_("Vector map name <%s> is too long"), name); + return -1; + } mapset = G_find_vector(xname, mapset); if (!mapset) return -1; @@ -58,12 +61,12 @@ int Vect_read_colors(const char *name, const char *mapset, if (strcmp(mapset, G_mapset()) == 0) { /* look for the regular color table */ - sprintf(buf, "%s/%s", GV_DIRECTORY, name); + (void)snprintf(buf, sizeof(buf), "%s/%s", GV_DIRECTORY, name); ret = Rast__read_colors(buf, GV_COLR_ELEMENT, mapset, colors); } else { /* look for secondary color table in current mapset */ - sprintf(buf, "%s/%s", GV_COLR2_DIRECTORY, mapset); + (void)snprintf(buf, sizeof(buf), "%s/%s", GV_COLR2_DIRECTORY, mapset); ret = Rast__read_colors(buf, name, G_mapset(), colors); } if (ret == -2) From 4cf93d28d995f4a012c68914fcbe95786245b444 Mon Sep 17 00:00:00 2001 From: Vaclav Petras Date: Sat, 12 Oct 2024 04:09:53 -0400 Subject: [PATCH 372/471] checks: Remove extra semicolons in r.terraflow (#4494) --- raster/r.terraflow/fill.cpp | 2 +- raster/r.terraflow/genericWindow.cpp | 2 +- raster/r.terraflow/sweep.cpp | 2 +- raster/r.terraflow/weightWindow.cpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/raster/r.terraflow/fill.cpp b/raster/r.terraflow/fill.cpp index ab1629a3b7f..c7b54cd0a47 100644 --- a/raster/r.terraflow/fill.cpp +++ b/raster/r.terraflow/fill.cpp @@ -518,7 +518,7 @@ void assignFinalDirections(AMI_STREAM *statstr, continue; } } -}; +} /* ********************************************************************** */ class directionElevationMerger { diff --git a/raster/r.terraflow/genericWindow.cpp b/raster/r.terraflow/genericWindow.cpp index 92b0852cd71..9b114773e2a 100644 --- a/raster/r.terraflow/genericWindow.cpp +++ b/raster/r.terraflow/genericWindow.cpp @@ -36,4 +36,4 @@ void fillPit(ElevationWindow &win) if (win.get(4) < min) { win.set(4, min); } -}; +} diff --git a/raster/r.terraflow/sweep.cpp b/raster/r.terraflow/sweep.cpp index 2f7fba8bb13..c98b844b016 100644 --- a/raster/r.terraflow/sweep.cpp +++ b/raster/r.terraflow/sweep.cpp @@ -65,7 +65,7 @@ sweepOutput::sweepOutput() #ifdef OUTPUT_TCI tci = (tci_type)nodataType::ELEVATION_NODATA; #endif -}; +} /* ------------------------------------------------------------ */ /* computes output parameters of cell (i,j) given the flow value, the diff --git a/raster/r.terraflow/weightWindow.cpp b/raster/r.terraflow/weightWindow.cpp index ccf55a82ca9..760dccc6b5c 100644 --- a/raster/r.terraflow/weightWindow.cpp +++ b/raster/r.terraflow/weightWindow.cpp @@ -229,7 +229,7 @@ void weightWindow::compute(const dimension_type i, const dimension_type j, cout << form("%3.2f ", weight.get(l)); cout << "]\n"; #endif -}; +} /* Find the dominant direction. Set corresponding weight to 1, and sets all other weights to 0. Set sumweight and sumcontour.*/ From 71066f415bfb1f85c488e61284f8b477fb7a910b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 12 Oct 2024 07:27:34 -0400 Subject: [PATCH 373/471] CI(deps): Update actions/upload-artifact action to v4.4.3 (#4481) --- .github/actions/create-upload-suggestions/action.yml | 4 ++-- .github/workflows/macos.yml | 2 +- .github/workflows/osgeo4w.yml | 2 +- .github/workflows/pytest.yml | 2 +- .github/workflows/python-code-quality.yml | 4 ++-- .github/workflows/ubuntu.yml | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/actions/create-upload-suggestions/action.yml b/.github/actions/create-upload-suggestions/action.yml index 181babe11e9..b80c08c1b46 100644 --- a/.github/actions/create-upload-suggestions/action.yml +++ b/.github/actions/create-upload-suggestions/action.yml @@ -177,7 +177,7 @@ runs: echo "diff-file-name=${INPUT_DIFF_FILE_NAME}" >> "${GITHUB_OUTPUT}" env: INPUT_DIFF_FILE_NAME: ${{ steps.tool-name-safe.outputs.diff-file-name }} - - uses: actions/upload-artifact@84480863f228bb9747b473957fcc9e309aa96097 # v4.4.2 + - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 id: upload-diff if: >- ${{ (steps.files_changed.outputs.files_changed == 'true') && @@ -200,7 +200,7 @@ runs: echo 'Suggestions can only be added near to lines changed in this PR.' echo 'If any fixes can be added as code suggestions, they will be added shortly from another workflow.' } >> "${GITHUB_STEP_SUMMARY}" - - uses: actions/upload-artifact@84480863f228bb9747b473957fcc9e309aa96097 # v4.4.2 + - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 id: upload-changes if: >- ${{ always() && diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 7b4e7eb18e2..41ac51df8ff 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -107,7 +107,7 @@ jobs: nc_spm_full_v2alpha2.tar.gz" - name: Make HTML test report available if: ${{ !cancelled() }} - uses: actions/upload-artifact@84480863f228bb9747b473957fcc9e309aa96097 # v4.4.2 + uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 with: name: testreport-macOS path: testreport diff --git a/.github/workflows/osgeo4w.yml b/.github/workflows/osgeo4w.yml index 180cc79ec08..a7694fa9b30 100644 --- a/.github/workflows/osgeo4w.yml +++ b/.github/workflows/osgeo4w.yml @@ -120,7 +120,7 @@ jobs: - name: Make HTML test report available if: ${{ always() }} - uses: actions/upload-artifact@84480863f228bb9747b473957fcc9e309aa96097 # v4.4.2 + uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 with: name: testreport-${{ matrix.os }} path: testreport diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 60ddb223074..f2a96cf41ba 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -116,7 +116,7 @@ jobs: token: ${{ secrets.CODECOV_TOKEN }} - name: Make python-only code coverage test report available if: ${{ !cancelled() }} - uses: actions/upload-artifact@84480863f228bb9747b473957fcc9e309aa96097 # v4.4.2 + uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 with: name: python-codecoverage-report-${{ matrix.os }}-${{ matrix.python-version }} path: coverage_html_report diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index 5da86464470..b8b423b2b16 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -129,7 +129,7 @@ jobs: bandit -c pyproject.toml -iii -r . -f sarif -o bandit.sarif --exit-zero - name: Upload Bandit Scan Results - uses: actions/upload-artifact@84480863f228bb9747b473957fcc9e309aa96097 # v4.4.2 + uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 with: name: bandit.sarif path: bandit.sarif @@ -201,7 +201,7 @@ jobs: cp -rp dist.$ARCH/docs/html/libpython sphinx-grass - name: Make Sphinx documentation available - uses: actions/upload-artifact@84480863f228bb9747b473957fcc9e309aa96097 # v4.4.2 + uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 with: name: sphinx-grass path: sphinx-grass diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 88ddd0d31b2..966dbfe21ab 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -149,7 +149,7 @@ jobs: - name: Make HTML test report available if: ${{ always() }} - uses: actions/upload-artifact@84480863f228bb9747b473957fcc9e309aa96097 # v4.4.2 + uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 with: name: testreport-${{ matrix.os }}-${{ matrix.config }}-${{ matrix.extra-include }} path: testreport From 542f3b479c6fd44477485aac26e5491bc30966a7 Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Sat, 12 Oct 2024 07:28:22 -0400 Subject: [PATCH 374/471] v.colors: Fix Resource Leak issue in read_rgb.c (#4497) --- vector/v.colors/read_rgb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/vector/v.colors/read_rgb.c b/vector/v.colors/read_rgb.c index 99aa054138d..c6e762ca0af 100644 --- a/vector/v.colors/read_rgb.c +++ b/vector/v.colors/read_rgb.c @@ -64,4 +64,5 @@ void rgb2colr(struct Map_info *Map, int layer, const char *rgb_column, G_warning(_("%d invalid RGB color values skipped"), nskipped); db_close_database_shutdown_driver(driver); + Vect_destroy_field_info(fi); } From 779af809951d802c2ba25cc7e01582c7d20da986 Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Sat, 12 Oct 2024 07:28:58 -0400 Subject: [PATCH 375/471] v.to.db: Fix Resource Leak issue in areas.c (#4498) * Fix Resource Leak issue * Fix Resource Leak issue --- vector/v.to.db/areas.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vector/v.to.db/areas.c b/vector/v.to.db/areas.c index 0977890276e..232cc4894d6 100644 --- a/vector/v.to.db/areas.c +++ b/vector/v.to.db/areas.c @@ -2,6 +2,7 @@ #include #include #include +#include #include "global.h" @@ -111,5 +112,6 @@ int read_areas(struct Map_info *Map) G_percent(area_num, nareas, 2); } + Vect_destroy_cats_struct(Cats); return 0; } From 03675b0323cfff4974c0298139d0d73199b0a5a1 Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Sat, 12 Oct 2024 10:23:47 -0400 Subject: [PATCH 376/471] d.vect: Fix Resource Leak issue in attr.c (#4496) * fix Resource Leak issue * Update display/d.vect/attr.c --- display/d.vect/attr.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/display/d.vect/attr.c b/display/d.vect/attr.c index 5a28807cbca..747b98c59af 100644 --- a/display/d.vect/attr.c +++ b/display/d.vect/attr.c @@ -35,9 +35,11 @@ int display_attr(struct Map_info *Map, int type, char *attrcol, db_init_string(&text); fi = Vect_get_field(Map, lattr->field); - if (fi == NULL) + if (fi == NULL) { + Vect_destroy_line_struct(Points); + Vect_destroy_cats_struct(Cats); return 1; - + } driver = db_start_driver_open_database(fi->driver, fi->database); if (driver == NULL) G_fatal_error(_("Unable to open database <%s> by driver <%s>"), @@ -140,6 +142,7 @@ int display_attr(struct Map_info *Map, int type, char *attrcol, db_close_database_shutdown_driver(driver); Vect_destroy_line_struct(Points); Vect_destroy_cats_struct(Cats); + Vect_destroy_field_info(fi); return 0; } From 3c4aa6d499bb3cb82b29e302c7cdeb7e1706243c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 14 Oct 2024 02:55:04 +0000 Subject: [PATCH 377/471] CI(deps): Lock file maintenance (#4509) --- flake.lock | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/flake.lock b/flake.lock index 51f61fb86f9..eacc92d3c4f 100644 --- a/flake.lock +++ b/flake.lock @@ -5,11 +5,11 @@ "nixpkgs-lib": "nixpkgs-lib" }, "locked": { - "lastModified": 1726153070, - "narHash": "sha256-HO4zgY0ekfwO5bX0QH/3kJ/h4KvUDFZg8YpkNwIbg1U=", + "lastModified": 1727826117, + "narHash": "sha256-K5ZLCyfO/Zj9mPFldf3iwS6oZStJcU4tSpiXTMYaaL0=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "bcef6817a8b2aa20a5a6dbb19b43e63c5bf8619a", + "rev": "3d04084d54bedc3d6b8b736c70ef449225c361b1", "type": "github" }, "original": { @@ -19,11 +19,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1727648392, - "narHash": "sha256-VTlVv1nSxImFxY6RPQpNZxvEOQ0u5s1wBFDgixySNDo=", + "lastModified": 1728538411, + "narHash": "sha256-f0SBJz1eZ2yOuKUr5CA9BHULGXVSn6miBuUWdTyhUhU=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "4e0c36e4dd53f35d5a6385bdae88895ec5832f70", + "rev": "b69de56fac8c2b6f8fd27f2eca01dcda8e0a4221", "type": "github" }, "original": { @@ -35,14 +35,14 @@ }, "nixpkgs-lib": { "locked": { - "lastModified": 1725233747, - "narHash": "sha256-Ss8QWLXdr2JCBPcYChJhz4xJm+h/xjl4G0c0XlP6a74=", + "lastModified": 1727825735, + "narHash": "sha256-0xHYkMkeLVQAMa7gvkddbPqpxph+hDzdu1XdGPJR+Os=", "type": "tarball", - "url": "https://github.com/NixOS/nixpkgs/archive/356624c12086a18f2ea2825fed34523d60ccc4e3.tar.gz" + "url": "https://github.com/NixOS/nixpkgs/archive/fb192fec7cc7a4c26d51779e9bab07ce6fa5597a.tar.gz" }, "original": { "type": "tarball", - "url": "https://github.com/NixOS/nixpkgs/archive/356624c12086a18f2ea2825fed34523d60ccc4e3.tar.gz" + "url": "https://github.com/NixOS/nixpkgs/archive/fb192fec7cc7a4c26d51779e9bab07ce6fa5597a.tar.gz" } }, "root": { From cb7cbab7e72f1d49bd3a761d1b0da2ee1b101646 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 14 Oct 2024 04:15:36 +0000 Subject: [PATCH 378/471] CI(deps): Lock file maintenance (#4510) From ed02be59ff4129bbc3bd9fbb7545b7b73648be99 Mon Sep 17 00:00:00 2001 From: Mohan Yelugoti Date: Mon, 14 Oct 2024 07:54:32 -0400 Subject: [PATCH 379/471] ps.map: initialize variable contents before using them in get_ll_bounds (#4501) In some situations, when some conditionals fails, we would be assigning uninitialized variables to values, which is undefined behavior. Fix that by assigning a value to the variables. This was found using cppcheck tool. Signed-off-by: Mohan Yelugoti --- ps/ps.map/do_geogrid.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ps/ps.map/do_geogrid.c b/ps/ps.map/do_geogrid.c index 0fac5f80cac..c22ef86379c 100644 --- a/ps/ps.map/do_geogrid.c +++ b/ps/ps.map/do_geogrid.c @@ -300,6 +300,7 @@ void get_ll_bounds(double *w, double *e, double *s, double *n) double ew, ns; int first; + east = west = north = south = 0.0; e1 = PS.w.east; w1 = PS.w.west; n1 = PS.w.north; From b5289ee0c71b666e8e1969cca07474298f44eded Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Mon, 14 Oct 2024 07:58:03 -0400 Subject: [PATCH 380/471] r.carve: Fix resource leak issue in enforce_ds.c (#4505) --- raster/r.carve/enforce_ds.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/raster/r.carve/enforce_ds.c b/raster/r.carve/enforce_ds.c index ea4d7a1ae7a..7a71dc97def 100644 --- a/raster/r.carve/enforce_ds.c +++ b/raster/r.carve/enforce_ds.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "enforce.h" #ifndef MAX @@ -487,4 +488,6 @@ static void process_line_segment(const int npts, void *rbuf, Point2 *pgxypts, prevrow = row; prevcol = col; } + Vect_destroy_line_struct(points); + Vect_destroy_cats_struct(cats); } From e139a4c0fd246bda49a9aff780a6f737306e3358 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Mon, 14 Oct 2024 13:06:14 -0400 Subject: [PATCH 381/471] gui: Solve a recursion error in gui/wxpython/lmgr/giface.py (#4514) --- gui/wxpython/lmgr/giface.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/gui/wxpython/lmgr/giface.py b/gui/wxpython/lmgr/giface.py index eac9a7fb2d8..67669039d5d 100644 --- a/gui/wxpython/lmgr/giface.py +++ b/gui/wxpython/lmgr/giface.py @@ -54,7 +54,9 @@ def __init__(self, tree): self._tree = tree def __len__(self): - return len(list(self)) + # The list constructor calls __len__ as an optimization if available, + # causing a RecursionError + return len([layer for layer in self]) # noqa: C416 def __iter__(self): """Iterates over the contents of the list.""" From 61b380f1c4e3163d67961448553a091642c27eda Mon Sep 17 00:00:00 2001 From: Nicklas Larsson Date: Mon, 14 Oct 2024 20:11:42 +0200 Subject: [PATCH 382/471] r.mask.status: fix null pointer dereference and false positive string overflow (#4512) Fixes two new issues reported by Coverity Scan: - Handles case of unsuccessful creation of json string - Silences false positive issue for string copy operation to buffer of size GMAPSET_MAX from G_mapset(). --- lib/raster/mask_info.c | 2 +- raster/r.mask.status/main.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/raster/mask_info.c b/lib/raster/mask_info.c index 42102de86dd..317bab75b63 100644 --- a/lib/raster/mask_info.c +++ b/lib/raster/mask_info.c @@ -124,7 +124,7 @@ int Rast__mask_info(char *name, char *mapset) char rname[GNAME_MAX], rmapset[GMAPSET_MAX]; strcpy(rname, "MASK"); - strcpy(rmapset, G_mapset()); + (void)G_strlcpy(rmapset, G_mapset(), GMAPSET_MAX); if (!G_find_raster(rname, rmapset)) return -1; diff --git a/raster/r.mask.status/main.c b/raster/r.mask.status/main.c index 5eb32300240..16790adf35c 100644 --- a/raster/r.mask.status/main.c +++ b/raster/r.mask.status/main.c @@ -109,6 +109,8 @@ int report_status(struct Parameters *params) else json_object_set_null(root_object, "is_reclass_of"); char *serialized_string = json_serialize_to_string_pretty(root_value); + if (!serialized_string) + G_fatal_error(_("Failed to initialize pretty JSON string.")); puts(serialized_string); json_free_serialized_string(serialized_string); json_value_free(root_value); From 761d98a78010e34daa1d217fef433d04020ab82f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Mon, 14 Oct 2024 15:30:57 -0400 Subject: [PATCH 383/471] style: Ignore deprecated PT004 rule (#4520) Ruff rule PT004 is deprecated, so doesn't run with --preview flag, but still appears without the --preview flag. Since the only error won't be fixed, ignore that rule for the time being. --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 6147e2db897..2a2287aaaad 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -176,6 +176,7 @@ ignore = [ "PLW1641", # eq-without-hash "PLW2901", # redefined-loop-name "PLW3201", # bad-dunder-method-name + "PT004", # pytest-missing-fixture-name-underscore # deprecated, so doesn't appear with --preview "PTH100", # os-path-abspath "PTH101", # os-chmod "PTH102", # os-mkdir From 3a9059b7e7bec4a302cf6455c65fbd7fea3f2f8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Mon, 14 Oct 2024 16:27:05 -0400 Subject: [PATCH 384/471] pytest: Collect code coverage in multiple workers too (#4451) * pytest: Collect code coverage in multiple workers too * utils: Handle paths that already have a .py extension in coverage_mapper * CI: Set INITIAL_GISBASE and INITIAL_PWD env vars for running pytest with multiple workers * Coverage: Omit gui/wxpython subfolders for now They are not covered at all and take some noticeable time to collect * Coverage: Remove unneeded omit patten for gui/wxpython subfolders --- .coveragerc | 1 + .github/workflows/pytest.yml | 11 +++++++++-- utils/coverage_mapper.py | 6 ++++-- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/.coveragerc b/.coveragerc index 65e13c2234b..409fde55643 100644 --- a/.coveragerc +++ b/.coveragerc @@ -9,6 +9,7 @@ omit = ${INITIAL_PWD-.}/.github/* ${INITIAL_PWD-.}/bin.*/* ${INITIAL_PWD-.}/dist.*/* + **/gui/wxpython/*/** **/OBJ.*/* source = . diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index f2a96cf41ba..e1b3b84b54e 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -83,8 +83,13 @@ jobs: run: | export PYTHONPATH=`grass --config python_path`:$PYTHONPATH export LD_LIBRARY_PATH=$(grass --config path)/lib:$LD_LIBRARY_PATH + export INITIAL_GISBASE="$(grass --config path)" + export INITIAL_PWD="${PWD}" pytest --verbose --color=yes --durations=0 --durations-min=0.5 \ - --numprocesses auto -ra . \ + --numprocesses auto \ + --cov \ + --cov-context=test \ + -ra . \ -m 'not needs_solo_run' - name: Run pytest with a single worker (for tests marked with needs_solo_run) @@ -92,9 +97,11 @@ jobs: export PYTHONPATH=`grass --config python_path`:$PYTHONPATH export LD_LIBRARY_PATH=$(grass --config path)/lib:$LD_LIBRARY_PATH export INITIAL_GISBASE="$(grass --config path)" - INITIAL_PWD="${PWD}" pytest --verbose --color=yes --durations=0 --durations-min=0.5 \ + export INITIAL_PWD="${PWD}" + pytest --verbose --color=yes --durations=0 --durations-min=0.5 \ --cov \ --cov-context=test \ + --cov-append \ -ra . \ -m 'needs_solo_run' - name: Fix non-standard installed script paths in coverage data diff --git a/utils/coverage_mapper.py b/utils/coverage_mapper.py index 9a2f1389781..88fa30f9b54 100644 --- a/utils/coverage_mapper.py +++ b/utils/coverage_mapper.py @@ -22,12 +22,14 @@ def map_scripts_paths(old_path): if INITIAL_GISBASE is None or INITIAL_PWD is None: return old_path p = Path(old_path) + extension = ".py" + p_name = p.stem if p.suffix == extension else p.name temporal_base = Path(INITIAL_GISBASE) / "scripts" / "t.*" base = Path(INITIAL_GISBASE) / "scripts" / "*" if p.match(str(temporal_base)): - return str(Path(INITIAL_PWD) / "temporal" / (p.name) / (p.name)) + ".py" + return str(Path(INITIAL_PWD) / "temporal" / (p_name) / (p_name)) + extension if p.match(str(base)): - return str(Path(INITIAL_PWD) / "scripts" / (p.name) / (p.name)) + ".py" + return str(Path(INITIAL_PWD) / "scripts" / (p_name) / (p_name)) + extension return old_path From a98691f4eff03bb36ac6cd14058eab6edeace742 Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Mon, 14 Oct 2024 17:02:46 -0400 Subject: [PATCH 385/471] v.net.timetable: Fix Resource Leak issue in main.c (#4508) * Fix Resource Leak issue * requested changes --- vector/v.net.timetable/main.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/vector/v.net.timetable/main.c b/vector/v.net.timetable/main.c index 5fb382009bb..424d8a851bd 100644 --- a/vector/v.net.timetable/main.c +++ b/vector/v.net.timetable/main.c @@ -176,9 +176,7 @@ void write_subroute(struct segment *seg, struct line_pnts *line, int line_id) struct line_cats *Cats; struct ilist *list; - Points = Vect_new_line_struct(); Cats = Vect_new_cats_struct(); - list = Vect_new_list(); r = seg->route; Vect_cat_set(Cats, 2, line_id); @@ -188,6 +186,9 @@ void write_subroute(struct segment *seg, struct line_pnts *line, int line_id) return; } + Points = Vect_new_line_struct(); + list = Vect_new_list(); + for (i = 0; i < nnodes; i++) edges[i] = 0; for (i = 0; i < lines[r]->n_values; i++) From f241532c70217d27955cdd0677d6e9cede697605 Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Mon, 14 Oct 2024 21:46:30 -0400 Subject: [PATCH 386/471] v.kernel: Fix resource Leak issue in main.c (#4506) Resource Leak --- vector/v.kernel/main.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/vector/v.kernel/main.c b/vector/v.kernel/main.c index 96198a4d8f7..c1e7f77a36a 100644 --- a/vector/v.kernel/main.c +++ b/vector/v.kernel/main.c @@ -769,7 +769,9 @@ double compute_all_net_distances(struct Map_info *In, struct Map_info *Net, G_debug(3, " kk = %d", kk); } } - + Vect_destroy_line_struct(APoints); + Vect_destroy_line_struct(BPoints); + Vect_destroy_boxlist(List); return (kk); } From c5c5ec5f51d1e8fe503a3b80f5778ebc80be7efe Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Tue, 15 Oct 2024 04:25:27 -0400 Subject: [PATCH 387/471] lib/vector/Vlib: Fix Resource leak issue in geos.c (#4507) --- lib/vector/Vlib/geos.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/lib/vector/Vlib/geos.c b/lib/vector/Vlib/geos.c index 719e0fdd270..2c68a0a6fba 100644 --- a/lib/vector/Vlib/geos.c +++ b/lib/vector/Vlib/geos.c @@ -290,7 +290,7 @@ GEOSCoordSequence *V1_read_line_geos(struct Map_info *Map, long offset, long size; double *x, *y, *z; - GEOSCoordSequence *pseq; + GEOSCoordSequence *pseq = NULL; G_debug(3, "V1_read_line_geos(): offset = %ld", offset); @@ -353,8 +353,6 @@ GEOSCoordSequence *V1_read_line_geos(struct Map_info *Map, long offset, G_debug(3, " n_points = %d dim = %d", n_points, (Map->head.with_z) ? 3 : 2); - pseq = GEOSCoordSeq_create(n_points, (Map->head.with_z) ? 3 : 2); - x = (double *)G_malloc(n_points * sizeof(double)); y = (double *)G_malloc(n_points * sizeof(double)); if (Map->head.with_z) @@ -362,17 +360,22 @@ GEOSCoordSequence *V1_read_line_geos(struct Map_info *Map, long offset, else z = NULL; - if (0 >= dig__fread_port_D(x, n_points, &(Map->dig_fp))) - return NULL; /* end of file */ + if (0 >= dig__fread_port_D(x, n_points, &(Map->dig_fp))) { + goto free_return; /* end of file */ + } - if (0 >= dig__fread_port_D(y, n_points, &(Map->dig_fp))) - return NULL; /* end of file */ + if (0 >= dig__fread_port_D(y, n_points, &(Map->dig_fp))) { + goto free_return; /* end of file */ + } if (Map->head.with_z) { - if (0 >= dig__fread_port_D(z, n_points, &(Map->dig_fp))) - return NULL; /* end of file */ + if (0 >= dig__fread_port_D(z, n_points, &(Map->dig_fp))) { + goto free_return; /* end of file */ + } } + pseq = GEOSCoordSeq_create(n_points, (Map->head.with_z) ? 3 : 2); + for (i = 0; i < n_points; i++) { GEOSCoordSeq_setX(pseq, i, x[i]); GEOSCoordSeq_setY(pseq, i, y[i]); @@ -382,6 +385,7 @@ GEOSCoordSequence *V1_read_line_geos(struct Map_info *Map, long offset, G_debug(3, " off = %ld", (long)dig_ftell(&(Map->dig_fp))); +free_return: G_free((void *)x); G_free((void *)y); if (z) From 15c5737dc601fbe71ab2bb614f14634ad17ddce0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 15 Oct 2024 07:19:47 -0400 Subject: [PATCH 388/471] CI(deps): Update github/codeql-action action to v3.26.13 (#4518) --- .github/workflows/codeql-analysis.yml | 4 ++-- .github/workflows/python-code-quality.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 88158341f13..6672287794c 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -56,7 +56,7 @@ jobs: if: ${{ matrix.language == 'c-cpp' }} - name: Initialize CodeQL - uses: github/codeql-action/init@c36620d31ac7c881962c3d9dd939c40ec9434f2b # v3.26.12 + uses: github/codeql-action/init@f779452ac5af1c261dce0346a8f964149f49322b # v3.26.13 with: languages: ${{ matrix.language }} config-file: ./.github/codeql/codeql-config.yml @@ -81,6 +81,6 @@ jobs: run: .github/workflows/build_ubuntu-22.04.sh "${HOME}/install" - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@c36620d31ac7c881962c3d9dd939c40ec9434f2b # v3.26.12 + uses: github/codeql-action/analyze@f779452ac5af1c261dce0346a8f964149f49322b # v3.26.13 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index b8b423b2b16..886cbe56b47 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -135,7 +135,7 @@ jobs: path: bandit.sarif - name: Upload SARIF File into Security Tab - uses: github/codeql-action/upload-sarif@c36620d31ac7c881962c3d9dd939c40ec9434f2b # v3.26.12 + uses: github/codeql-action/upload-sarif@f779452ac5af1c261dce0346a8f964149f49322b # v3.26.13 with: sarif_file: bandit.sarif From ca171b7e53a5ae075fe4857cb8bbdcb7f764d061 Mon Sep 17 00:00:00 2001 From: Nicklas Larsson Date: Tue, 15 Oct 2024 13:29:47 +0200 Subject: [PATCH 389/471] tests: enable use of md5 bin for checksum in raster_md5test.sh (#4527) Makes the script runnable on e.g. BSD platforms, which does not ship with md5sum. In addition fixes shellcheck warnings. --- testsuite/raster/raster_md5test.sh | 52 ++++++++++++++++-------------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/testsuite/raster/raster_md5test.sh b/testsuite/raster/raster_md5test.sh index 0bda0c3428c..b3281d4c505 100755 --- a/testsuite/raster/raster_md5test.sh +++ b/testsuite/raster/raster_md5test.sh @@ -14,19 +14,23 @@ if [ -z "$GISBASE" ] ; then fi #### check if we have sed -if [ ! -x "`which sed`" ] ; then +if [ ! -x "$(which sed)" ] ; then echo "$PROG: sed required, please install first" 1>&2 exit 1 fi -#### check if we have md5sum -if [ ! -x "`which md5sum`" ] ; then - echo "$PROG: md5sum required, please install first" 1>&2 - exit 1 +#### check if we have md5sum or md5 +if [ -x "$(which md5sum)" ] ; then + MD5="md5sum | cut -d' ' -f1" +elif [ -x "$(which md5)" ] ; then + MD5="md5 -q" +else + echo "$PROG: md5sum or md5 required, please install first" 1>&2 + exit 1 fi #### check if we have cut -if [ ! -x "`which cut`" ] ; then +if [ ! -x "$(which cut)" ] ; then echo "$PROG: cut required, please install first" 1>&2 exit 1 fi @@ -38,47 +42,47 @@ export LC_NUMERIC=C # enforce ZLIB export GRASS_COMPRESSOR=ZLIB -eval `g.gisenv` -: ${GISBASE?} ${GISDBASE?} ${LOCATION_NAME?} ${MAPSET?} +eval "$(g.gisenv)" +: "${GISBASE?}" "${GISDBASE?}" "${LOCATION_NAME?}" "${MAPSET?}" MAPSET_PATH=$GISDBASE/$LOCATION_NAME/$MAPSET # some definitions PIXEL=3 PID=$$ -TMPNAME="`echo ${PID}_tmp_testmap | sed 's+\.+_+g'`" +TMPNAME=$(echo ${PID}_tmp_testmap | sed 's+\.+_+g') # some functions - keep order here cleanup() { echo "Removing temporary map" - g.remove -f type=raster name=$TMPNAME > /dev/null + g.remove -f type=raster name="$TMPNAME" > /dev/null } # check if a MASK is already present: -MASKTMP=mask.$TMPNAME -USERMASK=usermask_${MASKTMP} -if test -f $MAPSET_PATH/cell/MASK +MASKTMP="mask.${TMPNAME}" +USERMASK="usermask_${MASKTMP}" +if test -f "${MAPSET_PATH}/cell/MASK" then echo "A user raster mask (MASK) is present. Saving it..." - g.rename raster=MASK,$USERMASK > /dev/null + g.rename raster=MASK,"$USERMASK" > /dev/null fi finalcleanup() { echo "Restoring user region" - g.region region=$TMPNAME - g.remove -f type=region name=$TMPNAME > /dev/null + g.region region="$TMPNAME" + g.remove -f type=region name="$TMPNAME" > /dev/null #restore user mask if present: - if test -f $MAPSET_PATH/cell/$USERMASK ; then + if test -f "${MAPSET_PATH}/cell/${USERMASK}" ; then echo "Restoring user MASK" g.remove -f type=raster name=MASK > /dev/null - g.rename raster=$USERMASK,MASK > /dev/null + g.rename raster="$USERMASK",MASK > /dev/null fi } check_exit_status() { - if [ $1 -ne 0 ] ; then + if [ "$1" -ne 0 ] ; then echo "An error occurred." cleanup ; finalcleanup exit 1 @@ -106,7 +110,7 @@ check_md5sum() } echo "Saving current & setting test region." -g.region save=$TMPNAME +g.region save="$TMPNAME" check_exit_status $? g.region s=0 n=$PIXEL w=0 e=$PIXEL res=1 tbres=1 check_exit_status $? @@ -118,8 +122,8 @@ r.mapcalc "$TMPNAME = 1" check_exit_status $? echo "MD5 checksum on output of INT/CELL test." -MD5="`r.out.ascii $TMPNAME precision=15 | md5sum | cut -d' ' -f1`" -check_md5sum "549e7dabe70df893803690571d2e1503" "$MD5" +SUM=$(r.out.ascii "$TMPNAME" precision=15 | eval "$MD5") +check_md5sum "549e7dabe70df893803690571d2e1503" "$SUM" cleanup echo "INT/CELL md5sum test successful" @@ -132,8 +136,8 @@ r.mapcalc "$TMPNAME = $VALUE" check_exit_status $? echo "MD5 checksum on output of FLOAT/FCELL test." -MD5="`r.out.ascii $TMPNAME precision=15 | md5sum | cut -d' ' -f1`" -check_md5sum "379f3d880b6d509051af6b4ccf470762" "$MD5" +SUM=$(r.out.ascii "$TMPNAME" precision=15 | eval "$MD5") +check_md5sum "379f3d880b6d509051af6b4ccf470762" "$SUM" cleanup echo "FLOAT/FCELL md5sum test successful" From f7d2ecfacca5f5bd9b0c0cd88752e7e0386b41d4 Mon Sep 17 00:00:00 2001 From: Markus Metz <33666869+metzm@users.noreply.github.com> Date: Tue, 15 Oct 2024 15:12:15 +0200 Subject: [PATCH 390/471] lib/vector/Vlib: always write out topo files in update mode (#3459) * Vlib: always write out topo files in update mode * delete support files only when closing * fix pygrass Vect_close --------- Co-authored-by: Huidae Cho --- lib/vector/Vlib/open.c | 1 + python/grass/pygrass/vector/abstract.py | 21 ++++++++------------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/lib/vector/Vlib/open.c b/lib/vector/Vlib/open.c index 501b31eb4cb..0008156c34a 100644 --- a/lib/vector/Vlib/open.c +++ b/lib/vector/Vlib/open.c @@ -572,6 +572,7 @@ int Vect__open_old(struct Map_info *Map, const char *name, const char *mapset, if (access(file_path, F_OK) == 0) /* fidx file exists? */ unlink(file_path); } + Map->support_updated = TRUE; } return level; diff --git a/python/grass/pygrass/vector/abstract.py b/python/grass/pygrass/vector/abstract.py index 6700dca8e4c..79307f0d354 100644 --- a/python/grass/pygrass/vector/abstract.py +++ b/python/grass/pygrass/vector/abstract.py @@ -457,14 +457,14 @@ def close(self, build=False): if hasattr(self, "table") and self.table is not None: self.table.conn.close() if self.is_open(): - if libvect.Vect_close(self.c_mapinfo) != 0: - str_err = "Error when trying to close the map with Vect_close" - raise GrassError(str_err) if ( self.c_mapinfo.contents.mode in {libvect.GV_MODE_RW, libvect.GV_MODE_WRITE} ) and build: self.build() + if libvect.Vect_close(self.c_mapinfo) != 0: + str_err = "Error when trying to close the map with Vect_close" + raise GrassError(str_err) def remove(self): """Remove vector map""" @@ -474,16 +474,11 @@ def remove(self): def build(self): """Close the vector map and build vector Topology""" - self.close() - libvect.Vect_set_open_level(1) - if libvect.Vect_open_old2(self.c_mapinfo, self.name, self.mapset, "0") != 1: - str_err = "Error when trying to open the vector map." - raise GrassError(str_err) - # Vect_build returns 1 on success and 0 on error (bool approach) - if libvect.Vect_build(self.c_mapinfo) != 1: - str_err = "Error when trying build topology with Vect_build" - raise GrassError(str_err) - libvect.Vect_close(self.c_mapinfo) + if self.is_open(): + # Vect_build returns 1 on success and 0 on error (bool approach) + if libvect.Vect_build(self.c_mapinfo) != 1: + str_err = "Error when trying build topology with Vect_build" + raise GrassError(str_err) if __name__ == "__main__": From c0c5380fb920042c9539490e21d30f9af2165b83 Mon Sep 17 00:00:00 2001 From: Anna Petrasova Date: Tue, 15 Oct 2024 09:58:35 -0400 Subject: [PATCH 391/471] r.sim.water: fix logfile writing (#4522) --- raster/r.sim/r.sim.sediment/main.c | 2 ++ raster/r.sim/r.sim.water/main.c | 4 +++- raster/r.sim/simlib/hydro.c | 3 --- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/raster/r.sim/r.sim.sediment/main.c b/raster/r.sim/r.sim.sediment/main.c index dd082fe0f81..d0197c2abb1 100644 --- a/raster/r.sim/r.sim.sediment/main.c +++ b/raster/r.sim/r.sim.sediment/main.c @@ -417,6 +417,8 @@ int main(int argc, char *argv[]) G_message(_("Using metric conversion factor %f, step=%f"), wp.conv, wp.step); + wp.observation = parm.observation->answer; + wp.logfile = parm.logfile->answer; init_library_globals(&wp); if ((wp.tc == NULL) && (wp.et == NULL) && (wp.conc == NULL) && diff --git a/raster/r.sim/r.sim.water/main.c b/raster/r.sim/r.sim.water/main.c index 61f128f5504..209be3e33d9 100644 --- a/raster/r.sim/r.sim.water/main.c +++ b/raster/r.sim/r.sim.water/main.c @@ -220,7 +220,7 @@ int main(int argc, char *argv[]) parm.logfile->required = NO; parm.logfile->description = _("Name for sampling points output text file. For each observation " - "vector point the time series of sediment transport is stored."); + "vector point the time series of water discharge is stored."); parm.logfile->guisection = _("Output"); parm.nwalk = G_define_option(); @@ -525,6 +525,8 @@ int main(int argc, char *argv[]) G_message(_("Using metric conversion factor %f, step=%f"), wp.conv, wp.step); + wp.observation = parm.observation->answer; + wp.logfile = parm.logfile->answer; init_library_globals(&wp); if ((wp.depth == NULL) && (wp.disch == NULL) && (wp.err == NULL)) diff --git a/raster/r.sim/simlib/hydro.c b/raster/r.sim/simlib/hydro.c index 74145ec9bff..eb7649763e9 100644 --- a/raster/r.sim/simlib/hydro.c +++ b/raster/r.sim/simlib/hydro.c @@ -151,9 +151,6 @@ void main_loop(void) maxwa = maxwa / nblock; } - /* Create the observation points */ - create_observation_points(); - G_debug(2, " maxwa, nblock %d %d", maxwa, nblock); for (iblock = 1; iblock <= nblock; iblock++) { From f22c9a2767960598ba99113eff595fda43bbfb8d Mon Sep 17 00:00:00 2001 From: Vaclav Petras Date: Tue, 15 Oct 2024 10:02:36 -0400 Subject: [PATCH 392/471] doc: Use lowercase for 2D raster mask and other masks (#4495) Similarly to #4401, this replaces usage of MASK by mask on additional places. It also improves wording of a warning in i.fft. --- imagery/i.fft/main.c | 5 +++-- imagery/i.pca/i.pca.html | 2 +- lib/ogsf/gk.c | 4 ++-- lib/ogsf/gvd.c | 2 +- raster/r.fill.dir/r.fill.dir.html | 8 ++++---- raster/r.li/TODO | 7 ++++--- raster/r.univar/r.univar.html | 2 +- 7 files changed, 16 insertions(+), 14 deletions(-) diff --git a/imagery/i.fft/main.c b/imagery/i.fft/main.c index ad9a87a7890..051428b77ef 100644 --- a/imagery/i.fft/main.c +++ b/imagery/i.fft/main.c @@ -105,8 +105,9 @@ int main(int argc, char *argv[]) inputfd = Rast_open_old(Cellmap_orig, ""); if (Rast_maskfd() >= 0) - G_warning(_("Raster MASK found, consider to remove " - "(see man-page). Will continue...")); + G_warning(_("Raster mask active, consider removing it" + " and running again without it (see documentation for" + " details). This current process will now continue...")); G_get_set_window(&window); /* get the current window for later */ diff --git a/imagery/i.pca/i.pca.html b/imagery/i.pca/i.pca.html index 17b3f5ed163..6fee184c618 100644 --- a/imagery/i.pca/i.pca.html +++ b/imagery/i.pca/i.pca.html @@ -11,7 +11,7 @@

    DESCRIPTION

    principal component with the highest importance.

    -The current geographic region definition and MASK settings are +The current geographic region definition and raster mask settings are respected when reading the input raster map layers. When the rescale option is used, the output files are rescaled to fit the min,max range. diff --git a/lib/ogsf/gk.c b/lib/ogsf/gk.c index 3390598960a..b4fea635cc4 100644 --- a/lib/ogsf/gk.c +++ b/lib/ogsf/gk.c @@ -172,8 +172,8 @@ void gk_follow_frames(Viewnode *view, int numsteps, Keylist *keys, int step, GS_get_from(tmp); G_debug(3, "gk_follow_frames():"); - G_debug(3, " MASK: %lx", mask); - G_debug(3, " FROM: %f %f %f", tmp[X], tmp[Y], tmp[Z]); + G_debug(3, " mask: %lx", mask); + G_debug(3, " from: %f %f %f", tmp[X], tmp[Y], tmp[Z]); /* ACS 1 line: was GS_get_focus(tmp); with this kanimator works also for flythrough navigation diff --git a/lib/ogsf/gvd.c b/lib/ogsf/gvd.c index 6a6176526a2..84867123c5e 100644 --- a/lib/ogsf/gvd.c +++ b/lib/ogsf/gvd.c @@ -201,7 +201,7 @@ int gvd_vect(geovect *gv, geosurf *gs, int do_fast) } gsd_endline(); } - /* need to handle MASK! */ + /* need to handle mask! */ else if (src == CONST_ATT) { /* for now - but later, do seg intersect maskedge */ if (gs_point_is_masked(gs, bgn) || diff --git a/raster/r.fill.dir/r.fill.dir.html b/raster/r.fill.dir/r.fill.dir.html index 6ad8eb8019c..17173ea3dd3 100644 --- a/raster/r.fill.dir/r.fill.dir.html +++ b/raster/r.fill.dir/r.fill.dir.html @@ -69,11 +69,11 @@

    DESCRIPTION

    attributes required by other hydrological models.

    -As any GRASS GIS module, r.fill.dir is sensitive to the -computational region settings. Thus +As any GRASS GIS module, r.fill.dir respects the +computational region settings. Thus, the module can be used to generate a flow direction map for any -sub-area within the full map layer. Also, r.fill.dir is -sensitive to any raster MASK in effect. +sub-area within the full raster map layer. Also, r.fill.dir +will take into account an active raster mask.

    NOTES

    diff --git a/raster/r.li/TODO b/raster/r.li/TODO index 61ddd6b0941..382195402a6 100644 --- a/raster/r.li/TODO +++ b/raster/r.li/TODO @@ -24,12 +24,13 @@ d.vect forests type=boundary ######## TODO: CHECK THIS: -# MASK test -g.copy rast=fields,MASK +# Test with raster mask +r.mask raster=fields r.li.patchdensity forests conf=movwindow7 output=forests_p_dens7mask --o d.erase d.rast.leg forests_p_dens7mask -# -> no negative values! but MASK is respected +r.mask -r +# -> no negative values! but mask is respected # zero data test r.mapcalc "forests = 0" diff --git a/raster/r.univar/r.univar.html b/raster/r.univar/r.univar.html index 5220bc64a44..debde03a1d6 100644 --- a/raster/r.univar/r.univar.html +++ b/raster/r.univar/r.univar.html @@ -53,7 +53,7 @@

    PERFORMANCE

    r.univar supports parallel processing using OpenMP. The user can specify the number of threads to be used with the nprocs parameter. -However, parallelization is disabled when the MASK is set. +However, parallelization is disabled when the raster mask is set.

    Due to the differences in summation order, users may encounter small floating points From ec2bc8a7df27b1984c878b7a0a209bad9e977acd Mon Sep 17 00:00:00 2001 From: Mohan Yelugoti Date: Tue, 15 Oct 2024 11:35:11 -0400 Subject: [PATCH 393/471] r3.in.v5d: Prevent integer overflow by changing literal constant type on Cray (#4363) When the code is being compiled for CRAY HPC machines, a macro and a function to convert IEEE single precision floating point number to CRAY number are defined. To adjust the base, '16258' constant is being used, which according to C rules (C99, section 6.4.4.1, subsection semantics) fits into an integer. Right shifting that integer, which is of 32 bits, by 48 results in integer overflow. Avoid this by defining the literal constant with the long data type. This comes with an extended discussion in PR #4363 and an idea to remove the code completely. Signed-off-by: Mohan Yelugoti --- raster3d/r3.in.v5d/binio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/raster3d/r3.in.v5d/binio.c b/raster3d/r3.in.v5d/binio.c index f8582e97313..b47723a2929 100644 --- a/raster3d/r3.in.v5d/binio.c +++ b/raster3d/r3.in.v5d/binio.c @@ -147,7 +147,7 @@ static void if_to_c(long *t, const long *f) { if (*f != 0) { *t = (((*f & 0x8000000000000000) | - ((*f & 0x7f80000000000000) >> 7) + (16258 << 48)) | + ((*f & 0x7f80000000000000) >> 7) + (16258L << 48)) | (((*f & 0x007fffff00000000) >> 8) | (0x0000800000000000))); if ((*f << 1) == 0) *t = 0; @@ -160,7 +160,7 @@ static void if_to_c(long *t, const long *f) #define IF_TO_C(T, F) \ if (F != 0) { \ T = (((F & 0x8000000000000000) | \ - ((F & 0x7f80000000000000) >> 7) + (16258 << 48)) | \ + ((F & 0x7f80000000000000) >> 7) + (16258L << 48)) | \ (((F & 0x007fffff00000000) >> 8) | (0x0000800000000000))); \ if ((F << 1) == 0) \ T = 0; \ From 1adbd2ced8411fcc1e375ac6d6d124c419e497f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Tue, 15 Oct 2024 11:59:46 -0400 Subject: [PATCH 394/471] CI: Assign milestone on merged PRs (#4414) * CI: Create a workflow to assign milestones * Add GH_TOKEN for gh cli * CI: Add GH_REPO for gh cli * CI: View PR from gh cli using html_url * CI: Download and parse version file * CI: Show version file * CI: Download and parse version file using a pipe * Show version file output Clean up * Pipe version file env to head * Rename variables to milestone and title * Edit PR with milestone * Get milestone from gh cli * Add comment on why API call is used for getting milestone * Show if the PR has a milestone set or not * Do not run steps if a milestone is already set * Add pull_request_target closed trigger * Add ref as url parameter * Remove debugging steps * CI: Handle RC followed by numbers in sed pattern replacement --- .github/workflows/milestones.yml | 70 ++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 .github/workflows/milestones.yml diff --git a/.github/workflows/milestones.yml b/.github/workflows/milestones.yml new file mode 100644 index 00000000000..e2ade4eb091 --- /dev/null +++ b/.github/workflows/milestones.yml @@ -0,0 +1,70 @@ +--- +name: Assign Milestone + +on: + pull_request_target: + types: [closed] + +jobs: + assign-milestone: + runs-on: ubuntu-latest + if: github.event.pull_request.merged + steps: + # Retreiving the current milestoone from API instead of github context, + # so up-to-date information is used when running after being queued or for reruns + # Otherwise, the information should be available using + # ${{ github.event.pull_request.milestone.title }} + - name: Get current milestone title + id: current-milestone + run: | + echo "milestone<> "${GITHUB_OUTPUT}" + gh pr view ${{ github.event.pull_request.html_url }} --json milestone \ + --jq .milestone.title >> "${GITHUB_OUTPUT}" + echo 'EOF' >> "${GITHUB_OUTPUT}" + env: + GH_TOKEN: ${{ github.token }} + GH_REPO: ${{ github.repository }} + - name: PR already has a milestone + run: echo "PR already has a milestone" + if: ${{ steps.current-milestone.outputs.milestone }} + - name: PR does not have a milestone + run: echo "PR does not have a milestone" + if: ${{ !steps.current-milestone.outputs.milestone }} + - name: Get VERSION file + if: ${{ !steps.current-milestone.outputs.milestone }} + id: version-file + run: | + echo "version<> "${GITHUB_OUTPUT}" + gh api \ + -H "Accept: application/vnd.github.raw" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "/repos/{owner}/{repo}/contents/include/VERSION?ref=${{ github.sha }}" >> "${GITHUB_OUTPUT}" + echo "EOF" >> "${GITHUB_OUTPUT}" + env: + GH_TOKEN: ${{ github.token }} + GH_REPO: ${{ github.repository }} + - name: Show version file + if: ${{ !steps.current-milestone.outputs.milestone }} + run: echo "${VERSIONFILE}" + env: + VERSIONFILE: ${{ steps.version-file.outputs.version }} + - name: Get milestone title from VERSION file + if: ${{ !steps.current-milestone.outputs.milestone }} + id: milestone + run: | + version=$(echo "$VERSIONFILE" | head -n 3 | xargs | sed 's/ /./g; s/\(RC[0-9]*\|dev\)//g') + echo "title=$version" >> "${GITHUB_OUTPUT}" + env: + VERSIONFILE: ${{ steps.version-file.outputs.version }} + - name: Show milestone title + if: ${{ !steps.current-milestone.outputs.milestone }} + run: echo "${MILESTONE}" + env: + MILESTONE: ${{ steps.milestone.outputs.title }} + - name: Set PR milestone + if: ${{ !steps.current-milestone.outputs.milestone }} + run: gh pr edit ${{ github.event.pull_request.html_url }} --milestone "${MILESTONE}" + env: + GH_TOKEN: ${{ github.token }} + GH_REPO: ${{ github.repository }} + MILESTONE: ${{ steps.milestone.outputs.title }} From 9a79aa4a9a637b7467cdfd7e0b2a2193f623f8a5 Mon Sep 17 00:00:00 2001 From: Vaclav Petras Date: Tue, 15 Oct 2024 19:09:57 -0400 Subject: [PATCH 395/471] tests: Revise content of the root testsuite dir (#4516) * Remove Makefiles. * Move the functioning test to the testsuite directory for the gunittest to pick it up. * Remove the extended example how to run gunittest. Point to the CI in the README instead. * Remove hemisphere generator because it does not test anything by itself (without further processing and a human). * Remove GPS import tests as the tools are not in core. * Remove division into subdirectories. The directories with the code should be used instead. * Improve the test for gunittest loader function with some duplication but now creating an overview of .py and .sh tests. --- python/grass/gunittest/loader.py | 16 +- testsuite/Makefile | 8 - testsuite/README.md | 44 +++--- .../test_framework_GRASS_GIS_with_NC.conf | 29 ---- .../test_framework_GRASS_GIS_with_NC.sh | 144 ------------------ testsuite/raster/Makefile | 3 - testsuite/raster/README | 1 - testsuite/raster/rhemisphere.sh | 48 ------ testsuite/{raster => }/raster_md5test.sh | 0 testsuite/vector/v.in.gps_test.sh | 26 ---- 10 files changed, 42 insertions(+), 277 deletions(-) delete mode 100644 testsuite/Makefile delete mode 100644 testsuite/examples/test_framework_GRASS_GIS_with_NC.conf delete mode 100755 testsuite/examples/test_framework_GRASS_GIS_with_NC.sh delete mode 100644 testsuite/raster/Makefile delete mode 100644 testsuite/raster/README delete mode 100755 testsuite/raster/rhemisphere.sh rename testsuite/{raster => }/raster_md5test.sh (100%) delete mode 100755 testsuite/vector/v.in.gps_test.sh diff --git a/python/grass/gunittest/loader.py b/python/grass/gunittest/loader.py index f01467ef1ba..50fa368f62a 100644 --- a/python/grass/gunittest/loader.py +++ b/python/grass/gunittest/loader.py @@ -235,4 +235,18 @@ def discover(self, start_dir, pattern="test*.py", top_level_dir=None): if __name__ == "__main__": - GrassTestLoader().discover() + for expression in [r".*\.py$", r".*\.sh$"]: + modules = discover_modules( + start_dir=".", + grass_location="all", + file_regexp=expression, + skip_dirs=GrassTestLoader.skip_dirs, + testsuite_dir=GrassTestLoader.testsuite_dir, + all_locations_value=GrassTestLoader.all_tests_value, + universal_location_value=GrassTestLoader.universal_tests_value, + import_modules=False, + exclude=None, + ) + print("Expression:", expression) + print(len(modules)) + print([module.file_path for module in modules]) diff --git a/testsuite/Makefile b/testsuite/Makefile deleted file mode 100644 index 7ed92b67442..00000000000 --- a/testsuite/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -MODULE_TOPDIR = .. - -SUBDIRS = \ - raster - -include $(MODULE_TOPDIR)/include/Make/Dir.make - -default: parsubdirs diff --git a/testsuite/README.md b/testsuite/README.md index 491fa4597af..36dda7d3da2 100644 --- a/testsuite/README.md +++ b/testsuite/README.md @@ -1,29 +1,39 @@ # Test suite -This directory contains scripts to check some functionality of GRASS GIS. +Tests are in directories `tests` and `testsuite` under each directory which has tests. +This directory contains additional scripts and information to test functionality +without a focus on a specific part of the code. -GRASS GIS testsuite documentation: +There are two testing mechanism in place, _pytest_ which is the modern way of testing +GRASS GIS. Tests using _pytest_ are written just as any other Python tests. -## Simple test data +In parallel, there is also custom unittest-based framework centered around +_grass.gunittest_ package. These tests run in the NC sample datasets and can be +executed using _pytest_ or directly. The unittest-based adds a number of custom +assert methods to accommodate different data and outputs typical in GRASS GIS. +_grass.gunittest_ documentation: + -Some tests may be launched in the location `../demolocation/`: +## Running tests + +Tests can be executed using _pytest_: ```bash -# create new mapset for test -grass ../demolocation/user1 -c -# run the test -make +# Setup the Python environment (if not set up already). +# Replace grass by path to the executable if not installed on path. +export PYTHONPATH=\$(grass --config python_path):\$PYTHONPATH +export LD_LIBRARY_PATH=\$(grass --config path)/lib:\$LD_LIBRARY_PATH +# Run the test. +pytest ``` -## Extended test data - -Most tests require the North Carolina Sample dataset, available from - +## Test data -## Notes +To test manually or to write tests, you may need to use the North Carolina +Sample dataset, available from +. -Since 2020: For a more advanced test suite, see - +## CI -Until 2019: For a more advanced test suite, see - +Most tests run in the CI. See the `.github` directory for details and +use it as a reference. diff --git a/testsuite/examples/test_framework_GRASS_GIS_with_NC.conf b/testsuite/examples/test_framework_GRASS_GIS_with_NC.conf deleted file mode 100644 index 6256108f6a8..00000000000 --- a/testsuite/examples/test_framework_GRASS_GIS_with_NC.conf +++ /dev/null @@ -1,29 +0,0 @@ -### CONFIGURATION -# -# name of binary: -GRASSBIN=grass -# source code directory as full path: -GRASSSRC="$(realpath ../../)" -# temporary grassdata directory -GRASSDATA="$HOME/grassdata" - -# leave 1 or more CPU free for other usage than testing -FREECPU=1 - -# Python binary to be used (python|python3) -PYTHON=python - -# here we suppose default compilation settings of GRASS GIS and no 'make install' -# may be no|yes -COMPILE="no" -# configure metascript with compiler flags: -CONFIGURE="${GRASSSRC}/conf_grass8.sh" - -# directory to store reports, e.g. in a subdirectory -REPORTS="testreports" - -# publish report on WWW Server (not needed for local tests) -# may be no|yes -PUBLISH="no" -# upload WWW dir on server for report publication (not used for local tests) -SERVERDIR="/var/www/html/grassgistestreports" diff --git a/testsuite/examples/test_framework_GRASS_GIS_with_NC.sh b/testsuite/examples/test_framework_GRASS_GIS_with_NC.sh deleted file mode 100755 index 808b4b9c716..00000000000 --- a/testsuite/examples/test_framework_GRASS_GIS_with_NC.sh +++ /dev/null @@ -1,144 +0,0 @@ -#!/bin/bash -############################################################################ -# -# MODULE: Example script to run testsuite -# AUTHOR(S): Markus Neteler, Sören Gebbert, Vaclav Petras -# PURPOSE: Test GRASS GIS using the test framework -# Documentation: -# https://trac.osgeo.org/grass/wiki/GSoC/2014/TestingFrameworkForGRASS -# https://grass.osgeo.org/grass-devel/manuals/libpython/gunittest_running_tests.html#example-bash-script-to-run-be-used-as-a-cron-job -# -# Data: -# We use the full NC dataset (nc_spm_full_v2_alpha.tar.gz) -# -# COPYRIGHT: (C) 2019-2021 by Markus Neteler, and the GRASS Development Team -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -############################################################################ - -### Fetch CONFIGURATION - -CONF="test_framework_GRASS_GIS_with_NC.conf" - -usage_msg(){ -echo "Usage: - $0 [conf_file] - -Example: - $0 ./${CONF} -" -} - -if [ ! -z "$1" ] ; then - case "$1" in - -h | --h | -help | --help) - usage_msg - exit 0 - ;; - *) - if [ -f ${1} ] ; then - CONF="$1" - else - echo "ERROR: $1 is not a file" - exit 1 - fi - ;; - esac -else - usage_msg - exit 0 -fi - -source ${CONF} - -######### nothing to change below - -set -e # fail fast - -# computer architecture: -ARCH=`${GRASSBIN} --config arch` - -# here we suppose default compilation settings of GRASS GIS and no make install -GRASSBIN="$GRASSSRC/bin.${ARCH}/${GRASSBIN}" -GRASSDIST="$GRASSSRC/dist.${ARCH}" - -# necessary hardcoded GRASS paths -GRASSDIST_PYTHON="$GRASSDIST/etc/python" -GRASS_MULTI_RUNNER="$GRASSSRC/python/grass/gunittest/multirunner.py" -GRASS_MULTI_REPORTER="$GRASSSRC/python/grass/gunittest/multireport.py" - -DATE_FLAGS="--utc +%Y-%m-%d-%H-%M" -NOW=$(date $DATE_FLAGS) - -# get number of processors of current machine -MYNPROC=`getconf _NPROCESSORS_ONLN` -# leave some free for other tasks -GCCTHREADS=`expr $MYNPROC - $FREECPU` -if [ $GCCTHREADS -lt 1 ] ; then - GCCTHREADS=1 -fi - -# contains last executed command stdout and stderr -# here were rely on reports being absolute -OUTPUT_LOGFILE="$REPORTS/output-$NOW.txt" - -# these are relative to REPORTS -CURRENT_REPORT_BASENAME="reports_for_date-" -FINAL_REPORT_DIR="summary_report" -CURRENT_REPORTS_DIR="$CURRENT_REPORT_BASENAME$NOW" -LOGFILE="$REPORTS/runs.log" - -mkdir -p $REPORTS/$CURRENT_REPORTS_DIR -mkdir -p $GRASSDATA - -# fetch sample data -SAMPLEDATA=nc_spm_full_v2alpha -(cd $GRASSDATA ; wget -c https://grass.osgeo.org/sampledata/north_carolina/$SAMPLEDATA.tar.gz ; tar xfz $SAMPLEDATA.tar.gz --strip-components 2) - -set -x - -echo "Testing of GRASS GIS started: $NOW" >> ${LOGFILE} - -if [ "$COMPILE" = "yes" ] ; then - ## compile current source code from scratch - cd $GRASSSRC - make distclean -j$GCCTHREADS - git pull - ./$CONFIGURE ... # configure meta script containing all the compiler flags - make -j$GCCTHREADS -fi - -# run tests for the current source code -cd $REPORTS/$CURRENT_REPORTS_DIR -$PYTHON $GRASS_MULTI_RUNNER \ - --grassbin $GRASSBIN \ - --grasssrc $GRASSSRC \ - --grassdata $GRASSDATA \ - --location $SAMPLEDATA --location-type nc # \ -# --location other_location --location-type other_type - -# create overall report of all so far executed tests -# the script depends on GRASS but just Python part is enough -export PYTHONPATH="$GRASSDIST_PYTHON:$PYTHONPATH" -$PYTHON $GRASS_MULTI_REPORTER --output $FINAL_REPORT_DIR \ - $CURRENT_REPORT_BASENAME*/* - -# publish on Web site -if [ "$PUBLISH" = "yes" ] ; then - ## although we cannot be sure the tests were executed was successfully - ## so publish or archive results - rsync -rtvu --delete $REPORTS/ $SERVERDIR -fi - -echo "Nightly ($NOW) GRASS GIS test finished: $(date $DATE_FLAGS)" >> ${LOGFILE} - -exit 0 diff --git a/testsuite/raster/Makefile b/testsuite/raster/Makefile deleted file mode 100644 index 21c1f8eb6c0..00000000000 --- a/testsuite/raster/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -all: - ./raster_md5test.sh - ./rmapcalc_test.sh diff --git a/testsuite/raster/README b/testsuite/raster/README deleted file mode 100644 index bc5b0940789..00000000000 --- a/testsuite/raster/README +++ /dev/null @@ -1 +0,0 @@ -Raster map tests go here diff --git a/testsuite/raster/rhemisphere.sh b/testsuite/raster/rhemisphere.sh deleted file mode 100755 index e312f7b36d1..00000000000 --- a/testsuite/raster/rhemisphere.sh +++ /dev/null @@ -1,48 +0,0 @@ -#!/bin/sh - -# Markus Neteler, 2006 -# This program is free software under the GNU General Public -# License (>=v2). Read the file COPYING that comes with GRASS -# for details. -# Test cases for 2D raster data -# generate a hemisphere to test slope, aspect, curvatures - -# some definitions: -BOXLENGTH=1000 # side length of test area -RADIUS=500 # half BOXLENGTH - -############ - -if [ -z "$GISBASE" ] ; then - echo "You must be in GRASS GIS to run this program." >&2 - exit 1 -fi - -# some functions - keep order here -TMP="disk.$$" - -cleanup() -{ - echo "Removing temporary map" - g.remove --q -f type=raster name=$TMP > /dev/null -} - -######################## - -g.region n=$BOXLENGTH s=0 w=0 e=$BOXLENGTH -p res=1 - -X="(col() - $RADIUS)" -Y="($RADIUS - row())" -r="sqrt($X^2 + $Y^2)" - -#Mask out unwanted parts (check for <= ??): -r.mapcalc "$TMP = if($r<$RADIUS,$r,null())" - -ALPHA="acos ($TMP/$RADIUS)" -HEIGHT="$RADIUS * sin($ALPHA)" - - -r.mapcalc "hemisphere = $HEIGHT" -cleanup -g.message "Generated raster map " -#echo "Now generate aspect + slope on " diff --git a/testsuite/raster/raster_md5test.sh b/testsuite/raster_md5test.sh similarity index 100% rename from testsuite/raster/raster_md5test.sh rename to testsuite/raster_md5test.sh diff --git a/testsuite/vector/v.in.gps_test.sh b/testsuite/vector/v.in.gps_test.sh deleted file mode 100755 index 62b2f5b651c..00000000000 --- a/testsuite/vector/v.in.gps_test.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/sh -# v.in.garmin and v.in.gpsbabel test script -# by Hamish Bowman 18 May 2007 ; public domain -# assumes downloading from a garmin on port /dev/gps - -for PGM in garmin gpsbabel ; do - for DO_PTS in pts lines ; do - for TASK in w t r ; do - if [ $TASK = w ] && [ $DO_PTS = "lines" ] ; then - continue - fi - if [ $DO_PTS = "pts" ] ; then - PTFLAG="-p" - else - PTFLAG="" - fi - MAPNAME="test_${PGM}_${TASK}_${DO_PTS}_$$" - echo "-- Running [v.in.$PGM] for [$TASK] download as [$DO_PTS] into <$MAPNAME> --" - v.in.$PGM -$TASK $PTFLAG out="$MAPNAME" - if [ $? -ne 0 ] ; then - exit 1 - fi - awk 'BEGIN {printf("\n\n\n\n")}' - done - done -done From bc9cb2a3f9540b031a6b8e8e7d7257ca01492d3e Mon Sep 17 00:00:00 2001 From: Vaclav Petras Date: Tue, 15 Oct 2024 19:17:08 -0400 Subject: [PATCH 396/471] doc: Use lowercase for mask (#4529) Again, use lowercase mask and other wording instead of MASK when talking about masking, similar to #4401 and #4495. It also adjusts the wording in r.topidx and i.segment. --- imagery/i.segment/i.segment.html | 2 +- imagery/i.segment/iseg.h | 2 +- ps/ps.map/r_instructions.c | 2 +- raster/r.resamp.interp/r.resamp.interp.html | 2 +- raster/r.topidx/r.topidx.html | 8 +++++--- 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/imagery/i.segment/i.segment.html b/imagery/i.segment/i.segment.html index 1e2596c1d7e..e30f21d3743 100644 --- a/imagery/i.segment/i.segment.html +++ b/imagery/i.segment/i.segment.html @@ -118,7 +118,7 @@

    Mean shift

    Boundary Constraints

    Boundary constraints limit the adjacency of pixels and segments. Each unique value present in the bounds raster are -considered as a MASK. Thus no segments in the final segmentated map +considered as a mask. Thus, no segments in the final segmented map will cross a boundary, even if their spectral data is very similar.

    Minimum Segment Size

    diff --git a/imagery/i.segment/iseg.h b/imagery/i.segment/iseg.h index 1e301d2cefa..a8dfb5542c3 100644 --- a/imagery/i.segment/iseg.h +++ b/imagery/i.segment/iseg.h @@ -118,7 +118,7 @@ struct globals { /* processing flags */ FLAG *candidate_flag, - *null_flag; /*TODO, need some way to remember MASK/NULL values. Was + *null_flag; /*TODO, need some way to remember mask/NULL values. Was using -1, 0, 1 in int array. Better to use 2 FLAG structures, better readability? */ diff --git a/ps/ps.map/r_instructions.c b/ps/ps.map/r_instructions.c index 0e2c012fd92..87a2e4bbfcf 100644 --- a/ps/ps.map/r_instructions.c +++ b/ps/ps.map/r_instructions.c @@ -29,7 +29,7 @@ static char *help[] = { "read unix-file eps Encapsulated PostScript file", "border [y|n] mapinfo map information", "window region definition region region definition", - "maskcolor MASK color", + "maskcolor mask color", "rectangle east north east north", "scale 1:#|# inches|# panels|1 inch = # miles", "outline map composition outline", diff --git a/raster/r.resamp.interp/r.resamp.interp.html b/raster/r.resamp.interp/r.resamp.interp.html index e4b77ac0d34..3c3eecc68a9 100644 --- a/raster/r.resamp.interp/r.resamp.interp.html +++ b/raster/r.resamp.interp/r.resamp.interp.html @@ -28,7 +28,7 @@

    NOTES

    Note that for bilinear, bicubic and lanczos interpolation, cells of the output raster that cannot be bounded by the appropriate number of input cell centers are set to NULL (NULL propagation). This could occur -due to the input cells being outside the current region, being NULL or MASKed. +due to the input cells being outside the current region, being NULL or masked.

    For longitude-latitude coordinate reference systems, diff --git a/raster/r.topidx/r.topidx.html b/raster/r.topidx/r.topidx.html index 56dd001f20a..75938e6a231 100644 --- a/raster/r.topidx/r.topidx.html +++ b/raster/r.topidx/r.topidx.html @@ -10,12 +10,14 @@

    DESCRIPTION

    the local surface topographic slope (delta vertical) / (delta horizontal).
    -

    Input maps may have NULL values. For example, if you have a MASK for a -watershed (basin map from r.water.outlet), the following command will -create a masked elevation map (belev): +

    Input maps may have NULL values. For example, if you have a raster mask set +for a watershed (using basin map from r.water.outlet), the following +command will create a masked elevation map (belev): +

     r.mapcalc "belev = if(isnull(basin), basin, elev)"
     
    +

    r.stats -Anc prints out averaged statistics for topographic index. From 241589424d9612b913ef7f5502839ee6ab2151bc Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Wed, 16 Oct 2024 04:08:22 -0400 Subject: [PATCH 397/471] r.in.gridatb: check if opening of file succeeds (#4532) --- raster/r.in.gridatb/file_io.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/raster/r.in.gridatb/file_io.c b/raster/r.in.gridatb/file_io.c index 256f9869473..52ad174df4d 100644 --- a/raster/r.in.gridatb/file_io.c +++ b/raster/r.in.gridatb/file_io.c @@ -12,6 +12,9 @@ void rdwr_gridatb(void) float idx; fp = fopen(file, "r"); + if (!fp) { + G_fatal_error(_("Unable to open file: %s"), file); + } buf[0] = 0; if (fscanf(fp, "%[^\n]", buf) != 1) From 362bee3ae66cec995805e355bb40cffe75c838f7 Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Wed, 16 Oct 2024 09:27:43 -0400 Subject: [PATCH 398/471] grass.script: Fixed E722 in v.what.shrds/ (#4530) --- .flake8 | 2 +- scripts/v.what.strds/v.what.strds.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.flake8 b/.flake8 index c01dc46b255..6bfcc1e5527 100644 --- a/.flake8 +++ b/.flake8 @@ -115,7 +115,7 @@ per-file-ignores = scripts/r.in.srtm/r.in.srtm.py: E722 scripts/r.fillnulls/r.fillnulls.py: E722 scripts/d.rast.edit/d.rast.edit.py: E722 - scripts/v.what.strds/v.what.strds.py: E722, E501 + scripts/v.what.strds/v.what.strds.py: E501 # Line too long (esp. module interface definitions) scripts/*/*.py: E501 temporal/t.rast.to.vect/t.rast.to.vect.py: E501 diff --git a/scripts/v.what.strds/v.what.strds.py b/scripts/v.what.strds/v.what.strds.py index 0e1bf893f98..8be00f3536d 100644 --- a/scripts/v.what.strds/v.what.strds.py +++ b/scripts/v.what.strds/v.what.strds.py @@ -225,7 +225,7 @@ def main(): pymap = Vector(output) try: pymap.open("r") - except: + except Exception: dbif.close() gs.fatal(_("Unable to create vector map <%s>") % output) From 39ff021208e8cbca0615c3545ffa9d191e149e88 Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Thu, 17 Oct 2024 10:49:45 -0400 Subject: [PATCH 399/471] wxGUI: Fixed F841 in mapwindow.py (#4538) --- gui/wxpython/nviz/mapwindow.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gui/wxpython/nviz/mapwindow.py b/gui/wxpython/nviz/mapwindow.py index 8cf972704e2..1eea5b278a9 100644 --- a/gui/wxpython/nviz/mapwindow.py +++ b/gui/wxpython/nviz/mapwindow.py @@ -412,7 +412,7 @@ def OnPaint(self, event): Debug.msg(1, "GLCanvas.OnPaint()") self.render["overlays"] = True - dc = wx.PaintDC(self) + wx.PaintDC(self) self.DoPaint() def DoPaint(self): @@ -1438,7 +1438,7 @@ def UnloadDataLayers(self, force=False): GError(parent=self, message=e.value) if force and self.baseId > 0: # unload base surface when quitting - ret = self._display.UnloadSurface(self.baseId) + self._display.UnloadSurface(self.baseId) self.baseId = -1 if update: self.lmgr.nviz.UpdateSettings() @@ -2134,10 +2134,10 @@ def UpdateVolumeProperties(self, id, data, isosurfId=None): # sliceId = 0 for slice in data["slice"]: - ret = self._display.AddSlice(id, slice_id=sliceId) + self._display.AddSlice(id, slice_id=sliceId) if "update" in slice["position"]: pos = slice["position"] - ret = self._display.SetSlicePosition( + self._display.SetSlicePosition( id, sliceId, pos["x1"], From a740da1d4a638c0e129ac95e7c298961f06f3111 Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Thu, 17 Oct 2024 10:51:50 -0400 Subject: [PATCH 400/471] wxGUI: Added TypeError to lmgr/ (#4537) --- gui/wxpython/lmgr/frame.py | 6 +++--- gui/wxpython/lmgr/layertree.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/gui/wxpython/lmgr/frame.py b/gui/wxpython/lmgr/frame.py index 2413d3b7c4a..963a36e3a58 100644 --- a/gui/wxpython/lmgr/frame.py +++ b/gui/wxpython/lmgr/frame.py @@ -1135,7 +1135,7 @@ def GetMenuCmd(self, event): layer = self.GetLayerTree().layer_selected name = self.GetLayerTree().GetLayerInfo(layer, key="maplayer").name type = self.GetLayerTree().GetLayerInfo(layer, key="type") - except AttributeError: + except (AttributeError, TypeError): layer = None if layer and len(cmdlist) == 1: # only if no parameters given @@ -1183,7 +1183,7 @@ def OnVDigit(self, event): # available only for vector map layers try: mapLayer = tree.GetLayerInfo(layer, key="maplayer") - except AttributeError: + except (AttributeError, TypeError): mapLayer = None if not mapLayer or mapLayer.GetType() != "vector": @@ -1860,7 +1860,7 @@ def OnShowAttributeTable(self, event, selection=None): # available only for vector map layers try: maptype = tree.GetLayerInfo(layer, key="maplayer").type - except AttributeError: + except (AttributeError, TypeError): maptype = None if not maptype or maptype != "vector": diff --git a/gui/wxpython/lmgr/layertree.py b/gui/wxpython/lmgr/layertree.py index 86531bcb5ad..855896e35ab 100644 --- a/gui/wxpython/lmgr/layertree.py +++ b/gui/wxpython/lmgr/layertree.py @@ -1758,7 +1758,7 @@ def OnDeleteLayer(self, event): try: if self.GetLayerInfo(item, key="type") != "group": self.Map.DeleteLayer(self.GetLayerInfo(item, key="maplayer")) - except AttributeError: + except (AttributeError, TypeError): pass # redraw map if auto-rendering is enabled @@ -2399,7 +2399,7 @@ def __FindSubItemByName(self, item, value): while item and item.IsOk(): try: itemLayer = self.GetLayerInfo(item, key="maplayer") - except KeyError: + except (KeyError, TypeError): return None if itemLayer and value == itemLayer.GetName(): From 4e8b0bf5e890f398b528f630c112b86b5a6d9471 Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Thu, 17 Oct 2024 11:01:06 -0400 Subject: [PATCH 401/471] wxGUI: Fixed F841 in modules/ (#4528) --- .flake8 | 2 +- gui/wxpython/modules/histogram.py | 1 - gui/wxpython/modules/import_export.py | 11 ++--------- gui/wxpython/modules/mapsets_picker.py | 2 +- 4 files changed, 4 insertions(+), 12 deletions(-) diff --git a/.flake8 b/.flake8 index 6bfcc1e5527..dfc22985b42 100644 --- a/.flake8 +++ b/.flake8 @@ -24,7 +24,7 @@ per-file-ignores = doc/python/m.distance.py: E501 gui/scripts/d.wms.py: E501 gui/wxpython/image2target/g.gui.image2target.py: E501 - gui/wxpython/modules/*: F841, E722 + gui/wxpython/modules/*: E722 gui/wxpython/nviz/*: F841, E266, E722, F403, F405 gui/wxpython/photo2image/*: F841, E722, E265 gui/wxpython/photo2image/g.gui.photo2image.py: E501, F841 diff --git a/gui/wxpython/modules/histogram.py b/gui/wxpython/modules/histogram.py index 92510406ac7..c8001858dff 100644 --- a/gui/wxpython/modules/histogram.py +++ b/gui/wxpython/modules/histogram.py @@ -496,7 +496,6 @@ def SaveToFile(self, event): def PrintMenu(self, event): """Print options and output menu""" - point = wx.GetMousePosition() printmenu = Menu() # Add items to the menu setup = wx.MenuItem(printmenu, id=wx.ID_ANY, text=_("Page setup")) diff --git a/gui/wxpython/modules/import_export.py b/gui/wxpython/modules/import_export.py index f2f70e47831..a0bd17e289c 100644 --- a/gui/wxpython/modules/import_export.py +++ b/gui/wxpython/modules/import_export.py @@ -461,7 +461,6 @@ def OnRun(self, event): dsn = self.dsnInput.GetDsn() if not dsn: return - ext = self.dsnInput.GetFormatExt() for layer, output, listId in data: userData = {} @@ -613,7 +612,7 @@ def OnRun(self, event): return if not data: - GMessage(_("No layers selected. Operation canceled."), parent=self) + GMessage(parent=self, message=_("No layers selected. Operation canceled.")) return if not self._validateOutputMapName(): @@ -644,13 +643,7 @@ def OnRun(self, event): if ext and layer.rfind(ext) > -1: layer = layer.replace("." + ext, "") if "|" in layer: - layer, geometry = layer.split("|", 1) - else: - geometry = None - - # TODO: v.import has no geometry option - # if geometry: - # cmd.append('geometry=%s' % geometry) + layer = layer.split("|", 1)[0] cmd = self.getSettingsPageCmd() cmd.append("input=%s" % dsn) diff --git a/gui/wxpython/modules/mapsets_picker.py b/gui/wxpython/modules/mapsets_picker.py index e7ea7a3e40a..73df8fde3cc 100755 --- a/gui/wxpython/modules/mapsets_picker.py +++ b/gui/wxpython/modules/mapsets_picker.py @@ -12,7 +12,7 @@ def main(): - app = wx.App() + wx.App() dlg = MapsetAccess(parent=None) dlg.CenterOnScreen() From 941b52f87c44e6f19b3be960553d37deb7c0f1e8 Mon Sep 17 00:00:00 2001 From: Anna Petrasova Date: Thu, 17 Oct 2024 11:02:44 -0400 Subject: [PATCH 402/471] GUI: fix crashing due to File menu translation issues (#4513) --- gui/wxpython/lmgr/workspace.py | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/gui/wxpython/lmgr/workspace.py b/gui/wxpython/lmgr/workspace.py index 774c22bf4ed..b1e7aa400f0 100644 --- a/gui/wxpython/lmgr/workspace.py +++ b/gui/wxpython/lmgr/workspace.py @@ -519,12 +519,20 @@ def CreateRecentFilesMenu(self, menu=None): :return None """ if menu: - file_menu = menu.GetMenu( - menuIndex=menu.FindMenu(title=_("File")), - ) - workspace_item = file_menu.FindItem( - id=file_menu.FindItem(itemString=_("Workspace")), - )[0] + menu_index = menu.FindMenu(_("File")) + if menu_index == wx.NOT_FOUND: + # try untranslated version + menu_index = menu.FindMenu("File") + if menu_index == wx.NOT_FOUND: + return + file_menu = menu.GetMenu(menu_index) + workspace_index = file_menu.FindItem(_("Workspace")) + if workspace_index == wx.NOT_FOUND: + workspace_index = file_menu.FindItem("Workspace") + if workspace_index == wx.NOT_FOUND: + return + workspace_item = file_menu.FindItemById(workspace_index) + self._recent_files = RecentFilesMenu( app_name="main", parent_menu=workspace_item.GetSubMenu(), From fd347b9e837a6903d1f917bd56edd3aa8a9995ba Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Thu, 17 Oct 2024 14:25:48 -0400 Subject: [PATCH 403/471] wxGUI: Fixed F841 in tools.py (#4539) --- .flake8 | 4 ++-- gui/wxpython/nviz/tools.py | 21 +++------------------ 2 files changed, 5 insertions(+), 20 deletions(-) diff --git a/.flake8 b/.flake8 index dfc22985b42..afe61c74642 100644 --- a/.flake8 +++ b/.flake8 @@ -24,8 +24,8 @@ per-file-ignores = doc/python/m.distance.py: E501 gui/scripts/d.wms.py: E501 gui/wxpython/image2target/g.gui.image2target.py: E501 - gui/wxpython/modules/*: E722 - gui/wxpython/nviz/*: F841, E266, E722, F403, F405 + gui/wxpython/modules/*: F841, E722 + gui/wxpython/nviz/*: E266, E722, F403, F405 gui/wxpython/photo2image/*: F841, E722, E265 gui/wxpython/photo2image/g.gui.photo2image.py: E501, F841 gui/wxpython/psmap/*: F841, E266, E722, F405, F403 diff --git a/gui/wxpython/nviz/tools.py b/gui/wxpython/nviz/tools.py index 20b1df1ffbd..153f8ffef86 100644 --- a/gui/wxpython/nviz/tools.py +++ b/gui/wxpython/nviz/tools.py @@ -667,7 +667,6 @@ def _createAnimationPage(self): parent=panel, id=wx.ID_ANY, label=" %s " % (_("Save image sequence")) ) boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL) - vSizer = wx.BoxSizer(wx.VERTICAL) gridSizer = wx.GridBagSizer(vgap=5, hgap=10) pwd = str(Path.cwd()) @@ -2817,7 +2816,6 @@ def OnAnimationFinished(self, mode): self.UpdateFrameIndex(index=0) slider = self.FindWindowById(self.win["anim"]["frameIndex"]["slider"]) - text = self.FindWindowById(self.win["anim"]["frameIndex"]["text"]) if mode == "record": count = anim.GetFrameCount() @@ -2922,7 +2920,7 @@ def OnConstantSelection(self, event): layerIdx = self.FindWindowById(self.win["constant"]["surface"]).GetSelection() if layerIdx == wx.NOT_FOUND: return - name = _("constant#") + str(layerIdx + 1) + data = self.mapWindow.constants[layerIdx] for attr, value in data["constant"].items(): if attr == "color": @@ -3123,8 +3121,6 @@ def _createIsosurfacePanel(self, parent): def _createSlicePanel(self, parent): panel = wx.Panel(parent=parent, id=wx.ID_ANY) - vSizer = wx.BoxSizer(wx.HORIZONTAL) - box = StaticBox( parent=panel, id=wx.ID_ANY, label=" %s " % (_("Slice attributes")) ) @@ -3412,12 +3408,11 @@ def OnSetSurface(self, event): """Surface selected, currently used for fringes""" name = event.GetString() try: - data = self._getLayerPropertiesByName(name, mapType="raster")["surface"] + self._getLayerPropertiesByName(name, mapType="raster")["surface"] except: self.EnablePage("fringe", False) return - layer = self._getMapLayerByName(name, mapType="raster") self.EnablePage("fringe", True) def OnSetRaster(self, event): @@ -3425,7 +3420,7 @@ def OnSetRaster(self, event): name = event.GetString() try: data = self._getLayerPropertiesByName(name, mapType="raster")["surface"] - except TypeError as e: + except TypeError: self.EnablePage("surface", False) return @@ -4590,10 +4585,6 @@ def OnVolumeSelect(self, event): if not winUp.IsEnabled(): winUp.Enable() - # update dialog - name = self.FindWindowById(self.win["volume"]["map"]).GetValue() - layer = self._getMapLayerByName(name, mapType="raster_3d") - if mode == "isosurf": data = self.GetLayerData("volume")["volume"]["isosurface"][selection] self.UpdateVolumeIsosurfPage(data) @@ -4694,8 +4685,6 @@ def OnVolumeDelete(self, event): if list.GetCount() > 0: list.SetSelection(list.GetCount() - 1) - name = self.FindWindowById(self.win["volume"]["map"]).GetValue() - layer = self._getMapLayerByName(name, mapType="raster_3d") data = self.GetLayerData("volume")["volume"] vid = data["object"]["id"] @@ -4736,8 +4725,6 @@ def OnVolumeMoveUp(self, event): if sel < 1: return # this should not happen - name = self.FindWindowById(self.win["volume"]["map"]).GetValue() - layer = self._getMapLayerByName(name, mapType="raster_3d") data = self.GetLayerData("volume")["volume"] id = data["object"]["id"] @@ -4776,8 +4763,6 @@ def OnVolumeMoveDown(self, event): if sel >= list.GetCount() - 1: return # this should not happen - name = self.FindWindowById(self.win["volume"]["map"]).GetValue() - layer = self._getMapLayerByName(name, mapType="raster_3d") data = self.GetLayerData("volume")["volume"] id = data["object"]["id"] From daee9896da48bced7d59cfb998952f7ff6e2245b Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Thu, 17 Oct 2024 14:27:05 -0400 Subject: [PATCH 404/471] wxGUI: Fixed F405 in psmap/ (#4541) --- .flake8 | 2 +- gui/wxpython/psmap/dialogs.py | 53 +++++++++++++++++++++------ gui/wxpython/psmap/frame.py | 57 +++++++++++++++++++++--------- gui/wxpython/psmap/instructions.py | 20 ++++++++--- 4 files changed, 100 insertions(+), 32 deletions(-) diff --git a/.flake8 b/.flake8 index afe61c74642..5c23d51bd36 100644 --- a/.flake8 +++ b/.flake8 @@ -28,7 +28,7 @@ per-file-ignores = gui/wxpython/nviz/*: E266, E722, F403, F405 gui/wxpython/photo2image/*: F841, E722, E265 gui/wxpython/photo2image/g.gui.photo2image.py: E501, F841 - gui/wxpython/psmap/*: F841, E266, E722, F405, F403 + gui/wxpython/psmap/*: F841, E266, E722 gui/wxpython/vdigit/*: F841, E722, F405, F403 gui/wxpython/vnet/*: F841 gui/wxpython/wxgui.py: F841 diff --git a/gui/wxpython/psmap/dialogs.py b/gui/wxpython/psmap/dialogs.py index 66ac66d9348..0bbe779818a 100644 --- a/gui/wxpython/psmap/dialogs.py +++ b/gui/wxpython/psmap/dialogs.py @@ -42,9 +42,8 @@ import wx import wx.lib.agw.floatspin as fs -from wx.lib.mixins.listctrl import ListCtrlAutoWidthMixin - from core import globalvar +from wx.lib.mixins.listctrl import ListCtrlAutoWidthMixin if globalvar.wxPythonPhoenix: from wx import Validator @@ -52,24 +51,25 @@ from wx import PyValidator as Validator import grass.script as gs - -from core.utils import PilImageToWxImage +from core.gcmd import GError, GMessage, RunCommand +from core.utils import PilImageToWxImage, cmp from dbmgr.vinfo import VectorDBInfo -from gui_core.gselect import Select -from core.gcmd import RunCommand, GError, GMessage from gui_core.dialogs import SymbolDialog +from gui_core.gselect import Select from gui_core.wrap import ( BitmapButton, BitmapComboBox, BitmapFromImage, Button, CheckBox, + CheckListCtrlMixin, Choice, ClientDC, ColourPickerCtrl, Dialog, DirBrowseButton, EmptyBitmap, + EmptyImage, ExpandoTextCtrl, FileBrowseButton, FloatSpin, @@ -86,11 +86,44 @@ StaticText, TextCtrl, TextEntryDialog, - EmptyImage, - CheckListCtrlMixin, ) -from psmap.utils import * -from psmap.instructions import * + +# Explicit imports from psmap.instructions +from psmap.instructions import ( + Image, + Labels, + Line, + MapFrame, + Mapinfo, + NewId, + NorthArrow, + Point, + Raster, + RasterLegend, + Rectangle, + Scalebar, + Text, + Vector, + VectorLegend, + VProperties, +) + +# Explicit imports from psmap.utils +from psmap.utils import ( + AutoAdjust, + BBoxAfterRotation, + ComputeSetRegion, + PaperMapCoordinates, + PILImage, + Rect2D, + Rect2DPP, + SetResolution, + UnitConversion, + convertRGB, + getRasterType, + havePILImage, + projInfo, +) # grass.set_raise_on_error(True) diff --git a/gui/wxpython/psmap/frame.py b/gui/wxpython/psmap/frame.py index b3196d348fa..13fdc91a984 100644 --- a/gui/wxpython/psmap/frame.py +++ b/gui/wxpython/psmap/frame.py @@ -16,10 +16,9 @@ """ import os -import sys - import queue as Queue -from math import sin, cos, pi, sqrt +import sys +from math import cos, pi, sin, sqrt import wx @@ -29,25 +28,51 @@ import wx.lib.flatnotebook as FN import grass.script as gs - from core import globalvar -from gui_core.menu import Menu -from core.gconsole import CmdThread, EVT_CMD_DONE -from psmap.toolbars import PsMapToolbar -from core.gcmd import RunCommand, GError, GMessage +from core.gcmd import GError, GMessage, RunCommand +from core.gconsole import EVT_CMD_DONE, CmdThread from core.settings import UserSettings from core.utils import PilImageToWxImage -from gui_core.forms import GUI -from gui_core.widgets import GNotebook from gui_core.dialogs import HyperlinkDialog +from gui_core.forms import GUI from gui_core.ghelp import ShowAboutDialog -from gui_core.wrap import ClientDC, PseudoDC, Rect, StockCursor, EmptyBitmap -from psmap.menudata import PsMapMenuData +from gui_core.menu import Menu from gui_core.toolbars import ToolSwitcher - -from psmap.dialogs import * -from psmap.instructions import * -from psmap.utils import * +from gui_core.widgets import GNotebook +from gui_core.wrap import ClientDC, EmptyBitmap, PseudoDC, Rect, StockCursor + +from psmap.dialogs import ( + ImageDialog, + LabelsDialog, + LegendDialog, + MainVectorDialog, + MapDialog, + MapinfoDialog, + NorthArrowDialog, + PageSetupDialog, + PointDialog, + RasterDialog, + RectangleDialog, + ScalebarDialog, + TextDialog, +) +from psmap.instructions import InitMap, Instruction, NewId, SetResolution, PageSetup +from psmap.menudata import PsMapMenuData +from psmap.toolbars import PsMapToolbar +from psmap.utils import ( + AutoAdjust, + ComputeSetRegion, + GetMapBounds, + PaperMapCoordinates, + PILImage, + Rect2D, + Rect2DPP, + Rect2DPS, + UnitConversion, + convertRGB, + havePILImage, + projInfo, +) class PsMapFrame(wx.Frame): diff --git a/gui/wxpython/psmap/instructions.py b/gui/wxpython/psmap/instructions.py index 9db64679422..845e99b2e6a 100644 --- a/gui/wxpython/psmap/instructions.py +++ b/gui/wxpython/psmap/instructions.py @@ -35,17 +35,27 @@ import os import string from math import ceil -from time import strftime, localtime +from time import localtime, strftime -import wx import grass.script as gs -from grass.script.task import cmdlist_to_tuple - +import wx from core.gcmd import GError, GMessage, GWarning from core.utils import GetCmdString from dbmgr.vinfo import VectorDBInfo +from grass.script.task import cmdlist_to_tuple from gui_core.wrap import NewId as wxNewId -from psmap.utils import * + +from psmap.utils import ( # Add any additional required names from psmap.utils here + BBoxAfterRotation, + GetMapBounds, + PaperMapCoordinates, + Rect2D, + Rect2DPP, + SetResolution, + UnitConversion, + getRasterType, + projInfo, +) def NewId(): From 0adc4dc2ec42e07c82012fb948b0b9cac4b4afb3 Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Thu, 17 Oct 2024 14:29:38 -0400 Subject: [PATCH 405/471] wxGUI: Fixed unused variable F841 (#4542) --- .flake8 | 1 - gui/wxpython/wxgui.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.flake8 b/.flake8 index 5c23d51bd36..92611007661 100644 --- a/.flake8 +++ b/.flake8 @@ -31,7 +31,6 @@ per-file-ignores = gui/wxpython/psmap/*: F841, E266, E722 gui/wxpython/vdigit/*: F841, E722, F405, F403 gui/wxpython/vnet/*: F841 - gui/wxpython/wxgui.py: F841 gui/wxpython/animation/g.gui.animation.py: E501 gui/wxpython/tplot/frame.py: F841, E722 gui/wxpython/tplot/g.gui.tplot.py: E501 diff --git a/gui/wxpython/wxgui.py b/gui/wxpython/wxgui.py index 447821c6724..8fac39c4d5a 100644 --- a/gui/wxpython/wxgui.py +++ b/gui/wxpython/wxgui.py @@ -164,7 +164,7 @@ def main(argv=None): app = GMApp(workspaceFile) # suppress wxPython logs - q = wx.LogNull() + wx.LogNull() set_raise_on_error(True) # register GUI PID From e540efc76618837947fefca21d8b141913ff6376 Mon Sep 17 00:00:00 2001 From: Ivan Mincik Date: Thu, 17 Oct 2024 19:48:24 +0000 Subject: [PATCH 406/471] nix: Fix locales in development environment (#4540) This fixes locales in nix provided development environment by setting environment variable. Closes #4503 --- flake.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/flake.nix b/flake.nix index 51174e1da74..a105607417f 100644 --- a/flake.nix +++ b/flake.nix @@ -34,6 +34,8 @@ pytest ]; + LOCALE_ARCHIVE = "${pkgs.glibcLocales}/lib/locale/locale-archive"; + shellHook = '' function dev-help { echo -e "\nWelcome to a GRASS development environment !" From 9dbc8c96b05eaff6df9dc2ffd55c7e8f45fa25e9 Mon Sep 17 00:00:00 2001 From: Markus Neteler Date: Thu, 17 Oct 2024 23:33:50 +0200 Subject: [PATCH 407/471] docs: add intro text to imageryintro.html (#4536) This PR adds an overview text to imageryintro.html including a link to addons. (aside, addons URL fix in g.gui.gmodeler.html) Co-authored-by: Nicklas Larsson Co-authored-by: Vaclav Petras --- gui/wxpython/gmodeler/g.gui.gmodeler.html | 2 +- imagery/imageryintro.html | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/gui/wxpython/gmodeler/g.gui.gmodeler.html b/gui/wxpython/gmodeler/g.gui.gmodeler.html index 631c06de9d8..a840768abeb 100644 --- a/gui/wxpython/gmodeler/g.gui.gmodeler.html +++ b/gui/wxpython/gmodeler/g.gui.gmodeler.html @@ -466,7 +466,7 @@

    SEE ALSO

    See also selected user models available from -GRASS Addons repository. +GRASS Addons repository.

    See also diff --git a/imagery/imageryintro.html b/imagery/imageryintro.html index 4b80951af9e..9018b4871ae 100644 --- a/imagery/imageryintro.html +++ b/imagery/imageryintro.html @@ -2,6 +2,21 @@

    Image processing in general

    +GRASS GIS provides a powerful suite of tools for the processing and +analysis of geospatial raster data, including satellite imagery and +aerial photography. Its image processing capabilities encompass a broad +range of preprocessing operations, such as data import, georeferencing, +radiometric calibration, and atmospheric correction. It is particularly +suited for handling Earth observation data, enabling the analysis of +multispectral and temporal datasets. GRASS GIS supports advanced +functionalities such as image classification, sensor fusion, and point +cloud statistics. The calculation of vegetation indices further enables +analyses of environmental, agricultural, and land cover dynamics. An +extensive collection of +addons +further enhances its image processing capabilities, particularly in the +context of Earth observation and remote sensing applications. + Digital numbers and physical values (reflection/radiance-at-sensor):

    Satellite imagery is commonly stored in Digital Numbers (DN) for From 37d40827fce95fc0e1231616d621f6bb496172ba Mon Sep 17 00:00:00 2001 From: Markus Neteler Date: Thu, 17 Oct 2024 23:44:17 +0200 Subject: [PATCH 408/471] GUI: EPSG code source: replace epsg.io with spatialreference.org (#4535) As epsg.io is partially outdated (see [source](https://github.com/maptiler/epsg.io/issues/171) and related) and PROJ is our engine anyway, this PR switches the references in the GUI to https://spatialreference.org. * "See also" section: added URLs to CRS Explorer and EPSG Geodetic Parameter Dataset Co-authored-by: Vaclav Petras --- gui/wxpython/location_wizard/wizard.py | 11 +++++++---- lib/init/helptext.html | 6 ++++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/gui/wxpython/location_wizard/wizard.py b/gui/wxpython/location_wizard/wizard.py index d8b6b4180ba..1f8de66dda6 100644 --- a/gui/wxpython/location_wizard/wizard.py +++ b/gui/wxpython/location_wizard/wizard.py @@ -1604,9 +1604,12 @@ def __init__(self, wizard, parent): self, data=None, columns=[_("Code"), _("Description"), _("Parameters")] ) - # epsg.io hyperlink + # A hyperlink to a CRS database (PROJ related) self.tlink = HyperlinkCtrl( - self, id=wx.ID_ANY, label="epsg.io", url="https://epsg.io/" + self, + id=wx.ID_ANY, + label="spatialreference.org", + url="https://spatialreference.org/", ) self.tlink.SetNormalColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT)) self.tlink.SetVisitedColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT)) @@ -1688,14 +1691,14 @@ def EnableNext(self, enable=True): def OnTextChange(self, event): value = self.searchb.GetValue() if value == "": - self.tlink.SetURL("https://epsg.io/") + self.tlink.SetURL("https://spatialreference.org/") self.epsgcode = None self.epsgdesc = self.epsgparams = "" self.searchb.ChangeValue("") self.OnBrowseCodes(None) self.EnableNext(False) else: - self.tlink.SetURL(str("https://epsg.io/?q={0}".format(value))) + self.tlink.SetURL(f"https://spatialreference.org/ref/?&search={value}") data = self.epsglist.Search(index=[0, 1, 2], pattern=value, firstOnly=False) if data: index = 0 diff --git a/lib/init/helptext.html b/lib/init/helptext.html index d988b6b0376..4a14dd52df1 100644 --- a/lib/init/helptext.html +++ b/lib/init/helptext.html @@ -62,7 +62,7 @@

    GRASS started in the default project, now what?

    Creating a New project with the Project Wizard

    If you know the CRS of your data or study area, -you can fill EPSG code +you can fill EPSG code or description and Project Wizard finds appropriate CRS from a predefined list of projections. @@ -97,4 +97,6 @@

    See also

    - List of EPSG codes (Database of worldwide coordinate systems) + List of EPSG codes (Database of worldwide coordinate systems), + CRS Explorer - PROJ codes, and + EPSG Geodetic Parameter Dataset From 3c678251df9b4dbe30c6cef2f075eeea18efa1df Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 18 Oct 2024 01:47:57 +0000 Subject: [PATCH 409/471] CI(deps): Update ruff to v0.7.0 (#4544) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * CI(deps): Update ruff to v0.7.0 * style: Fix FURB156: Use of hardcoded string charset --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Edouard Choinière <27212526+echoix@users.noreply.github.com> --- .github/workflows/python-code-quality.yml | 2 +- .pre-commit-config.yaml | 2 +- gui/wxpython/core/utils.py | 3 ++- gui/wxpython/gui_core/goutput.py | 3 ++- python/grass/imaging/images2ims.py | 3 ++- scripts/d.rast.edit/d.rast.edit.py | 4 +++- 6 files changed, 11 insertions(+), 6 deletions(-) diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index 886cbe56b47..9f71c28e922 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -36,7 +36,7 @@ jobs: # renovate: datasource=pypi depName=bandit BANDIT_VERSION: "1.7.10" # renovate: datasource=pypi depName=ruff - RUFF_VERSION: "0.6.9" + RUFF_VERSION: "0.7.0" runs-on: ${{ matrix.os }} permissions: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2c136da40ff..4c82e138950 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -37,7 +37,7 @@ repos: ) - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.6.9 + rev: v0.7.0 hooks: # Run the linter. - id: ruff diff --git a/gui/wxpython/core/utils.py b/gui/wxpython/core/utils.py index 3c1b4a7dd14..2b1f3f4997a 100644 --- a/gui/wxpython/core/utils.py +++ b/gui/wxpython/core/utils.py @@ -20,6 +20,7 @@ import re import inspect import operator +from string import digits from grass.script import core as grass from grass.script import task as gtask @@ -936,7 +937,7 @@ def SetAddOnPath(addonPath=None, key="PATH"): def color_resolve(color): - if len(color) > 0 and color[0] in "0123456789": + if len(color) > 0 and color[0] in digits: rgb = tuple(map(int, color.split(":"))) label = color else: diff --git a/gui/wxpython/gui_core/goutput.py b/gui/wxpython/gui_core/goutput.py index 3c8927f2233..6676eb69935 100644 --- a/gui/wxpython/gui_core/goutput.py +++ b/gui/wxpython/gui_core/goutput.py @@ -20,6 +20,7 @@ """ import textwrap +from string import digits import wx from wx import stc @@ -624,7 +625,7 @@ def AddStyledMessage(self, message, style=None): self.linePos = self.GetCurrentPos() if c != " ": last_c = c - if last_c not in ("0123456789"): + if last_c not in (digits): self.AddTextWrapped("\n", wrap=None) self.linePos = -1 else: diff --git a/python/grass/imaging/images2ims.py b/python/grass/imaging/images2ims.py index a5cee9b49de..d86a2247952 100644 --- a/python/grass/imaging/images2ims.py +++ b/python/grass/imaging/images2ims.py @@ -32,6 +32,7 @@ import os from operator import itemgetter +from string import digits try: import numpy as np @@ -117,7 +118,7 @@ def _getSequenceNumber(filename, part1, part2): # Get all numeric chars seq2 = "" for c in seq: - if c in "0123456789": + if c in digits: seq2 += c else: break diff --git a/scripts/d.rast.edit/d.rast.edit.py b/scripts/d.rast.edit/d.rast.edit.py index 6519d10c7a1..ea6cc1e46bf 100755 --- a/scripts/d.rast.edit/d.rast.edit.py +++ b/scripts/d.rast.edit/d.rast.edit.py @@ -77,6 +77,8 @@ import sys import math import atexit +from string import digits + import grass.script as gs from grass.script.setup import set_gui_path @@ -448,7 +450,7 @@ def OnClose(self, ev): def OnReturn(self, ev): self.app.brush = self.newval.GetValue() - if self.app.brush != "*" and self.app.brush.strip("0123456789") != "": + if self.app.brush != "*" and self.app.brush.strip(digits) != "": self.app.brush = "*" self.brush_update() From d788eee193b3e3a4d10194775cec9d4429802403 Mon Sep 17 00:00:00 2001 From: Mohan Yelugoti Date: Fri, 18 Oct 2024 00:31:28 -0400 Subject: [PATCH 410/471] i.atcorr: fix out of bound access in trunca, os and iso (#4493) * i.atcorr: fix out of bound access in trunca, os and iso In trunca(), under 2 if conditionals (if rumu[1] > 0.8 and rmu[1] > 0.94), k and kk values are set to -1 respectively. When these values are used to access an array, it leads to out of bound access, which is undefined behavior in c++. Similarly in os() and iso(), if `taup < h[i]` for all values, iplane would still be -1 and we would be accessing an array with this as an index, which is undefined behavior. In both cases, to avoid that, set that index to zero. This was found using cppcheck tool. Signed-off-by: Mohan Yelugoti * Update imagery/i.atcorr/computations.cpp When plane layer couldn't be determined, throw out an error and exit. Co-authored-by: Markus Metz <33666869+metzm@users.noreply.github.com> * Update imagery/i.atcorr/computations.cpp When plane layer couldn't be determined, throw out an error and exit. Co-authored-by: Markus Metz <33666869+metzm@users.noreply.github.com> * Fix clang-format issues Signed-off-by: Mohan Yelugoti --------- Signed-off-by: Mohan Yelugoti Co-authored-by: Markus Metz <33666869+metzm@users.noreply.github.com> --- imagery/i.atcorr/computations.cpp | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/imagery/i.atcorr/computations.cpp b/imagery/i.atcorr/computations.cpp index 53f8664ee06..6e5a38c5b07 100644 --- a/imagery/i.atcorr/computations.cpp +++ b/imagery/i.atcorr/computations.cpp @@ -102,14 +102,14 @@ double trunca() for (i = 0; i < 83; i++) { if (rmu[i] > 0.8) break; - k = i - 1; + k = i; } int kk = 0; for (i = 0; i < 83; i++) { if (rmu[i] > 0.94) break; - kk = i - 1; + kk = i; } double aa = @@ -118,7 +118,7 @@ double trunca() double x1 = (double)(log10(sixs_trunc.pha[kk])); double x2 = (double)acos(rmu[kk]); - for (i = kk + 1; i < 83; i++) { + for (i = kk; i < 83; i++) { double a; if (fabs(rmu[i] - 1) <= 1e-08) a = x1 - aa * x2; @@ -445,9 +445,14 @@ void os(const double tamoy, const double trmoy, const double pizmoy, /* compute position of the plane layer */ double taup = tap + trp; iplane = -1; - for (int i = 0; i <= ntp; i++) + for (int i = 0; i <= ntp; i++) { if (taup >= h[i]) iplane = i; + } + if (iplane == -1) { + G_fatal_error( + _("Position of the plane layer could not be determined")); + } /* update the layer from the end to the position to update if necessary */ @@ -1006,9 +1011,14 @@ void iso(const double tamoy, const double trmoy, const double pizmoy, /* compute position of the plane layer */ double taup = tap + trp; iplane = -1; - for (int i = 0; i <= ntp; i++) + for (int i = 0; i <= ntp; i++) { if (taup >= h[i]) iplane = i; + } + if (iplane == -1) { + G_fatal_error( + _("Position of the plane layer could not be determined")); + } /* update the layer from the end to the position to update if necessary */ From 0cb20eb612b933dc914461f6c541510213bc71e0 Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Fri, 18 Oct 2024 09:18:53 -0400 Subject: [PATCH 411/471] wxGUI: Fixed F841 in vnet/ (#4543) --- .flake8 | 1 - gui/wxpython/vnet/dialogs.py | 2 -- gui/wxpython/vnet/toolbars.py | 1 - gui/wxpython/vnet/vnet_core.py | 18 ------------------ gui/wxpython/vnet/vnet_data.py | 2 +- gui/wxpython/vnet/vnet_utils.py | 1 - gui/wxpython/vnet/widgets.py | 2 -- 7 files changed, 1 insertion(+), 26 deletions(-) diff --git a/.flake8 b/.flake8 index 92611007661..306a11fe02b 100644 --- a/.flake8 +++ b/.flake8 @@ -30,7 +30,6 @@ per-file-ignores = gui/wxpython/photo2image/g.gui.photo2image.py: E501, F841 gui/wxpython/psmap/*: F841, E266, E722 gui/wxpython/vdigit/*: F841, E722, F405, F403 - gui/wxpython/vnet/*: F841 gui/wxpython/animation/g.gui.animation.py: E501 gui/wxpython/tplot/frame.py: F841, E722 gui/wxpython/tplot/g.gui.tplot.py: E501 diff --git a/gui/wxpython/vnet/dialogs.py b/gui/wxpython/vnet/dialogs.py index 39cf2e4ae84..8d24b08df91 100644 --- a/gui/wxpython/vnet/dialogs.py +++ b/gui/wxpython/vnet/dialogs.py @@ -1245,8 +1245,6 @@ def __init__( wx.Dialog.__init__(self, parent, id, title, pos, size, style) self.vnet_mgr = vnet_mgr - - maxValue = 1e8 self.parent = parent self.settings = {} diff --git a/gui/wxpython/vnet/toolbars.py b/gui/wxpython/vnet/toolbars.py index 5f80e50cd9d..d31313091fe 100644 --- a/gui/wxpython/vnet/toolbars.py +++ b/gui/wxpython/vnet/toolbars.py @@ -235,6 +235,5 @@ def __init__(self, parent, vnet_mgr): self.Realize() def _toolbarData(self): - icons = {} return self._getToolbarData(()) diff --git a/gui/wxpython/vnet/vnet_core.py b/gui/wxpython/vnet/vnet_core.py index 8dda73b6770..2e98aa2ee8f 100644 --- a/gui/wxpython/vnet/vnet_core.py +++ b/gui/wxpython/vnet/vnet_core.py @@ -459,11 +459,6 @@ def _vnetPathRunAn(self, analysis, output, params, flags, catPts): return False mapName, mapSet = ParseMapStr(self.tmpTurnAn.GetVectMapName()) - cmdCopy = [ - "g.copy", - "vector=%s,%s" % (params["input"], mapName), - "--overwrite", - ] cmdParams.append("input=" + self.tmpTurnAn.GetVectMapName()) ret, msg, err = RunCommand( @@ -550,11 +545,6 @@ def _runTurnsAn(self, analysis, output, params, flags, catPts): # create and run commands which goes to analysis thread mapName, mapSet = ParseMapStr(self.tmpTurnAn.GetVectMapName()) - cmdCopy = [ - "g.copy", - "vector=%s,%s" % (params["input"], mapName), - "--overwrite", - ] cmdParams.append("input=" + self.tmpTurnAn.GetVectMapName()) ret, msg, err = RunCommand( @@ -580,14 +570,6 @@ def _updateTtbByGlobalCosts(self, vectMapName, tlayer): # TODO get layer number do not use it directly intervals = self.turnsData["global"].GetData() - cmdUpdGlob = [ - "v.db.update", - "map=", - self.inputData["input"].GetValue(), - "layer=%d" % tlayer, - "column=cost", - ] - dbInfo = VectorDBInfo(vectMapName) table = dbInfo.GetTable(tlayer) driver, database = dbInfo.GetDbSettings(tlayer) diff --git a/gui/wxpython/vnet/vnet_data.py b/gui/wxpython/vnet/vnet_data.py index 3adc5fa64e9..f2f17b0bfb5 100644 --- a/gui/wxpython/vnet/vnet_data.py +++ b/gui/wxpython/vnet/vnet_data.py @@ -1377,7 +1377,7 @@ def SetValue(self, value, line, col): def SetUTurns(self, value): """Checked if checeBox is checed""" - useUTurns = value + self.useUTurns = value def AppendRow(self, values): self.turn_data.append(values) diff --git a/gui/wxpython/vnet/vnet_utils.py b/gui/wxpython/vnet/vnet_utils.py index f0a354691bc..e3cc11aee17 100644 --- a/gui/wxpython/vnet/vnet_utils.py +++ b/gui/wxpython/vnet/vnet_utils.py @@ -133,7 +133,6 @@ def GetNearestNodeCat(e, n, layer, tresh, vectMap): vectlib.Vect_select_lines_by_box(openedMap, byref(box), vectlib.GV_POINT, List) found = 0 - dcost = 0 Cats = POINTER(vectlib.line_cats) Cats = vectlib.Vect_new_cats_struct() diff --git a/gui/wxpython/vnet/widgets.py b/gui/wxpython/vnet/widgets.py index ed1f2aa514c..71fcd0e0739 100644 --- a/gui/wxpython/vnet/widgets.py +++ b/gui/wxpython/vnet/widgets.py @@ -217,7 +217,6 @@ def GetCellValue(self, key, colName): if colNum < 0: return None - iColEd = self.dataTypes["colEditable"] if self.selIdxs[key][colNum] != -1: return self.selIdxs[key][colNum] @@ -230,7 +229,6 @@ def GetCellSelIdx(self, key, colName): :return: -1 if column does not has values to choose """ colNum = self._getColumnNum(colName) - iColEd = self.dataTypes["colEditable"] return self.selIdxs[key][colNum] def EditCellIndex(self, index, colName, cellData): From 6fd21a886dad53a02cbfb73fafe70eb9914b65be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Fri, 18 Oct 2024 09:39:56 -0400 Subject: [PATCH 412/471] tests: Call t.remove and g.remove once with multiple inputs as list (#4523) * t.rast.accumulate: Call t.remove with multiple inputs in test_accumulation * t.rast.accdetect: Call t.remove with multiple inputs * t.rast.univar: Call t.remove once with multiple inputs * tests: Call g.remove once with multiple names to remove One call per type, when all other arguments are the same * benchmark: Call g.remove once with multiple names to remove One call per type, when all other arguments are the same * utils/thumbnails: Call g.remove once with multiple names to remove * Use tuple for supplying multiple input maps --- imagery/i.gensig/testsuite/test_i_gensig.py | 10 +++-- imagery/i.maxlik/testsuite/test_i_maxlik.py | 11 +++-- imagery/i.vi/testsuite/test_vi.py | 14 +++---- lib/imagery/testsuite/test_imagery_sigfile.py | 9 ++-- .../testsuite/test_imagery_sigsetfile.py | 9 ++-- raster/r.contour/testsuite/test_r_contour.py | 7 ++-- .../testsuite/test_r_in_pdal_binning.py | 8 +++- .../testsuite/test_r_in_pdal_selection.py | 8 +++- raster/r.kappa/testsuite/test_r_kappa.py | 15 ++++--- .../benchmark/benchmark_r_mfilter_nprocs.py | 3 +- .../benchmark/benchmark_r_neighbors_nprocs.py | 3 +- raster/r.random/testsuite/test_r_random.py | 28 ++++++------- .../benchmark/benchmark_r_resamp_filter.py | 3 +- .../benchmark/benchmark_r_resamp_interp.py | 3 +- .../r.series/benchmark/benchmark_r_series.py | 3 +- .../benchmark/benchmark_r_slope_aspect.py | 12 +++--- .../benchmark_r_slope_aspect_memory.py | 11 +++-- raster/r.tile/testsuite/test_r_tile.py | 42 +++++-------------- .../r.univar/benchmark/benchmark_r_univar.py | 3 +- raster/r.univar/testsuite/test_r_univar.py | 11 ++--- raster3d/r3.null/testsuite/test.r3.null.sh | 9 +--- .../testsuite/test_rmapcalcsimple.py | 5 ++- .../testsuite/test_r_reclass_area.py | 8 +++- .../testsuite/test_v_rast_stats.py | 11 ++--- .../t.rast.accdetect/testsuite/test_simple.py | 3 +- .../testsuite/test_accumulation.py | 4 +- .../t.rast.series/testsuite/test_series.py | 24 +++++++---- .../testsuite/test_t_rast_univar.py | 3 +- .../t.topology/test.t.topology.reltime.sh | 3 +- .../t.vect.extract/test.t.vect.extract.sh | 3 +- utils/thumbnails.py | 9 ++-- vector/v.random/testsuite/test_v_random.py | 5 ++- .../benchmark/benchmark_v_surf_rst.py | 3 +- .../benchmark/benchmark_v_surf_rst_cv.py | 3 +- 34 files changed, 151 insertions(+), 155 deletions(-) diff --git a/imagery/i.gensig/testsuite/test_i_gensig.py b/imagery/i.gensig/testsuite/test_i_gensig.py index d75e3da2af4..836550fd1e6 100644 --- a/imagery/i.gensig/testsuite/test_i_gensig.py +++ b/imagery/i.gensig/testsuite/test_i_gensig.py @@ -92,9 +92,13 @@ def tearDownClass(cls): """Remove the temporary region and generated data""" cls.del_temp_region() shutil.rmtree(cls.sig_dir1, ignore_errors=True) - cls.runModule("g.remove", flags="f", type="raster", name=cls.b1, quiet=True) - cls.runModule("g.remove", flags="f", type="raster", name=cls.b2, quiet=True) - cls.runModule("g.remove", flags="f", type="raster", name=cls.train, quiet=True) + cls.runModule( + "g.remove", + flags="f", + type="raster", + name=(cls.b1, cls.b2, cls.train), + quiet=True, + ) def test_creation(self): """Test creating a signature""" diff --git a/imagery/i.maxlik/testsuite/test_i_maxlik.py b/imagery/i.maxlik/testsuite/test_i_maxlik.py index e7fbf6c1a79..2245d0d6d58 100644 --- a/imagery/i.maxlik/testsuite/test_i_maxlik.py +++ b/imagery/i.maxlik/testsuite/test_i_maxlik.py @@ -163,13 +163,12 @@ def tearDownClass(cls): cls.del_temp_region() shutil.rmtree(cls.sig_dir1, ignore_errors=True) shutil.rmtree(cls.sig_dir2, ignore_errors=True) - cls.runModule("g.remove", flags="f", type="raster", name=cls.b1, quiet=True) - cls.runModule("g.remove", flags="f", type="raster", name=cls.b2, quiet=True) cls.runModule( - "g.remove", flags="f", type="raster", name=cls.v1_class, quiet=True - ) - cls.runModule( - "g.remove", flags="f", type="raster", name=cls.v2_class, quiet=True + "g.remove", + flags="f", + type="raster", + name=(cls.b1, cls.b2, cls.v1_class, cls.v2_class), + quiet=True, ) cls.runModule("g.remove", flags="f", type="group", name=cls.group, quiet=True) diff --git a/imagery/i.vi/testsuite/test_vi.py b/imagery/i.vi/testsuite/test_vi.py index 0c383417257..b2160f924ed 100644 --- a/imagery/i.vi/testsuite/test_vi.py +++ b/imagery/i.vi/testsuite/test_vi.py @@ -26,14 +26,12 @@ def setUpClass(cls): @classmethod def tearDownClass(cls): - cls.runModule("g.remove", flags="f", type="raster", name="ipvi") - cls.runModule("g.remove", flags="f", type="raster", name="ndwi") - cls.runModule("g.remove", flags="f", type="raster", name="dvi") - cls.runModule("g.remove", flags="f", type="raster", name="sr") - cls.runModule("g.remove", flags="f", type="raster", name="evi") - cls.runModule("g.remove", flags="f", type="raster", name="evi2") - cls.runModule("g.remove", flags="f", type="raster", name="gari") - cls.runModule("g.remove", flags="f", type="raster", name="gemi") + cls.runModule( + "g.remove", + flags="f", + type="raster", + name="ipvi,ndwi,dvi,sr,evi,evi2,gari,gemi", + ) cls.del_temp_region() def test_vinameipvi(self): diff --git a/lib/imagery/testsuite/test_imagery_sigfile.py b/lib/imagery/testsuite/test_imagery_sigfile.py index 8d0e288561d..731ef047063 100644 --- a/lib/imagery/testsuite/test_imagery_sigfile.py +++ b/lib/imagery/testsuite/test_imagery_sigfile.py @@ -349,9 +349,12 @@ def setUpClass(cls): @classmethod def tearDownClass(cls): cls.del_temp_region() - cls.runModule("g.remove", flags="f", type="raster", name=cls.map1) - cls.runModule("g.remove", flags="f", type="raster", name=cls.map2) - cls.runModule("g.remove", flags="f", type="raster", name=cls.map3) + cls.runModule( + "g.remove", + flags="f", + type="raster", + name=(cls.map1, cls.map2, cls.map3), + ) def test_symmetric_complete_difference(self): # Prepare imagery group reference struct diff --git a/lib/imagery/testsuite/test_imagery_sigsetfile.py b/lib/imagery/testsuite/test_imagery_sigsetfile.py index d526e920885..80be7c0d3a5 100644 --- a/lib/imagery/testsuite/test_imagery_sigsetfile.py +++ b/lib/imagery/testsuite/test_imagery_sigsetfile.py @@ -242,9 +242,12 @@ def setUpClass(cls): @classmethod def tearDownClass(cls): cls.del_temp_region() - cls.runModule("g.remove", flags="f", type="raster", name=cls.map1) - cls.runModule("g.remove", flags="f", type="raster", name=cls.map2) - cls.runModule("g.remove", flags="f", type="raster", name=cls.map3) + cls.runModule( + "g.remove", + flags="f", + type="raster", + name=(cls.map1, cls.map2, cls.map3), + ) def test_symmetric_complete_difference(self): # Prepare imagery group reference struct diff --git a/raster/r.contour/testsuite/test_r_contour.py b/raster/r.contour/testsuite/test_r_contour.py index c9ee4c9876a..49617ef043d 100644 --- a/raster/r.contour/testsuite/test_r_contour.py +++ b/raster/r.contour/testsuite/test_r_contour.py @@ -32,10 +32,11 @@ def setUpClass(cls): def tearDownClass(cls): cls.del_temp_region() - cls.runModule("g.remove", type="vector", flags="f", name=cls.output) - cls.runModule("g.remove", type="vector", flags="f", name=cls.output + "_cut") cls.runModule( - "g.remove", type="vector", flags="f", name=cls.output + "_cut_flag_t" + "g.remove", + type="vector", + flags="f", + name=(cls.output, cls.output + "_cut", cls.output + "_cut_flag_t"), ) if os.path.isfile("testReport"): diff --git a/raster/r.in.pdal/testsuite/test_r_in_pdal_binning.py b/raster/r.in.pdal/testsuite/test_r_in_pdal_binning.py index a8a793f5bc7..c256224590d 100644 --- a/raster/r.in.pdal/testsuite/test_r_in_pdal_binning.py +++ b/raster/r.in.pdal/testsuite/test_r_in_pdal_binning.py @@ -68,8 +68,12 @@ def tearDown(self): This is executed after each test run. """ - self.runModule("g.remove", flags="f", type="raster", name=self.bin_raster) - self.runModule("g.remove", flags="f", type="raster", name=self.ref_raster) + self.runModule( + "g.remove", + flags="f", + type="raster", + name=(self.bin_raster, self.ref_raster), + ) @unittest.skipIf(shutil.which("r.in.pdal") is None, "Cannot find r.in.pdal") def test_method_n(self): diff --git a/raster/r.in.pdal/testsuite/test_r_in_pdal_selection.py b/raster/r.in.pdal/testsuite/test_r_in_pdal_selection.py index b7d5acd6b28..4796702d2f6 100644 --- a/raster/r.in.pdal/testsuite/test_r_in_pdal_selection.py +++ b/raster/r.in.pdal/testsuite/test_r_in_pdal_selection.py @@ -67,8 +67,12 @@ def tearDown(self): This is executed after each test run. """ - self.runModule("g.remove", flags="f", type="raster", name=self.imp_raster) - self.runModule("g.remove", flags="f", type="raster", name=self.ref_raster) + self.runModule( + "g.remove", + flags="f", + type="raster", + name=(self.imp_raster, self.ref_raster), + ) try: self.runModule("g.remove", flags="f", type="raster", name=self.base_raster) except AttributeError: diff --git a/raster/r.kappa/testsuite/test_r_kappa.py b/raster/r.kappa/testsuite/test_r_kappa.py index 620a4f4979b..ff11f477925 100644 --- a/raster/r.kappa/testsuite/test_r_kappa.py +++ b/raster/r.kappa/testsuite/test_r_kappa.py @@ -51,8 +51,9 @@ def setUpClass(cls): def tearDownClass(cls): """Remove temporary data""" cls.del_temp_region() - cls.runModule("g.remove", flags="f", type="raster", name=cls.ref_1) - cls.runModule("g.remove", flags="f", type="raster", name=cls.class_1) + cls.runModule( + "g.remove", flags="f", type="raster", name=(cls.ref_1, cls.class_1) + ) def test_m(self): """Test printing matrix only @@ -119,8 +120,9 @@ def setUpClass(cls): def tearDownClass(cls): """Remove temporary data""" cls.del_temp_region() - cls.runModule("g.remove", flags="f", type="raster", name=cls.ref_1) - cls.runModule("g.remove", flags="f", type="raster", name=cls.class_1) + cls.runModule( + "g.remove", flags="f", type="raster", name=(cls.ref_1, cls.class_1) + ) def match(self, pat, ref): if pat == "NA" or ref == "NA": @@ -233,8 +235,9 @@ def setUpClass(cls): def tearDownClass(cls): """Remove temporary data""" cls.del_temp_region() - cls.runModule("g.remove", flags="f", type="raster", name=cls.ref_1) - cls.runModule("g.remove", flags="f", type="raster", name=cls.class_1) + cls.runModule( + "g.remove", flags="f", type="raster", name=(cls.ref_1, cls.class_1) + ) def match(self, pat, ref): if pat == "NA" or ref == "NA": diff --git a/raster/r.mfilter/benchmark/benchmark_r_mfilter_nprocs.py b/raster/r.mfilter/benchmark/benchmark_r_mfilter_nprocs.py index 2dc85b411d8..9a0591f7cfc 100644 --- a/raster/r.mfilter/benchmark/benchmark_r_mfilter_nprocs.py +++ b/raster/r.mfilter/benchmark/benchmark_r_mfilter_nprocs.py @@ -55,8 +55,7 @@ def benchmark(size, label, results): overwrite=True, ) results.append(bm.benchmark_nprocs(module, label=label, max_nprocs=16, repeat=3)) - Module("g.remove", quiet=True, flags="f", type="raster", name=reference) - Module("g.remove", quiet=True, flags="f", type="raster", name=output) + Module("g.remove", quiet=True, flags="f", type="raster", name=(reference, output)) def generate_map(rows, cols, fname): diff --git a/raster/r.neighbors/benchmark/benchmark_r_neighbors_nprocs.py b/raster/r.neighbors/benchmark/benchmark_r_neighbors_nprocs.py index d355792a734..e7b3196c246 100644 --- a/raster/r.neighbors/benchmark/benchmark_r_neighbors_nprocs.py +++ b/raster/r.neighbors/benchmark/benchmark_r_neighbors_nprocs.py @@ -38,8 +38,7 @@ def benchmark(size, label, results): overwrite=True, ) results.append(bm.benchmark_nprocs(module, label=label, max_nprocs=16, repeat=3)) - Module("g.remove", quiet=True, flags="f", type="raster", name=reference) - Module("g.remove", quiet=True, flags="f", type="raster", name=output) + Module("g.remove", quiet=True, flags="f", type="raster", name=(reference, output)) def generate_map(rows, cols, fname): diff --git a/raster/r.random/testsuite/test_r_random.py b/raster/r.random/testsuite/test_r_random.py index ce12c719c8e..16aae357ebf 100644 --- a/raster/r.random/testsuite/test_r_random.py +++ b/raster/r.random/testsuite/test_r_random.py @@ -28,30 +28,30 @@ def setUpClass(cls): def tearDownClass(cls): cls.del_temp_region() - cls.runModule("g.remove", type="raster", flags="f", name=cls.raster) - cls.runModule("g.remove", type="raster", flags="f", name=cls.raster + "_null") - cls.runModule( - "g.remove", type="raster", flags="f", name=cls.raster + "_without_topology" - ) - cls.runModule("g.remove", type="raster", flags="f", name=cls.raster + "_3D") cls.runModule( "g.remove", type="raster", flags="f", - name=cls.raster + "_cover_landcover_1m", + name=( + cls.raster, + cls.raster + "_null", + cls.raster + "_without_topology", + cls.raster + "_3D", + cls.raster + "_cover_landcover_1m", + ), ) - cls.runModule("g.remove", type="vector", flags="f", name=cls.vector) - cls.runModule("g.remove", type="vector", flags="f", name=cls.vector + "_null") - cls.runModule( - "g.remove", type="vector", flags="f", name=cls.vector + "_without_topology" - ) - cls.runModule("g.remove", type="vector", flags="f", name=cls.vector + "_3D") cls.runModule( "g.remove", type="vector", flags="f", - name=cls.vector + "_cover_landcover_1m", + name=( + cls.vector, + cls.vector + "_null", + cls.vector + "_without_topology", + cls.vector + "_3D", + cls.vector + "_cover_landcover_1m", + ), ) def test_random_raster(self): diff --git a/raster/r.resamp.filter/benchmark/benchmark_r_resamp_filter.py b/raster/r.resamp.filter/benchmark/benchmark_r_resamp_filter.py index 8c7799e74fc..9ec46132c81 100644 --- a/raster/r.resamp.filter/benchmark/benchmark_r_resamp_filter.py +++ b/raster/r.resamp.filter/benchmark/benchmark_r_resamp_filter.py @@ -40,8 +40,7 @@ def benchmark(size, label, results): overwrite=True, ) results.append(bm.benchmark_nprocs(module, label=label, max_nprocs=8, repeat=3)) - Module("g.remove", quiet=True, flags="f", type="raster", name=reference) - Module("g.remove", quiet=True, flags="f", type="raster", name=output) + Module("g.remove", quiet=True, flags="f", type="raster", name=(reference, output)) def generate_map(rows, cols, fname): diff --git a/raster/r.resamp.interp/benchmark/benchmark_r_resamp_interp.py b/raster/r.resamp.interp/benchmark/benchmark_r_resamp_interp.py index 20196bc44bc..616dc045aa3 100644 --- a/raster/r.resamp.interp/benchmark/benchmark_r_resamp_interp.py +++ b/raster/r.resamp.interp/benchmark/benchmark_r_resamp_interp.py @@ -38,8 +38,7 @@ def benchmark(size, label, results): overwrite=True, ) results.append(bm.benchmark_nprocs(module, label=label, max_nprocs=16, repeat=3)) - Module("g.remove", quiet=True, flags="f", type="raster", name=reference) - Module("g.remove", quiet=True, flags="f", type="raster", name=output) + Module("g.remove", quiet=True, flags="f", type="raster", name=(reference, output)) def generate_map(rows, cols, fname): diff --git a/raster/r.series/benchmark/benchmark_r_series.py b/raster/r.series/benchmark/benchmark_r_series.py index ecf4432f2dd..72c9c3bd23c 100644 --- a/raster/r.series/benchmark/benchmark_r_series.py +++ b/raster/r.series/benchmark/benchmark_r_series.py @@ -37,8 +37,7 @@ def benchmark(size, label, results): overwrite=True, ) results.append(bm.benchmark_nprocs(module, label=label, max_nprocs=16, repeat=3)) - Module("g.remove", quiet=True, flags="f", type="raster", name=reference) - Module("g.remove", quiet=True, flags="f", type="raster", name=output) + Module("g.remove", quiet=True, flags="f", type="raster", name=(reference, output)) def generate_map(rows, cols, fname): diff --git a/raster/r.slope.aspect/benchmark/benchmark_r_slope_aspect.py b/raster/r.slope.aspect/benchmark/benchmark_r_slope_aspect.py index 220b813e323..522a938f26c 100644 --- a/raster/r.slope.aspect/benchmark/benchmark_r_slope_aspect.py +++ b/raster/r.slope.aspect/benchmark/benchmark_r_slope_aspect.py @@ -42,11 +42,13 @@ def benchmark(size, label, results): overwrite=True, ) results.append(bm.benchmark_nprocs(module, label=label, max_nprocs=16, repeat=3)) - Module("g.remove", quiet=True, flags="f", type="raster", name=reference) - Module("g.remove", quiet=True, flags="f", type="raster", name=slope) - Module("g.remove", quiet=True, flags="f", type="raster", name=aspect) - Module("g.remove", quiet=True, flags="f", type="raster", name=pcurv) - Module("g.remove", quiet=True, flags="f", type="raster", name=tcurv) + Module( + "g.remove", + quiet=True, + flags="f", + type="raster", + name=(reference, slope, aspect, pcurv, tcurv), + ) def generate_map(rows, cols, fname): diff --git a/raster/r.slope.aspect/benchmark/benchmark_r_slope_aspect_memory.py b/raster/r.slope.aspect/benchmark/benchmark_r_slope_aspect_memory.py index c8f5ac09f00..31824233bbc 100644 --- a/raster/r.slope.aspect/benchmark/benchmark_r_slope_aspect_memory.py +++ b/raster/r.slope.aspect/benchmark/benchmark_r_slope_aspect_memory.py @@ -46,10 +46,13 @@ def benchmark(memory, label, results, reference): ) results.append(bm.benchmark_nprocs(module, label=label, max_nprocs=20, repeat=10)) - Module("g.remove", quiet=True, flags="f", type="raster", name=slope) - Module("g.remove", quiet=True, flags="f", type="raster", name=aspect) - Module("g.remove", quiet=True, flags="f", type="raster", name=pcurv) - Module("g.remove", quiet=True, flags="f", type="raster", name=tcurv) + Module( + "g.remove", + quiet=True, + flags="f", + type="raster", + name=(slope, aspect, pcurv, tcurv), + ) def generate_map(rows, cols, fname): diff --git a/raster/r.tile/testsuite/test_r_tile.py b/raster/r.tile/testsuite/test_r_tile.py index f1dd3afcd3b..c2e62b60dd2 100644 --- a/raster/r.tile/testsuite/test_r_tile.py +++ b/raster/r.tile/testsuite/test_r_tile.py @@ -28,42 +28,20 @@ def setUpClass(cls): @classmethod def tearDownClass(cls): cls.del_temp_region() - cls.runModule( - "g.remove", type="raster", flags="f", name=cls.output_prefix + "-000-000" - ) - cls.runModule( - "g.remove", type="raster", flags="f", name=cls.output_prefix + "-000-001" - ) - cls.runModule( - "g.remove", type="raster", flags="f", name=cls.output_prefix + "-001-000" - ) - cls.runModule( - "g.remove", type="raster", flags="f", name=cls.output_prefix + "-001-001" - ) - - cls.runModule( - "g.remove", - type="raster", - flags="f", - name=cls.output_prefix + "overlap" + "-000-000", - ) - cls.runModule( - "g.remove", - type="raster", - flags="f", - name=cls.output_prefix + "overlap" + "-000-001", - ) - cls.runModule( - "g.remove", - type="raster", - flags="f", - name=cls.output_prefix + "overlap" + "-001-000", - ) cls.runModule( "g.remove", type="raster", flags="f", - name=cls.output_prefix + "overlap" + "-001-001", + name=( + cls.output_prefix + "-000-000", + cls.output_prefix + "-000-001", + cls.output_prefix + "-001-000", + cls.output_prefix + "-001-001", + cls.output_prefix + "overlap" + "-000-000", + cls.output_prefix + "overlap" + "-000-001", + cls.output_prefix + "overlap" + "-001-000", + cls.output_prefix + "overlap" + "-001-001", + ), ) def test_raster_tile(self): diff --git a/raster/r.univar/benchmark/benchmark_r_univar.py b/raster/r.univar/benchmark/benchmark_r_univar.py index 507e6b4b23e..58d607645c7 100644 --- a/raster/r.univar/benchmark/benchmark_r_univar.py +++ b/raster/r.univar/benchmark/benchmark_r_univar.py @@ -35,8 +35,7 @@ def benchmark(size, label, results): overwrite=True, ) results.append(bm.benchmark_nprocs(module, label=label, max_nprocs=16, repeat=3)) - Module("g.remove", quiet=True, flags="f", type="raster", name=reference) - Module("g.remove", quiet=True, flags="f", type="raster", name=output) + Module("g.remove", quiet=True, flags="f", type="raster", name=(reference, output)) def generate_map(rows, cols, fname): diff --git a/raster/r.univar/testsuite/test_r_univar.py b/raster/r.univar/testsuite/test_r_univar.py index de28f0753d5..c2ae6669df6 100644 --- a/raster/r.univar/testsuite/test_r_univar.py +++ b/raster/r.univar/testsuite/test_r_univar.py @@ -23,11 +23,12 @@ def tearDownClass(cls): cls.del_temp_region() def tearDown(self): - self.runModule("g.remove", flags="f", type="raster", name="map_a") - self.runModule("g.remove", flags="f", type="raster", name="map_b") - self.runModule("g.remove", flags="f", type="raster", name="map_negative") - self.runModule("g.remove", flags="f", type="raster", name="zone_map") - self.runModule("g.remove", flags="f", type="raster", name="zone_map_with_gap") + self.runModule( + "g.remove", + flags="f", + type="raster", + name="map_a,map_b,map_negative,zone_map,zone_map_with_gap", + ) def setUp(self): """Create input data""" diff --git a/raster3d/r3.null/testsuite/test.r3.null.sh b/raster3d/r3.null/testsuite/test.r3.null.sh index 1cf93110b24..d6a6ec35810 100755 --- a/raster3d/r3.null/testsuite/test.r3.null.sh +++ b/raster3d/r3.null/testsuite/test.r3.null.sh @@ -56,14 +56,7 @@ diff data/test_volume_double_null_1.ref test_volume_double_null_1.txt diff data/test_volume_double_null_2.ref test_volume_double_null_2.txt # Cleanup -g.remove -f type=raster_3d name=test_volume_float_1 -g.remove -f type=raster_3d name=test_volume_float_2 -g.remove -f type=raster_3d name=test_volume_float_null_1 -g.remove -f type=raster_3d name=test_volume_float_null_2 -g.remove -f type=raster_3d name=test_volume_double_1 -g.remove -f type=raster_3d name=test_volume_double_2 -g.remove -f type=raster_3d name=test_volume_double_null_1 -g.remove -f type=raster_3d name=test_volume_double_null_2 +g.remove -f type=raster_3d name=test_volume_float_1,test_volume_float_2,test_volume_float_null_1,test_volume_float_null_2,test_volume_double_1,test_volume_double_2,test_volume_double_null_1,test_volume_double_null_2 rm test_volume_float_1.txt rm test_volume_float_2.txt rm test_volume_float_null_1.txt diff --git a/scripts/r.mapcalc.simple/testsuite/test_rmapcalcsimple.py b/scripts/r.mapcalc.simple/testsuite/test_rmapcalcsimple.py index d400ccc7980..f133da22f7d 100644 --- a/scripts/r.mapcalc.simple/testsuite/test_rmapcalcsimple.py +++ b/scripts/r.mapcalc.simple/testsuite/test_rmapcalcsimple.py @@ -24,8 +24,9 @@ def setUpClass(cls): def tearDownClass(cls): map_output1 = "test1" map_output2 = "test2" - cls.runModule("g.remove", flags="f", type="raster", name=map_output1) - cls.runModule("g.remove", flags="f", type="raster", name=map_output2) + cls.runModule( + "g.remove", flags="f", type="raster", name=(map_output1, map_output2) + ) cls.del_temp_region() def test_rmapcalcsimple(self): diff --git a/scripts/r.reclass.area/testsuite/test_r_reclass_area.py b/scripts/r.reclass.area/testsuite/test_r_reclass_area.py index c8009ce5a1d..96c922bda05 100644 --- a/scripts/r.reclass.area/testsuite/test_r_reclass_area.py +++ b/scripts/r.reclass.area/testsuite/test_r_reclass_area.py @@ -26,8 +26,12 @@ def setUpClass(cls): @classmethod def tearDownClass(cls): cls.del_temp_region() - cls.runModule("g.remove", type="raster", flags="f", name=cls.output + "Greater") - cls.runModule("g.remove", type="raster", flags="f", name=cls.output + "Lesser") + cls.runModule( + "g.remove", + type="raster", + flags="f", + name=(cls.output + "Greater", cls.output + "Lesser"), + ) def test_reclassaeaGreater(self): """Testing r.reclass.area with greater""" diff --git a/scripts/v.rast.stats/testsuite/test_v_rast_stats.py b/scripts/v.rast.stats/testsuite/test_v_rast_stats.py index 56acbce5964..01ae6d4a7d2 100644 --- a/scripts/v.rast.stats/testsuite/test_v_rast_stats.py +++ b/scripts/v.rast.stats/testsuite/test_v_rast_stats.py @@ -24,11 +24,12 @@ def tearDownClass(cls): cls.del_temp_region() def tearDown(self): - self.runModule("g.remove", flags="f", type="raster", name="map_a") - self.runModule("g.remove", flags="f", type="raster", name="map_b") - self.runModule("g.remove", flags="f", type="raster", name="zone_map") - self.runModule("g.remove", flags="f", type="raster", name="row_map") - self.runModule("g.remove", flags="f", type="raster", name="test_line") + self.runModule( + "g.remove", + flags="f", + type="raster", + name="map_a,map_b,zone_map,row_map,test_line", + ) def setUp(self): """Create input data""" diff --git a/temporal/t.rast.accdetect/testsuite/test_simple.py b/temporal/t.rast.accdetect/testsuite/test_simple.py index 522c13f93e4..d67537487d5 100644 --- a/temporal/t.rast.accdetect/testsuite/test_simple.py +++ b/temporal/t.rast.accdetect/testsuite/test_simple.py @@ -62,8 +62,7 @@ def tearDownClass(cls): def tearDown(self): """Remove generated data""" - self.runModule("t.remove", flags="df", type="strds", inputs="B") - self.runModule("t.remove", flags="df", type="strds", inputs="C") + self.runModule("t.remove", flags="df", type="strds", inputs="B,C") def test_simple(self): self.assertModule( diff --git a/temporal/t.rast.accumulate/testsuite/test_accumulation.py b/temporal/t.rast.accumulate/testsuite/test_accumulation.py index f5bf6dcf5d7..566c1971f99 100644 --- a/temporal/t.rast.accumulate/testsuite/test_accumulation.py +++ b/temporal/t.rast.accumulate/testsuite/test_accumulation.py @@ -95,9 +95,7 @@ def setUpClass(cls): @classmethod def tearDownClass(cls): """Remove the temporary region""" - cls.runModule("t.remove", flags="df", type="strds", inputs="A") - cls.runModule("t.remove", flags="df", type="strds", inputs="Lower") - cls.runModule("t.remove", flags="df", type="strds", inputs="Upper") + cls.runModule("t.remove", flags="df", type="strds", inputs="A,Lower,Upper") cls.del_temp_region() def tearDown(self): diff --git a/temporal/t.rast.series/testsuite/test_series.py b/temporal/t.rast.series/testsuite/test_series.py index 99fe69d4352..e936e10e0e7 100644 --- a/temporal/t.rast.series/testsuite/test_series.py +++ b/temporal/t.rast.series/testsuite/test_series.py @@ -61,11 +61,15 @@ def tearDownClass(cls): type="raster", maps="series_average,series_maximum,series_minimum,series_minimum_2", ) - cls.runModule("g.remove", flags="f", type="raster", name="series_average") - cls.runModule("g.remove", flags="f", type="raster", name="series_maximum") - cls.runModule("g.remove", flags="f", type="raster", name="series_minimum") - cls.runModule("g.remove", flags="f", type="raster", name="series_minimum_2") - cls.runModule("g.remove", flags="f", type="raster", name="series_quantile") + cls.runModule( + "g.remove", + flags="f", + type="raster", + name=( + "series_average,series_maximum" + + ",series_minimum,series_minimum_2,series_quantile" + ), + ) def test_time_stamp(self): self.assertModule( @@ -206,10 +210,12 @@ def tearDownClass(cls): type="raster", maps="series_average,series_maximum,series_minimum,series_minimum_2", ) - cls.runModule("g.remove", flags="f", type="raster", name="series_average") - cls.runModule("g.remove", flags="f", type="raster", name="series_maximum") - cls.runModule("g.remove", flags="f", type="raster", name="series_minimum") - cls.runModule("g.remove", flags="f", type="raster", name="series_minimum_2") + cls.runModule( + "g.remove", + flags="f", + type="raster", + name="series_average,series_maximum,series_minimum,series_minimum_2", + ) def test_average(self): self.assertModule( diff --git a/temporal/t.rast.univar/testsuite/test_t_rast_univar.py b/temporal/t.rast.univar/testsuite/test_t_rast_univar.py index dca870d6a98..17831b9b5b9 100644 --- a/temporal/t.rast.univar/testsuite/test_t_rast_univar.py +++ b/temporal/t.rast.univar/testsuite/test_t_rast_univar.py @@ -147,8 +147,7 @@ def setUpClass(cls): @classmethod def tearDownClass(cls): """Remove the temporary region""" - cls.runModule("t.remove", flags="df", type="strds", inputs="A") - cls.runModule("t.remove", flags="df", type="strds", inputs="B") + cls.runModule("t.remove", flags="df", type="strds", inputs="A,B") cls.runModule("g.remove", flags="f", type="raster", name="zones") cls.del_temp_region() diff --git a/temporal/t.topology/test.t.topology.reltime.sh b/temporal/t.topology/test.t.topology.reltime.sh index 847289c4b2f..60e49355e9a 100755 --- a/temporal/t.topology/test.t.topology.reltime.sh +++ b/temporal/t.topology/test.t.topology.reltime.sh @@ -96,6 +96,5 @@ cat "${n5}" t.topology input=precip_rel_y t.topology -m input=precip_rel_y -t.remove type=strds input=precip_rel_d -t.remove type=strds input=precip_rel_y +t.remove type=strds input=precip_rel_d,precip_rel_y t.unregister type=raster file="${n1}" diff --git a/temporal/t.vect.extract/test.t.vect.extract.sh b/temporal/t.vect.extract/test.t.vect.extract.sh index b5cd6882e94..8b176dcc8ed 100755 --- a/temporal/t.vect.extract/test.t.vect.extract.sh +++ b/temporal/t.vect.extract/test.t.vect.extract.sh @@ -44,5 +44,4 @@ t.vect.list input=soil_abs3 columns=name,start_time,end_time,primitives # @postprocess t.unregister type=vector maps=soil_1,soil_2,soil_3,soil_4,soil_5,soil_6,soil_7,soil_8 t.remove type=stvds input=soil_abs1,soil_abs2,soil_abs3 -g.remove -f type=vector name=soil_1,soil_2,soil_3,soil_4,soil_5,soil_6,soil_7,soil_8 -g.remove -f type=vector name=new_vect_1,new_vect_2,new_vect_3,new_vect_4,new_vect_5,new_vect_6 +g.remove -f type=vector name=soil_1,soil_2,soil_3,soil_4,soil_5,soil_6,soil_7,soil_8,new_vect_1,new_vect_2,new_vect_3,new_vect_4,new_vect_5,new_vect_6 diff --git a/utils/thumbnails.py b/utils/thumbnails.py index ee9bd5e17ab..181a16aa666 100755 --- a/utils/thumbnails.py +++ b/utils/thumbnails.py @@ -23,13 +23,14 @@ def cleanup(): + names = [] if tmp_grad_rel: - gs.run_command( - "g.remove", flags="f", type="raster", name=tmp_grad_rel, quiet=True - ) + names.append(tmp_grad_rel) if tmp_grad_abs: + names.append(tmp_grad_abs) + if len(names) > 0: gs.run_command( - "g.remove", flags="f", type="raster", name=tmp_grad_abs, quiet=True + "g.remove", flags="f", type="raster", name=",".join(names), quiet=True ) diff --git a/vector/v.random/testsuite/test_v_random.py b/vector/v.random/testsuite/test_v_random.py index e5cc95a93db..d11faf458f9 100644 --- a/vector/v.random/testsuite/test_v_random.py +++ b/vector/v.random/testsuite/test_v_random.py @@ -31,8 +31,9 @@ def tearDownClass(cls): cls.del_temp_region() def tearDown(cls): - cls.runModule("g.remove", type="vector", flags="f", name=cls.output) - cls.runModule("g.remove", type="vector", flags="f", name=cls.output2) + cls.runModule( + "g.remove", type="vector", flags="f", name=(cls.output, cls.output2) + ) def test_num_points(self): """Checking if number of points equals 100""" diff --git a/vector/v.surf.rst/benchmark/benchmark_v_surf_rst.py b/vector/v.surf.rst/benchmark/benchmark_v_surf_rst.py index 91fd784e0b3..ac69b441010 100644 --- a/vector/v.surf.rst/benchmark/benchmark_v_surf_rst.py +++ b/vector/v.surf.rst/benchmark/benchmark_v_surf_rst.py @@ -36,8 +36,7 @@ def benchmark(size, label, results): ) results.append(bm.benchmark_nprocs(module, label=label, max_nprocs=8, repeat=3)) - Module("g.remove", quiet=True, flags="f", type="raster", name=reference) - Module("g.remove", quiet=True, flags="f", type="raster", name=output) + Module("g.remove", quiet=True, flags="f", type="raster", name=(reference, output)) def generate_map(npoints, rows, cols, fname): diff --git a/vector/v.surf.rst/benchmark/benchmark_v_surf_rst_cv.py b/vector/v.surf.rst/benchmark/benchmark_v_surf_rst_cv.py index 21a3c7459eb..f140ee67b8a 100644 --- a/vector/v.surf.rst/benchmark/benchmark_v_surf_rst_cv.py +++ b/vector/v.surf.rst/benchmark/benchmark_v_surf_rst_cv.py @@ -37,8 +37,7 @@ def benchmark(size, label, results): ) results.append(bm.benchmark_nprocs(module, label=label, max_nprocs=8, repeat=3)) - Module("g.remove", quiet=True, flags="f", type="raster", name=reference) - Module("g.remove", quiet=True, flags="f", type="raster", name=output) + Module("g.remove", quiet=True, flags="f", type="raster", name=(reference, output)) def generate_map(npoints, rows, cols, fname): From 62b001b1318fb331334d6a9bab3ec0f6fba60360 Mon Sep 17 00:00:00 2001 From: ww2406 <57472892+ww2406@users.noreply.github.com> Date: Fri, 18 Oct 2024 07:41:20 -0700 Subject: [PATCH 413/471] gui: Add fix for wxGUI Data import Python error (#4504) This fixes #648 by adding a command tracker queue. When the queue is not empty, closing the dialog is prevented to ensure the completion callback can still access UI elements. --- gui/wxpython/modules/import_export.py | 47 +++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/gui/wxpython/modules/import_export.py b/gui/wxpython/modules/import_export.py index a0bd17e289c..c3106d0467b 100644 --- a/gui/wxpython/modules/import_export.py +++ b/gui/wxpython/modules/import_export.py @@ -18,9 +18,11 @@ @author Martin Landa @author Anna Kratochvilova (GroupDialog, SymbolDialog) +@author William Welch (commands running queue) """ import os +from collections import deque from pathlib import Path @@ -60,6 +62,8 @@ def __init__( self.commandId = -1 # id of running command + self._commands_running = deque() + wx.Dialog.__init__( self, parent, id, title, style=style, name="MultiImportDialog" ) @@ -122,8 +126,9 @@ def __init__( # buttons # # cancel + self._DEFAULT_CLOSE_BTN_TEXT = _("Close dialog") self.btn_close = CloseButton(parent=self.panel) - self.btn_close.SetToolTip(_("Close dialog")) + self.btn_close.SetToolTip(_(self._DEFAULT_CLOSE_BTN_TEXT)) self.btn_close.Bind(wx.EVT_BUTTON, self.OnClose) # run self.btn_run = Button(parent=self.panel, id=wx.ID_OK, label=_("&Import")) @@ -131,7 +136,7 @@ def __init__( self.btn_run.SetDefault() self.btn_run.Bind(wx.EVT_BUTTON, self.OnRun) - self.Bind(wx.EVT_CLOSE, lambda evt: self.Destroy()) + self.Bind(wx.EVT_CLOSE, self._handleCloseEvent) self.notebook = GNotebook(parent=self, style=globalvar.FNPageDStyle) @@ -270,6 +275,33 @@ def _validateOutputMapName(self): return False return True + def _handleCloseEvent(self, event: wx.CloseEvent): + if event.CanVeto() and len(self._commands_running) > 0: + # prevent dialog close while async commands are running + event.Veto() + return + self.Destroy() + + def _addToCommandQueue(self): + self._commands_running.append(True) + + # disable the dialog close button + self.btn_close.SetToolTip( + _("Dialog cannot be closed while command is running.") + ) + self.btn_close.Disable() + + def _removeFromCommandQueue(self): + try: + self._commands_running.pop() + except IndexError: + pass + finally: + if len(self._commands_running) == 0: + # enable the dialog close button + self.btn_close.Enable() + self.btn_close.SetToolTip(self._DEFAULT_CLOSE_BTN_TEXT) + def OnClose(self, event=None): """Close dialog""" self.Close() @@ -518,6 +550,7 @@ def OnRun(self, event): ): cmd.append("--overwrite") + self._addToCommandQueue() # run in Layer Manager self._giface.RunCmd( cmd, onDone=self.OnCmdDone, userData=userData, addLayer=False @@ -526,9 +559,11 @@ def OnRun(self, event): def OnCmdDone(self, event): """Load layers and close if required""" if not hasattr(self, "AddLayers"): + self._removeFromCommandQueue() return self.AddLayers(event.returncode, event.cmd, event.userData) + self._removeFromCommandQueue() if event.returncode == 0 and self.closeOnFinish.IsChecked(): self.Close() @@ -663,6 +698,7 @@ def OnRun(self, event): ): cmd.append("--overwrite") + self._addToCommandQueue() # run in Layer Manager self._giface.RunCmd( cmd, onDone=self.OnCmdDone, userData=userData, addLayer=False @@ -671,10 +707,13 @@ def OnRun(self, event): def OnCmdDone(self, event): """Load layers and close if required""" if not hasattr(self, "AddLayers"): + self._removeFromCommandQueue() return self.AddLayers(event.returncode, event.cmd, event.userData) + self._removeFromCommandQueue() + if self.popOGR: os.environ.pop("GRASS_VECTOR_OGR") @@ -873,16 +912,20 @@ def OnRun(self, event): ): cmd.append("--overwrite") + self._commands_running.append(True) # run in Layer Manager self._giface.RunCmd(cmd, onDone=self.OnCmdDone, addLayer=False) def OnCmdDone(self, event): """Load layers and close if required""" if not hasattr(self, "AddLayers"): + self._removeFromCommandQueue() return self.AddLayers(event.returncode, event.cmd) + self._removeFromCommandQueue() + if self.closeOnFinish.IsChecked(): self.Close() From 5cff700eb5bcaa9adcba7cb67c790d16407acaf1 Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Fri, 18 Oct 2024 16:35:35 -0400 Subject: [PATCH 414/471] docs: Fixed E721 in gunittest/ (#4549) --- .flake8 | 3 --- python/grass/gunittest/invoker.py | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.flake8 b/.flake8 index 306a11fe02b..a5588997635 100644 --- a/.flake8 +++ b/.flake8 @@ -66,7 +66,6 @@ per-file-ignores = # TODO: Is this really needed? python/grass/pygrass/vector/__init__.py: E402 python/grass/pygrass/raster/__init__.py: E402 - python/grass/gunittest/invoker.py: E721 python/grass/pygrass/vector/__init__.py: E402 python/grass/pygrass/modules/interface/*.py: F401 python/grass/pygrass/modules/grid/*.py: F401 @@ -82,13 +81,11 @@ per-file-ignores = python/grass/temporal/temporal_granularity.py: E722 python/grass/temporal/temporal_raster_base_algebra.py: E722 python/grass/temporal/temporal_topology_dataset_connector.py: E722 - python/grass/temporal/univar_statistics.py: E231 # Current benchmarks/tests are changing sys.path before import. # Possibly, a different approach should be taken there anyway. python/grass/pygrass/tests/benchmark.py: E402, F401, F821 # Configuration file for Sphinx: # Ignoring import/code mix and line length. - python/grass/docs/conf.py: E402 # Files not managed by Black python/grass/imaging/images2gif.py: E226 # Unused imports in init files diff --git a/python/grass/gunittest/invoker.py b/python/grass/gunittest/invoker.py index f07e8dd5a84..2fe9c898b8b 100644 --- a/python/grass/gunittest/invoker.py +++ b/python/grass/gunittest/invoker.py @@ -244,7 +244,7 @@ def try_decode(data, encodings): Path(stdout_path).write_text(stdout) with open(stderr_path, "w") as stderr_file: - if type(stderr) == "bytes": + if isinstance(stderr, bytes): stderr_file.write(decode(stderr)) elif isinstance(stderr, str): stderr_file.write(stderr) From 2a0ad5f1057ad66f716f044bc13bc4d6a7be5d95 Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Fri, 18 Oct 2024 16:52:08 -0400 Subject: [PATCH 415/471] wxGUI: Fixed E266 in nviz/ (#4547) --- .flake8 | 2 +- gui/wxpython/nviz/tools.py | 46 -------------------------------------- 2 files changed, 1 insertion(+), 47 deletions(-) diff --git a/.flake8 b/.flake8 index a5588997635..5d2d792b0c3 100644 --- a/.flake8 +++ b/.flake8 @@ -25,7 +25,7 @@ per-file-ignores = gui/scripts/d.wms.py: E501 gui/wxpython/image2target/g.gui.image2target.py: E501 gui/wxpython/modules/*: F841, E722 - gui/wxpython/nviz/*: E266, E722, F403, F405 + gui/wxpython/nviz/*: E722, F403, F405 gui/wxpython/photo2image/*: F841, E722, E265 gui/wxpython/photo2image/g.gui.photo2image.py: E501, F841 gui/wxpython/psmap/*: F841, E266, E722 diff --git a/gui/wxpython/nviz/tools.py b/gui/wxpython/nviz/tools.py index 153f8ffef86..7ced7d157e1 100644 --- a/gui/wxpython/nviz/tools.py +++ b/gui/wxpython/nviz/tools.py @@ -1196,33 +1196,6 @@ def _createSurfacePage(self, parent): flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border=3, ) - # - # mask - # - # box = wx.StaticBox (parent = panel, id = wx.ID_ANY, - # label = " %s " % (_("Mask"))) - ## boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL) - ## gridSizer = wx.GridBagSizer(vgap = 5, hgap = 5) - ## - # gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY, - # label = _("Mask zeros:")), - # pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL) - ## - # elev = wx.CheckBox(parent = panel, id = wx.ID_ANY, - # label = _("by elevation")) - # elev.Enable(False) # TODO: not implemented yet - ## gridSizer.Add(item = elev, pos = (0, 1)) - ## - # color = wx.CheckBox(parent = panel, id = wx.ID_ANY, - # label = _("by color")) - # color.Enable(False) # TODO: not implemented yet - ## gridSizer.Add(item = color, pos = (0, 2)) - ## - # boxSizer.Add(item = gridSizer, proportion = 1, - # flag = wx.ALL | wx.EXPAND, border = 3) - # pageSizer.Add(item = boxSizer, proportion = 0, - ## flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, - # border = 3) panel.SetSizer(pageSizer) @@ -1852,24 +1825,6 @@ def _createVectorPage(self, parent): icolor.Bind(csel.EVT_COLOURSELECT, self.OnVectorPoints) gridSizer.Add(icolor, flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT, pos=(0, 4)) - # icon width - seems to do nothing - # gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY, - # label = _("width")), - # pos = (1, 1), flag = wx.ALIGN_CENTER_VERTICAL | - # wx.ALIGN_RIGHT) - ## - # iwidth = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1), - ## initial = 1, - ## min = 1, - # max = 1e6) - # iwidth.SetName('value') - # iwidth.SetValue(100) - ## self.win['vector']['points']['width'] = iwidth.GetId() - ## iwidth.Bind(wx.EVT_SPINCTRL, self.OnVectorPoints) - ## iwidth.Bind(wx.EVT_TEXT, self.OnVectorPoints) - # gridSizer.Add(item = iwidth, pos = (1, 2), - # flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT) - # icon symbol gridSizer.Add( StaticText(parent=panel, id=wx.ID_ANY, label=_("symbol:")), pos=(0, 5), @@ -3084,7 +3039,6 @@ def _createIsosurfacePanel(self, parent): ) value.Bind(wx.EVT_TEXT_ENTER, self.OnVolumeIsosurfMap) value.Bind(wx.EVT_KILL_FOCUS, self.OnVolumeIsosurfMap) - ## value.Bind(wx.EVT_TEXT, self.OnVolumeIsosurfMap) else: size = (65, -1) value = SpinCtrl(parent=panel, id=wx.ID_ANY, size=size, initial=0) From 1a803b41615713e89d4b531e204ee76e22318255 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 19 Oct 2024 06:14:44 -0400 Subject: [PATCH 416/471] CI(deps): Update ubuntu:22.04 Docker digest to 0e5e4a5 (#4553) --- Dockerfile | 2 +- docker/ubuntu/Dockerfile | 2 +- docker/ubuntu_wxgui/Dockerfile | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 41fb3264c07..a5c6ab1ee1d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,7 +5,7 @@ ARG GUI=without -FROM ubuntu:22.04@sha256:58b87898e82351c6cf9cf5b9f3c20257bb9e2dcf33af051e12ce532d7f94e3fe as common_start +FROM ubuntu:22.04@sha256:0e5e4a57c2499249aafc3b40fcd541e9a456aab7296681a3994d631587203f97 as common_start LABEL authors="Carmen Tawalika,Markus Neteler,Anika Weinmann,Stefan Blumentrath" LABEL maintainer="tawalika@mundialis.de,neteler@mundialis.de,weinmann@mundialis.de" diff --git a/docker/ubuntu/Dockerfile b/docker/ubuntu/Dockerfile index 41fb3264c07..a5c6ab1ee1d 100644 --- a/docker/ubuntu/Dockerfile +++ b/docker/ubuntu/Dockerfile @@ -5,7 +5,7 @@ ARG GUI=without -FROM ubuntu:22.04@sha256:58b87898e82351c6cf9cf5b9f3c20257bb9e2dcf33af051e12ce532d7f94e3fe as common_start +FROM ubuntu:22.04@sha256:0e5e4a57c2499249aafc3b40fcd541e9a456aab7296681a3994d631587203f97 as common_start LABEL authors="Carmen Tawalika,Markus Neteler,Anika Weinmann,Stefan Blumentrath" LABEL maintainer="tawalika@mundialis.de,neteler@mundialis.de,weinmann@mundialis.de" diff --git a/docker/ubuntu_wxgui/Dockerfile b/docker/ubuntu_wxgui/Dockerfile index e15d5dcf478..0d1ead788a3 100644 --- a/docker/ubuntu_wxgui/Dockerfile +++ b/docker/ubuntu_wxgui/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:22.04@sha256:58b87898e82351c6cf9cf5b9f3c20257bb9e2dcf33af051e12ce532d7f94e3fe +FROM ubuntu:22.04@sha256:0e5e4a57c2499249aafc3b40fcd541e9a456aab7296681a3994d631587203f97 LABEL authors="Carmen Tawalika,Markus Neteler,Anika Weinmann,Tomas Zigo" LABEL maintainer="tawalika@mundialis.de,neteler@mundialis.de,weinmann@mundialis.de" From 7fbaa7e5a1b7cfe4fa3701a4ecfef51547843269 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Sun, 20 Oct 2024 10:52:45 -0400 Subject: [PATCH 417/471] style: Enable checking for pep8-naming (N) rules with ruff, fix N804, N805 and N813 (#4556) * style: Enable checking for pep8-naming (N) rules with Ruff Rules already respected: - dunder-function-name (N807) - constant-imported-as-non-constant (N811) - camelcase-imported-as-acronym (N817) * style: Fix invalid-first-argument-name-for-class-method (N804) Ruff rule: https://docs.astral.sh/ruff/rules/invalid-first-argument-name-for-class-method/ Two files had class methods that didn't use cls as their first argument * style: Fix camelcase-imported-as-lowercase (N813) Ruff rule: https://docs.astral.sh/ruff/rules/camelcase-imported-as-lowercase/ One instance only in a pytest file * style: Fix invalid-first-argument-name-for-method (N805) Ruff rule: https://docs.astral.sh/ruff/rules/invalid-first-argument-name-for-method/ Three instances in gui/wxpython/mapswipe/frame.py were ignored, as an inner class is defined using self2 and also references self from the containing class. --- gui/wxpython/mapswipe/frame.py | 6 ++--- pyproject.toml | 11 +++++++++ .../interface/testsuite/test_modules.py | 4 ++-- python/grass/script/tests/test_script_task.py | 6 ++--- raster/r.basins.fill/testsuite/testrbf.py | 4 ++-- raster/r.series/testsuite/test_r_series.py | 6 ++--- .../r.terraflow/testsuite/test_r_terraflow.py | 24 +++++++++---------- raster/r.to.vect/testsuite/test_r_to_vect.py | 4 ++-- .../r.viewshed/testsuite/test_r_viewshed.py | 4 ++-- scripts/r.import/testsuite/test_r_import.py | 4 ++-- scripts/r.reclass.area/testsuite/testrra.py | 4 ++-- scripts/v.import/testsuite/test_v_import.py | 4 ++-- vector/v.extract/testsuite/test_v_extract.py | 4 ++-- vector/v.net/testsuite/test_v_net.py | 4 ++-- vector/v.random/testsuite/test_v_random.py | 6 ++--- vector/v.select/testsuite/test_v_select.py | 4 ++-- vector/v.to.3d/testsuite/test_vto3d.py | 9 ++++--- 17 files changed, 61 insertions(+), 47 deletions(-) diff --git a/gui/wxpython/mapswipe/frame.py b/gui/wxpython/mapswipe/frame.py index a7dd0df8f56..b7b81636605 100644 --- a/gui/wxpython/mapswipe/frame.py +++ b/gui/wxpython/mapswipe/frame.py @@ -545,15 +545,15 @@ class _onDone: we are pasting together 2 rendered images, so we need to know when both are finished.""" - def __init__(self2): + def __init__(self2): # noqa: N805 self2.called = 0 - def __call__(self2): + def __call__(self2): # noqa: N805 self2.called += 1 if self2.called == 2: self2.process() - def process(self2): + def process(self2): # noqa: N805 # create empty white image - needed for line im = wx.Image(width, height) im.Replace(0, 0, 0, 255, 255, 255) diff --git a/pyproject.toml b/pyproject.toml index 2a2287aaaad..ed9cd2255a1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,6 +48,7 @@ select = [ "INT", # flake8-gettext (INT) "ISC", # flake8-implicit-str-concat (ISC) "LOG", # flake8-logging (LOG) + "N", # pep8-naming (N) "NPY", # NumPy-specific rules (NPY) "PERF", # Perflint (PERF) "PGH", # pygrep-hooks (PGH) @@ -145,6 +146,16 @@ ignore = [ "FBT003", # boolean-positional-value-in-call "I001", # unsorted-imports "ISC003", # explicit-string-concatenation + "N801", # invalid-class-name + "N802", # invalid-function-name + "N803", # invalid-argument-name + "N806", # non-lowercase-variable-in-function + "N812", # lowercase-imported-as-non-lowercase + "N814", # camelcase-imported-as-constant + "N815", # mixed-case-variable-in-class-scope + "N816", # mixed-case-variable-in-global-scope + "N818", # error-suffix-on-exception-name + "N999", # invalid-module-name "PERF203", # try-except-in-loop "PERF401", # manual-list-comprehension "PERF402", # manual-list-copy diff --git a/python/grass/pygrass/modules/interface/testsuite/test_modules.py b/python/grass/pygrass/modules/interface/testsuite/test_modules.py index 3318154f87d..ee005498692 100644 --- a/python/grass/pygrass/modules/interface/testsuite/test_modules.py +++ b/python/grass/pygrass/modules/interface/testsuite/test_modules.py @@ -21,7 +21,7 @@ class ModulesMeta(type): - def __new__(mcs, name, bases, dict): + def __new__(cls, name, bases, dict): def gen_test(cmd): def test(self): Module(cmd) @@ -36,7 +36,7 @@ def test(self): for cmd in cmds: test_name = "test__%s" % cmd.replace(".", "_") dict[test_name] = gen_test(cmd) - return type.__new__(mcs, name, bases, dict) + return type.__new__(cls, name, bases, dict) class TestModules(TestCase, metaclass=ModulesMeta): diff --git a/python/grass/script/tests/test_script_task.py b/python/grass/script/tests/test_script_task.py index 6799885761e..cae8735827e 100644 --- a/python/grass/script/tests/test_script_task.py +++ b/python/grass/script/tests/test_script_task.py @@ -1,11 +1,11 @@ -from grass.script.task import grassTask as gtask +from grass.script.task import grassTask def test_mapcalc_simple_e_name(): - gt = gtask("r.mapcalc.simple") + gt = grassTask("r.mapcalc.simple") assert gt.get_param("e")["name"] == "e" def test_mapcalc_simple_expession_name(): - gt = gtask("r.mapcalc.simple") + gt = grassTask("r.mapcalc.simple") assert gt.get_param("expression")["name"] == "expression" diff --git a/raster/r.basins.fill/testsuite/testrbf.py b/raster/r.basins.fill/testsuite/testrbf.py index b9937b05689..eaa57388150 100644 --- a/raster/r.basins.fill/testsuite/testrbf.py +++ b/raster/r.basins.fill/testsuite/testrbf.py @@ -47,8 +47,8 @@ def setUpClass(cls): def tearDownClass(cls): cls.del_temp_region() - def tearDown(cls): - cls.runModule("g.remove", flags="f", type="raster", name=cls.output) + def tearDown(self): + self.runModule("g.remove", flags="f", type="raster", name=self.output) def test_no1(self): lakes = "lakes" diff --git a/raster/r.series/testsuite/test_r_series.py b/raster/r.series/testsuite/test_r_series.py index 93bfd129242..e067a26e1da 100644 --- a/raster/r.series/testsuite/test_r_series.py +++ b/raster/r.series/testsuite/test_r_series.py @@ -18,13 +18,13 @@ def setUpClass(cls): call_module("r.mapcalc", expression=f"{cls.sum_mapcalc} = {cls.elevation} * 4") @classmethod - def tearDownClass(self): - self.del_temp_region() + def tearDownClass(cls): + cls.del_temp_region() call_module( "g.remove", flags="f", type_="raster", - name=self.sum_mapcalc, + name=cls.sum_mapcalc, ) def tearDown(self): diff --git a/raster/r.terraflow/testsuite/test_r_terraflow.py b/raster/r.terraflow/testsuite/test_r_terraflow.py index 62ee0eee15d..4085151a337 100644 --- a/raster/r.terraflow/testsuite/test_r_terraflow.py +++ b/raster/r.terraflow/testsuite/test_r_terraflow.py @@ -23,25 +23,25 @@ def tearDownClass(cls): """!Remove the temporary region""" cls.del_temp_region() - def setUp(cls): + def setUp(self): """Create input data for steady state groundwater flow computation""" - if not os.path.exists(cls.testdir): - os.mkdir(cls.testdir) + if not os.path.exists(self.testdir): + os.mkdir(self.testdir) - def test_univar_mfd(cls): + def test_univar_mfd(self): # compute a steady state groundwater flow - cls.assertModule( + self.assertModule( "r.terraflow", overwrite=True, verbose=True, - elevation=cls.elevation, + elevation=self.elevation, filled="terra_flooded", direction="terra_flowdir", swatershed="terra_sink", accumulation="terra_flowaccum", tci="terra_tci", - directory=cls.testdir, - stats=cls.teststats, + directory=self.testdir, + stats=self.teststats, ) # Output of r.univar -g @@ -111,16 +111,16 @@ def test_univar_mfd(cls): sum=8341670.75914752""" # cls.assertRasterFitsUnivar(raster="terra_flooded", reference=terra_flooded_univar, precision=3) - cls.assertRasterFitsUnivar( + self.assertRasterFitsUnivar( raster="terra_flowdir", reference=terra_flowdir_univar, precision=3 ) - cls.assertRasterFitsUnivar( + self.assertRasterFitsUnivar( raster="terra_sink", reference=terra_sink_univar, precision=3 ) - cls.assertRasterFitsUnivar( + self.assertRasterFitsUnivar( raster="terra_flowaccum", reference=terra_flowaccum_univar, precision=3 ) - cls.assertRasterFitsUnivar( + self.assertRasterFitsUnivar( raster="terra_tci", reference=terra_tci_univar, precision=3 ) diff --git a/raster/r.to.vect/testsuite/test_r_to_vect.py b/raster/r.to.vect/testsuite/test_r_to_vect.py index fbd27af6a33..7826bf44084 100644 --- a/raster/r.to.vect/testsuite/test_r_to_vect.py +++ b/raster/r.to.vect/testsuite/test_r_to_vect.py @@ -28,8 +28,8 @@ def setUpClass(cls): def tearDownClass(cls): cls.del_temp_region() - def tearDown(cls): - cls.runModule("g.remove", type="vector", flags="f", name=cls.output) + def tearDown(self): + self.runModule("g.remove", type="vector", flags="f", name=self.output) def test_flags(self): """Testing flag s""" diff --git a/raster/r.viewshed/testsuite/test_r_viewshed.py b/raster/r.viewshed/testsuite/test_r_viewshed.py index b6825c62018..2b136abd551 100644 --- a/raster/r.viewshed/testsuite/test_r_viewshed.py +++ b/raster/r.viewshed/testsuite/test_r_viewshed.py @@ -15,10 +15,10 @@ def setUpClass(cls): def tearDownClass(cls): cls.del_temp_region() - def tearDown(cls): + def tearDown(self): """Remove viewshed map after each test method""" # TODO: eventually, removing maps should be handled through testing framework functions - cls.runModule("g.remove", flags="f", type="raster", name=cls.viewshed) + self.runModule("g.remove", flags="f", type="raster", name=self.viewshed) def test_limits(self): """Test if results is in expected limits""" diff --git a/scripts/r.import/testsuite/test_r_import.py b/scripts/r.import/testsuite/test_r_import.py index 0e4ec3839e5..075b3a363cf 100755 --- a/scripts/r.import/testsuite/test_r_import.py +++ b/scripts/r.import/testsuite/test_r_import.py @@ -13,9 +13,9 @@ class TestRImportRegion(TestCase): def setUpClass(cls): cls.runModule("g.region", raster="elevation") - def tearDown(cls): + def tearDown(self): """Remove imported map after each test method""" - cls.runModule("g.remove", flags="f", type="raster", name=cls.imported) + self.runModule("g.remove", flags="f", type="raster", name=self.imported) def test_import_estimate(self): """Test e flag""" diff --git a/scripts/r.reclass.area/testsuite/testrra.py b/scripts/r.reclass.area/testsuite/testrra.py index e6e2300875b..8270c122129 100644 --- a/scripts/r.reclass.area/testsuite/testrra.py +++ b/scripts/r.reclass.area/testsuite/testrra.py @@ -26,8 +26,8 @@ def setUpClass(cls): def tearDownClass(cls): cls.del_temp_region() - def tearDown(cls): - cls.runModule("g.remove", type="raster", flags="f", name=cls.output) + def tearDown(self): + self.runModule("g.remove", type="raster", flags="f", name=self.output) def test_flag_c(self): """Testing flag c""" diff --git a/scripts/v.import/testsuite/test_v_import.py b/scripts/v.import/testsuite/test_v_import.py index d8dd6b85887..90934bbb3e6 100755 --- a/scripts/v.import/testsuite/test_v_import.py +++ b/scripts/v.import/testsuite/test_v_import.py @@ -18,9 +18,9 @@ class TestVImport(TestCase): imported = "test_v_import_imported" - def tearDown(cls): + def tearDown(self): """Remove imported map after each test method""" - cls.runModule("g.remove", flags="f", type="vector", name=cls.imported) + self.runModule("g.remove", flags="f", type="vector", name=self.imported) def test_import_same_proj_gpkg(self): """Import GPKG in same proj, default params""" diff --git a/vector/v.extract/testsuite/test_v_extract.py b/vector/v.extract/testsuite/test_v_extract.py index d266e8ad649..7f067c5723e 100644 --- a/vector/v.extract/testsuite/test_v_extract.py +++ b/vector/v.extract/testsuite/test_v_extract.py @@ -54,8 +54,8 @@ def setUpClass(cls): def tearDownClass(cls): cls.del_temp_region() - def tearDown(cls): - cls.runModule("g.remove", flags="f", type="vector", name=cls.output) + def tearDown(self): + self.runModule("g.remove", flags="f", type="vector", name=self.output) def test_flagd(self): """Testing flag d""" diff --git a/vector/v.net/testsuite/test_v_net.py b/vector/v.net/testsuite/test_v_net.py index 469ac127f47..50bb047628f 100644 --- a/vector/v.net/testsuite/test_v_net.py +++ b/vector/v.net/testsuite/test_v_net.py @@ -6,10 +6,10 @@ class TestVNet(TestCase): network = "test_vnet" - def tearDown(cls): + def tearDown(self): """Remove viewshed map after each test method""" # TODO: eventually, removing maps should be handled through testing framework functions - cls.runModule("g.remove", flags="f", type="vector", name=cls.network) + self.runModule("g.remove", flags="f", type="vector", name=self.network) def test_nodes(self): """Test""" diff --git a/vector/v.random/testsuite/test_v_random.py b/vector/v.random/testsuite/test_v_random.py index d11faf458f9..7b68434f46b 100644 --- a/vector/v.random/testsuite/test_v_random.py +++ b/vector/v.random/testsuite/test_v_random.py @@ -30,9 +30,9 @@ def setUpClass(cls): def tearDownClass(cls): cls.del_temp_region() - def tearDown(cls): - cls.runModule( - "g.remove", type="vector", flags="f", name=(cls.output, cls.output2) + def tearDown(self): + self.runModule( + "g.remove", type="vector", flags="f", name=(self.output, self.output2) ) def test_num_points(self): diff --git a/vector/v.select/testsuite/test_v_select.py b/vector/v.select/testsuite/test_v_select.py index b8564505a6a..c72aac8c4cc 100644 --- a/vector/v.select/testsuite/test_v_select.py +++ b/vector/v.select/testsuite/test_v_select.py @@ -17,8 +17,8 @@ class TestRasterReport(TestCase): ainput = "geology" output = "testvselect" - def tearDown(cls): - cls.runModule("g.remove", type="vector", flags="f", name=cls.output) + def tearDown(self): + self.runModule("g.remove", type="vector", flags="f", name=self.output) def test_opo(self): """Testing operator overlap""" diff --git a/vector/v.to.3d/testsuite/test_vto3d.py b/vector/v.to.3d/testsuite/test_vto3d.py index bfa982ddd87..2297ec3a174 100644 --- a/vector/v.to.3d/testsuite/test_vto3d.py +++ b/vector/v.to.3d/testsuite/test_vto3d.py @@ -15,10 +15,13 @@ def setUpClass(cls): def tearDownClass(cls): cls.del_temp_region() - def tearDown(cls): + def tearDown(self): """Remove contours map after each test method""" - cls.runModule( - "g.remove", flags="f", type="vector", name=[cls.contours2d, cls.contours3d] + self.runModule( + "g.remove", + flags="f", + type="vector", + name=[self.contours2d, self.contours3d], ) def test_contours(self): From 884fc6f93d9b2090b5fcff149376dd4eeda3f6dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Sun, 20 Oct 2024 11:13:19 -0400 Subject: [PATCH 418/471] style: Fix multi-value-repeated-key-literal (F601) (Pylint W0109) (#4557) Ruff rule: https://docs.astral.sh/ruff/rules/multi-value-repeated-key-literal/ Pylint : https://pylint.readthedocs.io/en/latest/user_guide/messages/warning/duplicate-key.html --- gui/wxpython/gmodeler/model.py | 1 - pyproject.toml | 1 - python/grass/script/raster.py | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/gui/wxpython/gmodeler/model.py b/gui/wxpython/gmodeler/model.py index 23e308037ff..fdf6ac63b36 100644 --- a/gui/wxpython/gmodeler/model.py +++ b/gui/wxpython/gmodeler/model.py @@ -2285,7 +2285,6 @@ def _processComments(self): "size": size, "text": text, "id": int(node.get("id", -1)), - "text": text, } ) diff --git a/pyproject.toml b/pyproject.toml index ed9cd2255a1..6aa9e062c29 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -136,7 +136,6 @@ ignore = [ "F401", # unused-import "F403", # undefined-local-with-import-star "F405", # undefined-local-with-import-star-usage - "F601", # multi-value-repeated-key-literal "F811", # redefined-while-unused "F821", # undefined-name "F822", # undefined-export diff --git a/python/grass/script/raster.py b/python/grass/script/raster.py index bcaab7ef596..54c1219dbaf 100644 --- a/python/grass/script/raster.py +++ b/python/grass/script/raster.py @@ -66,7 +66,7 @@ def raster_history(map, overwrite=False, env=None): "Unable to write history for <%(map)s>. " "Raster map <%(map)s> not found in current mapset." ) - % {"map": map, "map": map} + % {"map": map} ) return False From 32723e04f5edddf4ccca280efa05afbb122929d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Sun, 20 Oct 2024 11:34:46 -0400 Subject: [PATCH 419/471] style: Fix if-else-block-instead-of-dict-get (SIM401) (#4558) Ruff rule: https://docs.astral.sh/ruff/rules/if-else-block-instead-of-dict-get/ Python docs: https://docs.python.org/3/library/stdtypes.html#dict.get The get method never raises a KeyError. --- gui/wxpython/iclass/frame.py | 2 +- gui/wxpython/mapdisp/frame.py | 2 +- gui/wxpython/vnet/dialogs.py | 5 +---- gui/wxpython/vnet/vnet_core.py | 5 +---- pyproject.toml | 1 - python/grass/temporal/register.py | 5 +---- scripts/i.pansharpen/i.pansharpen.py | 5 +---- 7 files changed, 6 insertions(+), 19 deletions(-) diff --git a/gui/wxpython/iclass/frame.py b/gui/wxpython/iclass/frame.py index 606c364c76f..b19d8d7d3ac 100644 --- a/gui/wxpython/iclass/frame.py +++ b/gui/wxpython/iclass/frame.py @@ -515,7 +515,7 @@ def ActivateSecondMap(self, event=None): def GetMapToolbar(self): """Returns toolbar with zooming tools""" - return self.toolbars["iClassMap"] if "iClassMap" in self.toolbars else None + return self.toolbars.get("iClassMap", None) def GetClassColor(self, cat): """Get class color as string diff --git a/gui/wxpython/mapdisp/frame.py b/gui/wxpython/mapdisp/frame.py index c9b5d86e4ba..0a1660ff06a 100644 --- a/gui/wxpython/mapdisp/frame.py +++ b/gui/wxpython/mapdisp/frame.py @@ -1569,7 +1569,7 @@ def SetProperties( def GetMapToolbar(self): """Returns toolbar with zooming tools""" - return self.toolbars["map"] if "map" in self.toolbars else None + return self.toolbars.get("map", None) def GetDialog(self, name): """Get selected dialog if exist""" diff --git a/gui/wxpython/vnet/dialogs.py b/gui/wxpython/vnet/dialogs.py index 8d24b08df91..ee553a0b741 100644 --- a/gui/wxpython/vnet/dialogs.py +++ b/gui/wxpython/vnet/dialogs.py @@ -1015,10 +1015,7 @@ def OnAnalysisChanged(self, event): attrCols = an_props["cmdParams"]["cols"] for col in attrCols.keys(): - if "inputField" in attrCols[col]: - colInptF = attrCols[col]["inputField"] - else: - colInptF = col + colInptF = attrCols[col].get("inputField", col) if col in skip: continue diff --git a/gui/wxpython/vnet/vnet_core.py b/gui/wxpython/vnet/vnet_core.py index 2e98aa2ee8f..f55550e65e2 100644 --- a/gui/wxpython/vnet/vnet_core.py +++ b/gui/wxpython/vnet/vnet_core.py @@ -744,10 +744,7 @@ def _setInputParams(self, analysis, params, flags): inParams = [] for col, v in self.data.GetAnalysisProperties()["cmdParams"]["cols"].items(): - if "inputField" in v: - colInptF = v["inputField"] - else: - colInptF = col + colInptF = v.get("inputField", col) inParams.append(col + "=" + params[colInptF]) diff --git a/pyproject.toml b/pyproject.toml index 6aa9e062c29..eeffbc0ec95 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -249,7 +249,6 @@ ignore = [ "SIM116", # if-else-block-instead-of-dict-lookup "SIM118", # in-dict-keys "SIM223", # expr-and-false - "SIM401", # if-else-block-instead-of-dict-get "SLF001", # private-member-access "TRY002", # raise-vanilla-class "TRY003", # raise-vanilla-args diff --git a/python/grass/temporal/register.py b/python/grass/temporal/register.py index 8b7852ab557..92be36a3547 100644 --- a/python/grass/temporal/register.py +++ b/python/grass/temporal/register.py @@ -264,10 +264,7 @@ def register_maps_in_space_time_dataset( end = row["end"] # Use the semantic label from file - if "semantic_label" in row: - semantic_label = row["semantic_label"] - else: - semantic_label = None + semantic_label = row.get("semantic_label", None) is_in_db = map_object.is_in_db(dbif, mapset) diff --git a/scripts/i.pansharpen/i.pansharpen.py b/scripts/i.pansharpen/i.pansharpen.py index bb271c07deb..e78581c8845 100755 --- a/scripts/i.pansharpen/i.pansharpen.py +++ b/scripts/i.pansharpen/i.pansharpen.py @@ -750,10 +750,7 @@ def matchhist(original, target, matched): ) for n in range(256): - if str(n) in stats_dict: - num_cells = stats_dict[str(n)] - else: - num_cells = 0 + num_cells = stats_dict.get(str(n), 0) cum_cells += num_cells From e523163b16c24df62d23c7e5bf149846ae2d42d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Sun, 20 Oct 2024 16:02:11 -0400 Subject: [PATCH 420/471] style: Enable checking for E ruff rules from pycodestyle, fixing simple issues (#4560) * style: Fix missing-whitespace (E231) * style: Fix mixed-spaces-and-tabs (E101) Ruff rule: https://docs.astral.sh/ruff/rules/mixed-spaces-and-tabs/ * style: Enable checking for E ruff rules from pycodestyle * style: Fix multiple-leading-hashes-for-block-comment (E266) Ruff rule: https://docs.astral.sh/ruff/rules/multiple-leading-hashes-for-block-comment/ For gui/wxpython/psmap/frame.py, the commented code was present since it was moved from addons to the main repo 13 years ago. At the time, both pair of lines had the same indentation, before PEP8 only formatted the second lines. File temporal/t.rast.what/t.rast.what.py is ignored for E265 and E266, as it is being addressed in #4550 --- gui/wxpython/psmap/dialogs.py | 2 +- gui/wxpython/psmap/frame.py | 4 ---- pyproject.toml | 8 +++----- python/grass/temporal/univar_statistics.py | 4 ++-- raster/r.basins.fill/testsuite/testrbf.py | 4 ++-- .../r.in.ascii/testsuite/test_r_in_ascii.py | 4 ++-- raster/r.proj/testsuite/test_rproj.py | 20 +++++++++---------- raster/r.reclass/testsuite/test_r_reclass.py | 4 ++-- raster/r.tile/testsuite/testrt.py | 4 ++-- raster/r.what/testsuite/testrw.py | 4 ++-- scripts/r.reclass.area/testsuite/testrra.py | 4 ++-- vector/v.extract/testsuite/test_v_extract.py | 4 ++-- .../v.vect.stats/testsuite/test_vect_stats.py | 4 ++-- 13 files changed, 32 insertions(+), 38 deletions(-) diff --git a/gui/wxpython/psmap/dialogs.py b/gui/wxpython/psmap/dialogs.py index 0bbe779818a..c4784525cc9 100644 --- a/gui/wxpython/psmap/dialogs.py +++ b/gui/wxpython/psmap/dialogs.py @@ -3619,7 +3619,7 @@ def _rasterLegend(self, notebook): self.Bind(wx.EVT_CHECKBOX, self.OnIsLegend, self.isRLegend) self.Bind(wx.EVT_RADIOBUTTON, self.OnDiscrete, self.discrete) self.Bind(wx.EVT_RADIOBUTTON, self.OnDiscrete, self.continuous) - ## self.Bind(wx.EVT_CHECKBOX, self.OnDefaultSize, panel.defaultSize) + # self.Bind(wx.EVT_CHECKBOX, self.OnDefaultSize, panel.defaultSize) self.Bind(wx.EVT_CHECKBOX, self.OnRange, self.range) self.rasterSelect.GetTextCtrl().Bind(wx.EVT_TEXT, self.OnRaster) diff --git a/gui/wxpython/psmap/frame.py b/gui/wxpython/psmap/frame.py index 13fdc91a984..1d937483123 100644 --- a/gui/wxpython/psmap/frame.py +++ b/gui/wxpython/psmap/frame.py @@ -780,8 +780,6 @@ def OnAddRaster(self, event): if not self._checkMapFrameExists(type_id=id): return - ## dlg = RasterDialog(self, id = id, settings = self.instruction) - # dlg.ShowModal() if "mapNotebook" in self.openDialogs: self.openDialogs["mapNotebook"].notebook.ChangeSelection(1) else: @@ -800,8 +798,6 @@ def OnAddVect(self, event): if not self._checkMapFrameExists(type_id=id): return - ## dlg = MainVectorDialog(self, id = id, settings = self.instruction) - # dlg.ShowModal() if "mapNotebook" in self.openDialogs: self.openDialogs["mapNotebook"].notebook.ChangeSelection(2) else: diff --git a/pyproject.toml b/pyproject.toml index eeffbc0ec95..4b7856bab3b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,9 +34,7 @@ select = [ "COM", # flake8-commas (COM) "D", # pydocstyle (D) "DTZ", # flake8-datetimez (DTZ) - "E4", # pycodestyle (E, W) - "E7", # pycodestyle (E, W) - "E9", # pycodestyle (E, W) + "E", # pycodestyle (E, W) "F", # Pyflakes (F) "FA", # flake8-future-annotations (FA) "FBT", # flake8-boolean-trap (FBT) @@ -127,8 +125,8 @@ ignore = [ "DTZ006", # call-datetime-fromtimestamp "DTZ007", # call-datetime-strptime-without-zone "DTZ011", # call-date-today - "E401", # multiple-imports-on-one-line "E402", # module-import-not-at-top-of-file + "E501", # line-too-long "E721", # type-comparison "E722", # bare-except "E731", # lambda-assignment @@ -366,7 +364,7 @@ ignore = [ "temporal/t.rast.algebra/testsu*/*_algebra_arithmetic.py" = ["FLY002"] "temporal/t.rast.colors/t.rast.colors.py" = ["SIM115"] "temporal/t.rast.series/t.rast.series.py" = ["SIM115"] -"temporal/t.rast.what/t.rast.what.py" = ["SIM115"] +"temporal/t.rast.what/t.rast.what.py" = ["E265", "E266", "SIM115"] "temporal/t.register/testsu*/*_raster_different_local.py" = ["FLY002"] "temporal/t.register/testsu*/*_raster_mapmetadata.py" = ["FLY002"] "temporal/t.register/testsuite/test_t_register_raster.py" = ["FLY002"] diff --git a/python/grass/temporal/univar_statistics.py b/python/grass/temporal/univar_statistics.py index 96a9f65474d..dd334e7e9f1 100755 --- a/python/grass/temporal/univar_statistics.py +++ b/python/grass/temporal/univar_statistics.py @@ -103,7 +103,7 @@ def compute_univar_stats(registered_map_info, stats_module, fs, rast_region=Fals for perc in stats_module.inputs.percentile: perc_value = stats[ "percentile_" - f"{str(perc).rstrip('0').rstrip('.').replace('.','_')}" + f"{str(perc).rstrip('0').rstrip('.').replace('.', '_')}" ] string += f"{fs}{perc_value}" string += eol @@ -220,7 +220,7 @@ def print_gridded_dataset_univar_statistics( cols.extend( [ "percentile_" - f"{str(perc).rstrip('0').rstrip('.').replace('.','_')}" + f"{str(perc).rstrip('0').rstrip('.').replace('.', '_')}" for perc in percentile ] ) diff --git a/raster/r.basins.fill/testsuite/testrbf.py b/raster/r.basins.fill/testsuite/testrbf.py index eaa57388150..4077a2113a4 100644 --- a/raster/r.basins.fill/testsuite/testrbf.py +++ b/raster/r.basins.fill/testsuite/testrbf.py @@ -5,8 +5,8 @@ Author: Sunveer Singh Copyright: (C) 2017 by Sunveer Singh and the GRASS Development Team Licence: This program is free software under the GNU General Public - License (>=v2). Read the file COPYING that comes with GRASS - for details. + License (>=v2). Read the file COPYING that comes with GRASS + for details. """ import unittest diff --git a/raster/r.in.ascii/testsuite/test_r_in_ascii.py b/raster/r.in.ascii/testsuite/test_r_in_ascii.py index cff31b7b3e2..baa892d83a8 100644 --- a/raster/r.in.ascii/testsuite/test_r_in_ascii.py +++ b/raster/r.in.ascii/testsuite/test_r_in_ascii.py @@ -5,8 +5,8 @@ Author: Sunveer Singh, Google Code-in 2017 Copyright: (C) 2017 by Sunveer Singh and the GRASS Development Team Licence: This program is free software under the GNU General Public - License (>=v2). Read the file COPYING that comes with GRASS - for details. + License (>=v2). Read the file COPYING that comes with GRASS + for details. """ from grass.gunittest.case import TestCase diff --git a/raster/r.proj/testsuite/test_rproj.py b/raster/r.proj/testsuite/test_rproj.py index 5f3834be881..678e8b7cd06 100644 --- a/raster/r.proj/testsuite/test_rproj.py +++ b/raster/r.proj/testsuite/test_rproj.py @@ -57,7 +57,7 @@ def run_rproj_test(self, method, statics): The expected statics of the output raster """ output = method - ## Get the boundary and set up region for the projected map + # Get the boundary and set up region for the projected map stdout = call_module( "r.proj", project=src_project, @@ -80,7 +80,7 @@ def run_rproj_test(self, method, statics): res=1, ) - ## Project the map + # Project the map self.assertModule( "r.proj", project=src_project, @@ -91,13 +91,13 @@ def run_rproj_test(self, method, statics): quiet=True, ) - ## Validate the output + # Validate the output self.assertRasterFitsUnivar(output, reference=statics, precision=1e-7) self.assertRasterFitsInfo(output, reference=raster_info, precision=1e-7) def test_nearest(self): """Testing method nearest""" - ## Set up variables and validation values + # Set up variables and validation values method = "nearest" statics = """n=40930 min=55.5787925720215 @@ -109,7 +109,7 @@ def test_nearest(self): def test_bilinear(self): """Testing method bilinear""" - ## Set up variables and validation values + # Set up variables and validation values method = "bilinear" statics = """n=40845 min=56.3932914733887 @@ -121,7 +121,7 @@ def test_bilinear(self): def test_bicubic(self): """Testing method bicubic""" - ## Set up variables and validation values + # Set up variables and validation values method = "bicubic" statics = """n=40677 min=56.2407836914062 @@ -133,7 +133,7 @@ def test_bicubic(self): def test_lanczos(self): """Testing method lanczos""" - ## Set up variables and validation values + # Set up variables and validation values method = "lanczos" statics = """n=40585 min=56.2350921630859 @@ -145,7 +145,7 @@ def test_lanczos(self): def test_bilinear_f(self): """Testing method bilinear_f""" - ## Set up variables and validation values + # Set up variables and validation values method = "bilinear_f" statics = """n=40930 min=55.5787925720215 @@ -157,7 +157,7 @@ def test_bilinear_f(self): def test_bicubic_f(self): """Testing method bicubic_f""" - ## Set up variables and validation values + # Set up variables and validation values method = "bicubic_f" statics = """n=40930 min=55.5787925720215 @@ -169,7 +169,7 @@ def test_bicubic_f(self): def test_lanczos_f(self): """Testing method lanczos_f""" - ## Set up variables and validation values + # Set up variables and validation values method = "lanczos_f" statics = """n=40930 min=55.5787925720215 diff --git a/raster/r.reclass/testsuite/test_r_reclass.py b/raster/r.reclass/testsuite/test_r_reclass.py index 3f4ecfdf6a3..8b1eb2e523a 100644 --- a/raster/r.reclass/testsuite/test_r_reclass.py +++ b/raster/r.reclass/testsuite/test_r_reclass.py @@ -5,8 +5,8 @@ Author: Sunveer Singh, Google Code-in 2017 Copyright: (C) 2017 by Sunveer Singh and the GRASS Development Team Licence: This program is free software under the GNU General Public - License (>=v2). Read the file COPYING that comes with GRASS - for details. + License (>=v2). Read the file COPYING that comes with GRASS + for details. """ from grass.gunittest.case import TestCase diff --git a/raster/r.tile/testsuite/testrt.py b/raster/r.tile/testsuite/testrt.py index 6cb5a91c296..a26ff967d33 100644 --- a/raster/r.tile/testsuite/testrt.py +++ b/raster/r.tile/testsuite/testrt.py @@ -5,8 +5,8 @@ Author: Sunveer Singh, Google Code-in 2018 Copyright: (C) 2018 by Sunveer Singh and the GRASS Development Team Licence: This program is free software under the GNU General Public - License (>=v2). Read the file COPYING that comes with GRASS - for details. + License (>=v2). Read the file COPYING that comes with GRASS + for details. """ from grass.gunittest.case import TestCase diff --git a/raster/r.what/testsuite/testrw.py b/raster/r.what/testsuite/testrw.py index 8184f534720..fdaf7eec98c 100644 --- a/raster/r.what/testsuite/testrw.py +++ b/raster/r.what/testsuite/testrw.py @@ -5,8 +5,8 @@ Author: Sunveer Singh, Google Code-in 2018 Copyright: (C) 2018 by Sunveer Singh and the GRASS Development Team Licence: This program is free software under the GNU General Public - License (>=v2). Read the file COPYING that comes with GRASS - for details. + License (>=v2). Read the file COPYING that comes with GRASS + for details. """ from grass.gunittest.case import TestCase diff --git a/scripts/r.reclass.area/testsuite/testrra.py b/scripts/r.reclass.area/testsuite/testrra.py index 8270c122129..5e11bf6aecc 100644 --- a/scripts/r.reclass.area/testsuite/testrra.py +++ b/scripts/r.reclass.area/testsuite/testrra.py @@ -5,8 +5,8 @@ Author: Sunveer Singh, Google Code-in 2018 Copyright: (C) 2018 by Sunveer Singh and the GRASS Development Team Licence: This program is free software under the GNU General Public - License (>=v2). Read the file COPYING that comes with GRASS - for details. + License (>=v2). Read the file COPYING that comes with GRASS + for details. """ from grass.gunittest.case import TestCase diff --git a/vector/v.extract/testsuite/test_v_extract.py b/vector/v.extract/testsuite/test_v_extract.py index 7f067c5723e..958f153d1a3 100644 --- a/vector/v.extract/testsuite/test_v_extract.py +++ b/vector/v.extract/testsuite/test_v_extract.py @@ -5,8 +5,8 @@ Author: Sunveer Singh, Google Code-in 2017 Copyright: (C) 2017 by Sunveer Singh and the GRASS Development Team Licence: This program is free software under the GNU General Public - License (>=v2). Read the file COPYING that comes with GRASS - for details. + License (>=v2). Read the file COPYING that comes with GRASS + for details. """ import os diff --git a/vector/v.vect.stats/testsuite/test_vect_stats.py b/vector/v.vect.stats/testsuite/test_vect_stats.py index fe982ceff33..e8a1c30c966 100644 --- a/vector/v.vect.stats/testsuite/test_vect_stats.py +++ b/vector/v.vect.stats/testsuite/test_vect_stats.py @@ -5,8 +5,8 @@ Author: Sunveer Singh, Google Code-in 2017 Copyright: (C) 2017 by Sunveer Singh and the GRASS Development Team Licence: This program is free software under the GNU General Public - License (>=v2). Read the file COPYING that comes with GRASS - for details. + License (>=v2). Read the file COPYING that comes with GRASS + for details. """ from grass.gunittest.case import TestCase From 870343fe17389f3d40d0c6fa81a906d07805ac65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Sun, 20 Oct 2024 17:06:56 -0400 Subject: [PATCH 421/471] style: Fix if-else-block-instead-of-if-exp (SIM108) (part 1) (#4561) Ruff rule: https://docs.astral.sh/ruff/rules/if-else-block-instead-of-if-exp Since the amount of changes for SIM108 are quite large, (more than 330), I've decided to split the fixes in more than 1 PR. I kept `scripts/*`, `python/*` and `gui/*` in other PRs. To avoid conflicts, I didn't commit the modified `pyproject.toml`, nor extra typing annotations that I used to help guiding and validating these fixes. * style: Fix if-else-block-instead-of-if-exp (SIM108) in utils/ * style: Fix if-else-block-instead-of-if-exp (SIM108) in lib/init/grass.py * style: Fix if-else-block-instead-of-if-exp (SIM108) in doc/ * style: Fix if-else-block-instead-of-if-exp (SIM108) in man/build_html.py * style: Fix if-else-block-instead-of-if-exp (SIM108) in temporal/ * style: Manual fixes of if-else-block-instead-of-if-exp (SIM108) in temporal/ --- doc/python/raster_example_ctypes.py | 5 +-- doc/python/vector_example_ctypes.py | 5 +-- lib/init/grass.py | 33 ++++----------- man/build_html.py | 5 +-- temporal/t.info/t.info.py | 6 +-- temporal/t.list/t.list.py | 5 +-- temporal/t.rast.accdetect/t.rast.accdetect.py | 41 ++++--------------- .../t.rast.accumulate/t.rast.accumulate.py | 38 ++++------------- temporal/t.rast.aggregate/t.rast.aggregate.py | 5 +-- temporal/t.rast.to.rast3/t.rast.to.rast3.py | 5 +-- temporal/t.rast.what/t.rast.what.py | 20 ++------- temporal/t.rename/t.rename.py | 11 +---- temporal/t.topology/t.topology.py | 5 +-- temporal/t.unregister/t.unregister.py | 7 +--- .../t.vect.observe.strds.py | 7 +--- .../t.vect.what.strds/t.vect.what.strds.py | 9 ++-- utils/g.html2man/ggroff.py | 5 +-- utils/mkrest.py | 5 +-- utils/ppmrotate.py | 7 +--- utils/update_version.py | 5 +-- 20 files changed, 48 insertions(+), 181 deletions(-) diff --git a/doc/python/raster_example_ctypes.py b/doc/python/raster_example_ctypes.py index 67f7a2cf4cb..54de0aab30b 100644 --- a/doc/python/raster_example_ctypes.py +++ b/doc/python/raster_example_ctypes.py @@ -40,10 +40,7 @@ sys.exit("You must be in GRASS GIS to run this program") # parse command line arguments, prompt user for a raster map name if one wasn't given -if len(sys.argv) == 2: - input = sys.argv[1] -else: - input = input("Name of raster map? ") +input = sys.argv[1] if len(sys.argv) == 2 else input("Name of raster map? ") # initialize GRASS library G_gisinit("") diff --git a/doc/python/vector_example_ctypes.py b/doc/python/vector_example_ctypes.py index ae3877224d0..27053d653c9 100644 --- a/doc/python/vector_example_ctypes.py +++ b/doc/python/vector_example_ctypes.py @@ -33,10 +33,7 @@ if "GISBASE" not in os.environ: sys.exit("You must be in GRASS GIS to run this program.") -if len(sys.argv) == 2: - input = sys.argv[1] -else: - input = input("Name of vector map? ") +input = sys.argv[1] if len(sys.argv) == 2 else input("Name of vector map? ") # initialize GRASS library G_gisinit("") diff --git a/lib/init/grass.py b/lib/init/grass.py index d2d8301c52b..505d48dadae 100755 --- a/lib/init/grass.py +++ b/lib/init/grass.py @@ -125,7 +125,7 @@ def clean_env(): write_gisrc(env_new, gisrc) -def is_debug(): +def is_debug() -> bool: """Returns True if we are in debug mode For debug messages use ``debug()``. @@ -133,13 +133,8 @@ def is_debug(): global _DEBUG if _DEBUG is not None: return _DEBUG - _DEBUG = os.getenv("GRASS_DEBUG") # translate to bool (no or empty variable means false) - if _DEBUG: - _DEBUG = True - else: - _DEBUG = False - return _DEBUG + return bool(os.getenv("GRASS_DEBUG")) def debug(msg): @@ -553,10 +548,7 @@ def write_gisrc(kv, filename, append=False): def add_mapset_to_gisrc(gisrc, grassdb, location, mapset): - if os.access(gisrc, os.R_OK): - kv = read_gisrc(gisrc) - else: - kv = {} + kv = read_gisrc(gisrc) if os.access(gisrc, os.R_OK) else {} kv["GISDBASE"] = grassdb kv["LOCATION_NAME"] = location kv["MAPSET"] = mapset @@ -564,10 +556,7 @@ def add_mapset_to_gisrc(gisrc, grassdb, location, mapset): def add_last_mapset_to_gisrc(gisrc, last_mapset_path): - if os.access(gisrc, os.R_OK): - kv = read_gisrc(gisrc) - else: - kv = {} + kv = read_gisrc(gisrc) if os.access(gisrc, os.R_OK) else {} kv["LAST_MAPSET_PATH"] = last_mapset_path write_gisrc(kv, gisrc) @@ -1333,11 +1322,8 @@ def get_shell(): sh = os.path.basename(sh) else: # If SHELL is not set, see if there is Bash and use it. - if shutil.which("bash"): - sh = "bash" - else: - # Fallback to sh if there is no Bash on path. - sh = "sh" + # Fallback to sh if there is no Bash on path. + sh = "bash" if shutil.which("bash") else "sh" # Ensure the variable is set. os.environ["SHELL"] = sh @@ -1651,11 +1637,8 @@ def sh_like_startup(location, location_name, grass_env_file, sh): else: f.write("test -r ~/.alias && . ~/.alias\n") - if os.getenv("ISISROOT"): - # GRASS GIS and ISIS blend - grass_name = "ISIS-GRASS" - else: - grass_name = "GRASS" + # GRASS GIS and ISIS blend + grass_name = "GRASS" if not os.getenv("ISISROOT") else "ISIS-GRASS" if sh == "zsh": f.write("setopt PROMPT_SUBST\n") diff --git a/man/build_html.py b/man/build_html.py index 0ef860d42f3..a8cc4c057a5 100644 --- a/man/build_html.py +++ b/man/build_html.py @@ -464,10 +464,7 @@ def write_html_cmd_overview(f): def write_html_footer(f, index_url, year=None): - if year is None: - cur_year = default_year - else: - cur_year = year + cur_year = default_year if year is None else year f.write( footer_tmpl.substitute( grass_version=grass_version, index_url=index_url, year=cur_year diff --git a/temporal/t.info/t.info.py b/temporal/t.info/t.info.py index 575446030a1..c04fe0c0a5f 100755 --- a/temporal/t.info/t.info.py +++ b/temporal/t.info/t.info.py @@ -103,11 +103,7 @@ def main(): if not system and not name: gs.fatal(_("Please specify %s=") % ("name")) - if name.find("@") >= 0: - id_ = name - else: - id_ = name + "@" + gs.gisenv()["MAPSET"] - + id_ = name if name.find("@") >= 0 else name + "@" + gs.gisenv()["MAPSET"] dataset = tgis.dataset_factory(type_, id_) if not dataset.is_in_db(dbif): diff --git a/temporal/t.list/t.list.py b/temporal/t.list/t.list.py index 52285d04b83..c41bcae4e22 100755 --- a/temporal/t.list/t.list.py +++ b/temporal/t.list/t.list.py @@ -123,10 +123,7 @@ def main(): outfile = open(outpath, "w") for ttype in temporal_type.split(","): - if ttype == "absolute": - time = "absolute time" - else: - time = "relative time" + time = "absolute time" if ttype == "absolute" else "relative time" stds_list = tgis.get_dataset_list(type, ttype, columns, where, order, dbif=dbif) diff --git a/temporal/t.rast.accdetect/t.rast.accdetect.py b/temporal/t.rast.accdetect/t.rast.accdetect.py index 1588b44d4b9..17433e06f3f 100644 --- a/temporal/t.rast.accdetect/t.rast.accdetect.py +++ b/temporal/t.rast.accdetect/t.rast.accdetect.py @@ -173,11 +173,7 @@ def main(): mapset = tgis.get_current_mapset() - if input.find("@") >= 0: - id = input - else: - id = input + "@" + mapset - + id = input if input.find("@") >= 0 else input + "@" + mapset input_strds = tgis.SpaceTimeRasterDataset(id) if not input_strds.is_in_db(): @@ -261,10 +257,7 @@ def main(): # The minimum threshold space time raster dataset minimum_strds = None if minimum: - if minimum.find("@") >= 0: - minimum_id = minimum - else: - minimum_id = minimum + "@" + mapset + minimum_id = minimum if minimum.find("@") >= 0 else minimum + "@" + mapset minimum_strds = tgis.SpaceTimeRasterDataset(minimum_id) if not minimum_strds.is_in_db(): @@ -282,10 +275,7 @@ def main(): # The maximum threshold space time raster dataset maximum_strds = None if maximum: - if maximum.find("@") >= 0: - maximum_id = maximum - else: - maximum_id = maximum + "@" + mapset + maximum_id = maximum if maximum.find("@") >= 0 else maximum + "@" + mapset maximum_strds = tgis.SpaceTimeRasterDataset(maximum_id) if not maximum_strds.is_in_db(): @@ -304,16 +294,10 @@ def main(): if input_strds.is_time_absolute(): start = tgis.string_to_datetime(start) - if stop: - stop = tgis.string_to_datetime(stop) - else: - stop = input_strds_end + stop = tgis.string_to_datetime(stop) if stop else input_strds_end else: start = int(start) - if stop: - stop = int(stop) - else: - stop = input_strds_end + stop = int(stop) if stop else input_strds_end if input_strds.is_time_absolute(): end = tgis.increment_datetime_by_string(start, cycle) @@ -365,10 +349,7 @@ def main(): if indicator: num_maps = len(input_maps) for i in range(num_maps): - if reverse: - map = input_maps[num_maps - i - 1] - else: - map = input_maps[i] + map = input_maps[num_maps - i - 1] if reverse else input_maps[i] if ( input_strds.get_temporal_type() == "absolute" @@ -637,19 +618,13 @@ def compute_occurrence( # Aggregate num_maps = len(input_maps) for i in range(num_maps): - if reverse: - map = input_maps[num_maps - i - 1] - else: - map = input_maps[i] + map = input_maps[num_maps - i - 1] if reverse else input_maps[i] # Compute the days since start input_start, input_end = map.get_temporal_extent_as_tuple() td = input_start - start - if map.is_time_absolute(): - days = tgis.time_delta_to_relative_time(td) - else: - days = td + days = tgis.time_delta_to_relative_time(td) if map.is_time_absolute() else td if input_strds.get_temporal_type() == "absolute" and tsuffix == "gran": suffix = tgis.create_suffix_from_datetime( diff --git a/temporal/t.rast.accumulate/t.rast.accumulate.py b/temporal/t.rast.accumulate/t.rast.accumulate.py index 6fe067e02bc..d254d21c110 100644 --- a/temporal/t.rast.accumulate/t.rast.accumulate.py +++ b/temporal/t.rast.accumulate/t.rast.accumulate.py @@ -192,11 +192,7 @@ def main(): mapset = tgis.get_current_mapset() - if input.find("@") >= 0: - id = input - else: - id = input + "@" + mapset - + id = input if input.find("@") >= 0 else input + "@" + mapset input_strds = tgis.SpaceTimeRasterDataset(id) if not input_strds.is_in_db(): @@ -205,10 +201,7 @@ def main(): input_strds.select(dbif) - if output.find("@") >= 0: - out_id = output - else: - out_id = output + "@" + mapset + out_id = output if output.find("@") >= 0 else output + "@" + mapset # The output space time raster dataset output_strds = tgis.SpaceTimeRasterDataset(out_id) @@ -244,11 +237,7 @@ def main(): # The lower threshold space time raster dataset if lower: - - if lower.find("@") >= 0: - lower_id = lower - else: - lower_id = lower + "@" + mapset + lower_id = lower if lower.find("@") >= 0 else lower + "@" + mapset lower_strds = tgis.SpaceTimeRasterDataset(lower_id) if not lower_strds.is_in_db(): @@ -271,11 +260,7 @@ def main(): _("The upper option works only in conjunction with the lower option") ) - if upper.find("@") >= 0: - upper_id = upper - else: - upper_id = upper + "@" + mapset - + upper_id = upper if upper.find("@") >= 0 else upper + "@" + mapset upper_strds = tgis.SpaceTimeRasterDataset(upper_id) if not upper_strds.is_in_db(): dbif.close() @@ -293,17 +278,11 @@ def main(): if input_strds.is_time_absolute(): start = tgis.string_to_datetime(start) - if stop: - stop = tgis.string_to_datetime(stop) - else: - stop = input_strds_end + stop = tgis.string_to_datetime(stop) if stop else input_strds_end start = tgis.adjust_datetime_to_granularity(start, granularity) else: start = int(start) - if stop: - stop = int(stop) - else: - stop = input_strds_end + stop = int(stop) if stop else input_strds_end if input_strds.is_time_absolute(): end = tgis.increment_datetime_by_string(start, cycle) @@ -371,10 +350,7 @@ def main(): num_maps = len(gran_list) for i in range(num_maps): - if reverse: - map = gran_list[num_maps - i - 1] - else: - map = gran_list[i] + map = gran_list[num_maps - i - 1] if reverse else gran_list[i] # Select input maps based on temporal topology relations input_maps = [] if map.get_equal(): diff --git a/temporal/t.rast.aggregate/t.rast.aggregate.py b/temporal/t.rast.aggregate/t.rast.aggregate.py index 609d6d19d38..0675407794a 100755 --- a/temporal/t.rast.aggregate/t.rast.aggregate.py +++ b/temporal/t.rast.aggregate/t.rast.aggregate.py @@ -218,10 +218,7 @@ def main(): dbif, gs.overwrite(), ) - if register_null: - register_null = False - else: - register_null = True + register_null = not register_null tgis.register_map_object_list( "rast", diff --git a/temporal/t.rast.to.rast3/t.rast.to.rast3.py b/temporal/t.rast.to.rast3/t.rast.to.rast3.py index 7776d7426ee..118e19549c6 100755 --- a/temporal/t.rast.to.rast3/t.rast.to.rast3.py +++ b/temporal/t.rast.to.rast3/t.rast.to.rast3.py @@ -183,10 +183,7 @@ def main(): gs.warning(_("%s failed to set units.") % "r3.support") # Register the space time voxel cube in the temporal GIS - if output.find("@") >= 0: - id = output - else: - id = output + "@" + mapset + id = output if output.find("@") >= 0 else output + "@" + mapset start, end = sp.get_temporal_extent_as_tuple() r3ds = tgis.Raster3DDataset(id) diff --git a/temporal/t.rast.what/t.rast.what.py b/temporal/t.rast.what/t.rast.what.py index b3cec73a538..4d13c6a924c 100755 --- a/temporal/t.rast.what/t.rast.what.py +++ b/temporal/t.rast.what/t.rast.what.py @@ -410,10 +410,7 @@ def one_point_per_row_output( for i in range(len(values)): start, end = map_list[i].get_temporal_extent_as_tuple() - if vcat: - cat_str = "{ca}{sep}".format(ca=cat, sep=separator) - else: - cat_str = "" + cat_str = "{ca}{sep}".format(ca=cat, sep=separator) if vcat else "" if site_input: coor_string = ( "%(x)10.10f%(sep)s%(y)10.10f%(sep)s%(site_name)s%(sep)s" @@ -480,10 +477,7 @@ def one_point_per_col_output( out_str = "start%(sep)send" % ({"sep": separator}) # Define different separator for coordinates and sites - if separator == ",": - coor_sep = ";" - else: - coor_sep = "," + coor_sep = ";" if separator == "," else "," for row in matrix: if vcat: @@ -517,10 +511,7 @@ def one_point_per_col_output( first = False - if vcat: - ncol = 4 - else: - ncol = 3 + ncol = 4 if vcat else 3 for col in range(num_cols - ncol): start, end = output_time_list[count][col].get_temporal_extent_as_tuple() time_string = "%(start)s%(sep)s%(end)s" % ( @@ -567,10 +558,7 @@ def one_point_per_timerow_output( if write_header: if first is True: - if vcat: - header = "cat{sep}".format(sep=separator) - else: - header = "" + header = "cat{sep}".format(sep=separator) if vcat else "" if site_input: header += "x%(sep)sy%(sep)ssite" % ({"sep": separator}) else: diff --git a/temporal/t.rename/t.rename.py b/temporal/t.rename/t.rename.py index a6aee79b790..a1f1a164fee 100755 --- a/temporal/t.rename/t.rename.py +++ b/temporal/t.rename/t.rename.py @@ -59,15 +59,8 @@ def main(): # Get the current mapset to create the id of the space time dataset mapset = gs.gisenv()["MAPSET"] - if input.find("@") >= 0: - old_id = input - else: - old_id = input + "@" + mapset - - if output.find("@") >= 0: - new_id = output - else: - new_id = output + "@" + mapset + old_id = input if input.find("@") >= 0 else input + "@" + mapset + new_id = output if output.find("@") >= 0 else output + "@" + mapset # Do not overwrite yourself if new_id == old_id: diff --git a/temporal/t.topology/t.topology.py b/temporal/t.topology/t.topology.py index 4356596f319..e9da9b369a7 100755 --- a/temporal/t.topology/t.topology.py +++ b/temporal/t.topology/t.topology.py @@ -74,10 +74,7 @@ def main(): spatial = None if spatio_temporal_relations: - if sp.get_type() == "strds": - spatial = "2D" - else: - spatial = "3D" + spatial = "2D" if sp.get_type() == "strds" else "3D" if temporal_relations or spatio_temporal_relations: sp.print_spatio_temporal_relationships(maps=maps, spatial=spatial) diff --git a/temporal/t.unregister/t.unregister.py b/temporal/t.unregister/t.unregister.py index b39b7b9819f..cacfe579a7b 100755 --- a/temporal/t.unregister/t.unregister.py +++ b/temporal/t.unregister/t.unregister.py @@ -93,12 +93,7 @@ def main(): # Map names as comma separated string if maps is not None and maps != "": - if maps.find(",") == -1: - maplist = [ - maps, - ] - else: - maplist = maps.split(",") + maplist = [maps] if maps.find(",") == -1 else maps.split(",") # Build the maplist for count in range(len(maplist)): diff --git a/temporal/t.vect.observe.strds/t.vect.observe.strds.py b/temporal/t.vect.observe.strds/t.vect.observe.strds.py index 9c60a3d026c..547f60a3250 100755 --- a/temporal/t.vect.observe.strds/t.vect.observe.strds.py +++ b/temporal/t.vect.observe.strds/t.vect.observe.strds.py @@ -202,11 +202,8 @@ def main(): vector_db = gs.vector.vector_db(input) # We copy the vector table and create the new layers - if vector_db: - # Use the first layer to copy the categories from - layers = "1," - else: - layers = "" + # If vector_db, use the first layer to copy the categories from + layers = "1," if vector_db else "" first = True for layer in range(num_samples): layer += 1 diff --git a/temporal/t.vect.what.strds/t.vect.what.strds.py b/temporal/t.vect.what.strds/t.vect.what.strds.py index 6ea36e79579..b1f2c17a9c6 100755 --- a/temporal/t.vect.what.strds/t.vect.what.strds.py +++ b/temporal/t.vect.what.strds/t.vect.what.strds.py @@ -158,12 +158,9 @@ def main(): raster_maps = (new_map.get_id(),) for rastermap in raster_maps: - if column: - col_name = column - else: - # Create a new column with the SQL compliant - # name of the sampled raster map - col_name = rastermap.split("@")[0].replace(".", "_") + # Create a new column with the SQL compliant + # name of the sampled raster map if not column + col_name = column or rastermap.split("@")[0].replace(".", "_") coltype = "DOUBLE PRECISION" # Get raster type diff --git a/utils/g.html2man/ggroff.py b/utils/g.html2man/ggroff.py index e62c5ec2b1c..a0458dd0334 100644 --- a/utils/g.html2man/ggroff.py +++ b/utils/g.html2man/ggroff.py @@ -119,10 +119,7 @@ def fmt(self, format, content, var=None): self.show(pre) if sep != "": if var: - if var == "index": - val = self.get("index") + [0] - else: - val = True + val = self.get("index") + [0] if var == "index" else True self.pp_with(content, var, val) else: self.pp(content) diff --git a/utils/mkrest.py b/utils/mkrest.py index 24481bc3374..8d156e0bcc7 100755 --- a/utils/mkrest.py +++ b/utils/mkrest.py @@ -21,10 +21,7 @@ from datetime import datetime pgm = sys.argv[1] -if len(sys.argv) > 1: - year = sys.argv[2] -else: - year = str(datetime.now().year) +year = sys.argv[2] if len(sys.argv) > 1 else str(datetime.now().year) src_file = "%s.html" % pgm tmp_file = "%s.tmp.txt" % pgm diff --git a/utils/ppmrotate.py b/utils/ppmrotate.py index ec041e0f253..cef32eba0f4 100755 --- a/utils/ppmrotate.py +++ b/utils/ppmrotate.py @@ -110,11 +110,8 @@ def convert_and_rotate(src, dst, flip=False): to_png = False if dst.lower().endswith(".png"): to_png = True - if to_png: - tmp_img = gs.tempfile() + ".ppm" - # TODO: clean up the file - else: - tmp_img = dst + # TODO: clean up the file + tmp_img = gs.tempfile() + ".ppm" if to_png else dst write_ppm(tmp_img, ppm) if to_png: ppmtopng(dst, tmp_img) diff --git a/utils/update_version.py b/utils/update_version.py index c66a34a62cd..947e68a1648 100755 --- a/utils/update_version.py +++ b/utils/update_version.py @@ -226,10 +226,7 @@ def status(args): version_info = read_version_file() today = datetime.date.today().isoformat() version = construct_version(version_info) - if not version_info.micro.endswith("dev"): - tag = version - else: - tag = None + tag = version if not version_info.micro.endswith("dev") else None if args.bash: status_as_bash(version_info=version_info, today=today, version=version, tag=tag) else: From 5d4f7fb6dc36a05b0b02d0d301756b3b5033531d Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Mon, 21 Oct 2024 08:32:27 -0400 Subject: [PATCH 422/471] v.hull: fix null pointer dereference issues (#4524) --- vector/v.hull/chull.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/vector/v.hull/chull.c b/vector/v.hull/chull.c index 93dfd15742e..cd8dcf52153 100644 --- a/vector/v.hull/chull.c +++ b/vector/v.hull/chull.c @@ -687,6 +687,8 @@ void CleanEdges(void) e = edges; DELETE(edges, e); } + if (!edges) + return; e = edges->next; do { if (e->delete) { @@ -711,6 +713,8 @@ void CleanFaces(void) f = faces; DELETE(faces, f); } + if (!faces) + return; f = faces->next; do { if (f->visible) { @@ -746,6 +750,8 @@ void CleanVertices(void) v = vertices; DELETE(vertices, v); } + if (!vertices) + return; v = vertices->next; do { if (v->mark && !v->onhull) { From 24b398a38159a88e2b4b41241cd578b3fee15052 Mon Sep 17 00:00:00 2001 From: Anna Petrasova Date: Mon, 21 Oct 2024 09:48:43 -0400 Subject: [PATCH 423/471] r.sim.water: add tests (#4551) --- .../testsuite/data/depth_complex.pack | Bin 0 -> 36093 bytes .../testsuite/data/depth_default.pack | Bin 0 -> 25906 bytes .../testsuite/data/discharge_complex.pack | Bin 0 -> 38375 bytes .../testsuite/data/discharge_default.pack | Bin 0 -> 38180 bytes .../r.sim.water/testsuite/test_r_sim_water.py | 201 ++++++++++++++++++ 5 files changed, 201 insertions(+) create mode 100644 raster/r.sim/r.sim.water/testsuite/data/depth_complex.pack create mode 100644 raster/r.sim/r.sim.water/testsuite/data/depth_default.pack create mode 100644 raster/r.sim/r.sim.water/testsuite/data/discharge_complex.pack create mode 100644 raster/r.sim/r.sim.water/testsuite/data/discharge_default.pack create mode 100644 raster/r.sim/r.sim.water/testsuite/test_r_sim_water.py diff --git a/raster/r.sim/r.sim.water/testsuite/data/depth_complex.pack b/raster/r.sim/r.sim.water/testsuite/data/depth_complex.pack new file mode 100644 index 0000000000000000000000000000000000000000..60f9de2be64c74c7d2c2dc1d40a8b0385bd0810a GIT binary patch literal 36093 zcmV)pK%2iGiwFp@%MND(|72xwbZB2=Z*6dFWq2-dVPk6m?7azCjcwQe-#k*Os7R$j zgyw0lwXQ^(MW%#MDn%Mpl91*}hC(Ts8pte4C^U#sse`t@B*#qOGH?WA5k{YUAnV;Tg#DZyCD%_;642_J9``*{n zH_$WTQ8D@}{ol2`}P9cNkpd;H(n z*zmue|Md+1Vg5HT);Hu)(fup^|8F|~djnq@eB-BvGDg>>L2Lqwu1d?nY&m(p#O4E zS>f(3=HV8+$`@4p+&oMT#N0r|*ucb8&%i`mSIjdARCKl37G@b5nCkY;A*{Z1-_%5G zgt^d_GFz)kzLw&=6`rm>1e_;Oqzcl~r_RaqT`+tAa`G4tRP7O%# zUl;!y8~wxlZ)9SkI}rc>6(e{}fG4OdUE%K*EVg7#u;*g`z&;%Sl~9jBu@zyf7rXoW z1_XKr1ugUUTfBs`Jvd=tLvTR<2lW4cwf>v->Hom`-``gMSFUpN3;v(=|9`3f1_uAA z|Hk?Q`+tAQ&;>p1KI7=>^|`)*KFhRkE;cqcg}%23+H{6qnjuB9}H?&f#(im;PL?<1&%UG%jc z_5Z(f{@>>i|Ff;Y|NZ*^Kkfe;4DA2^HN$+-*v(D1Qh3aDON>q?E{o=i(HL-w0sSA) z|G%~V5A=WijrE^>JM=k)Z@^!s|9bzZ|HgX81O2~$$xSApcc=ddqq zrT_jb{!Q-x-|D}iuKqti|8t=J?=Kk|D!MBF)B$KBrVIK;Mx2L#>iuCP6GN7Xp^=^e z`wZtlU9tb>Jp1!T+|zxhbd5}PjY0R1^J4$X?#H}|se%DXa4gCaeKLYOWC&R6$H>>QY`Rsm9 zYVD^*NIxyP{Q_1y`)R|y&bH_KX>Z@psoZ{uX}-)J(qMysT{SqsWJNU$K+tGN9Pt2O%>%B{x_v+d{FrhcyH_G^TH?`H(J-ylkwes1ITJ8Xa5&$PAu+&QS98N2(r zi`&nUb&1P5y)i{PKU}x+^xdF+_X01^My)bF&P8F}xIG#6Y%9Q#W5nR{#!@(WVH0G( zWr1rE3*rqmV9O#C*id~1hR)Q3Fn;@j@Wv~0`!PB40lb`6f!;kM zp?HN5+(_Vo&GS7VYV%~+Yg!Cvj{uZD=|D02hd9*k6B_2vLuNx7&TTQm`HIibGiM@h zFP@A$N95w}L$7dK-F4iq){f;%6;Lgs2p-toguJdmaBIy4qxr{J-EmPn<9K1Y1ga(?#)z!M z7gI&i?SdOdR~2Et+Btj}Yl-g*yGVW?r@^D&AP;ZthfUuyk;haW_J(Cb)al0%BPb6$ zTv(94@FDmuEr%rsZb72tO}I1U9o%+23hiRQaG1q0+!f@2dwula=E)u?o+gBjDfeKP zR1s`m-wIo1`h#Pn2FECeGSr}^B^){@yhT~byC|b(iehso!?$xmIO44sPU;pzgJqFu zBIt;A_uO&1^?tlnoQ@%j+@a=@JXn?hJlkb}ykT!a_ii?v>FR<@RzILRUmx~*93TOi zb;MIDh2@Si*!%4eiMCge*wHW>)bV)Uf zQXkgRJkOn^ms>~{JFk)vE1is8J`jIM42jpJk=)M+ygu~+X8vqJ$Dx7P!uJK6=C7pK zv8%95;3b~m-AX(k2hkYWcw%|B|29afCqU5>BPcj61-k`rfMnq$SU$rYf_iVl=B2>O zwy%K2UN0e6>L?^+`@qplQ{cnLb7%rDF?3BhyztrtMLKnmB%z6_!I$82_et2k*bi2h z)xq}KHIVt@6BL#`f(vm=;qD_tIQNPN4$G~8mqwB(uC4$*-SsF`G6{uOyg;ZW^s<0M+T(Bk$rK8w$_b2u)lf|SRa-@(iN)sp5 zk!Z4yCvCFbF8gH*exz;2tVurJ#fvY2L;t6CfD!GTs7!tV;6tcNgYk0nHi z#enSUxe%f<6%NiC2T#sFLD8KrP`dUtY~AGti+{km*(~UFDMBG@S>$~#k2Br+rkkIS z(74kb+Qvyj`M3@6u{IJ_+e^*Tm-I}W{cDQZW|>nMXt4&Tep5x6g`3E7+%<~&;6Zs= zx^ztb0_8k*r&Zz;NpoxtDK=gq9HCDkCk<$_%S-rwUXu4{c~=C5 z^35j8b^#LHR!i~~#iX?QCdW7)4bn8-zmmooe4~*KE+mu}i*?}&#B2MErtOI*t(8lN zH7A5TG7O2;yzxH_0?zA9Inxqr<|&ATa8O9rgAl(&&Q!K=WH5|%7Ubwh=;S>d-6&mu z3$nGw!Tpcg(7kdugvl<0#A9CIS&<0m*RO%XC+a}^Zi8)xH0%yegHyX>q3&lf+`l;u z-pG~0=Yu=omZbpPG!2F);z{r*M;PAeKZ72nTC_0fMTfJ!sPLc~E}9EMTEiUjO!+~d zr|+fDD{q-C6i=OKEEj9$I^`Em4ts~1R!>pIXDjXB!%q(u@20&g=Ft2|1sdhgOUj`y zNYQmKjh)?hA;oe@x3hpuPf3%;WnL1qy-IxIx@1%JisHsg&`OtHauIk&(iNXb$80F} zp3B4Q{tcw7T0k1f^)xAV7|oTrPK)$n$UKoD$E;*p+Eqje+keq=-3;=a=}gns@1>bj zKaj_`r!=iZj?C%;X^3q#X=5f{N&P}Y1}9*IP&z!qa!J&pL0B-&p0rAi7^<41%ifq9r#$Bh9rG=*jMod zF5UVHk)M-bInNsKUGNaXU*tk~x&~;qM{+*9Y_KgH0t=|nsf7o*v!MIYQ{;CDgtvqI z;NI#puxGdj99(zD)%l--YXFokZHxYFS@*<>5=hQwan!6&U9_s%$7*es>-ZJ8vQBaC14c4DjH zc{0fICPn{OWK?BKikpU!R?AA#sF_dV8KX%hK!7IS$fwEi_T)BcH%axj;g?|zG+pf? z=}Xlne zfukEup(MT&9+|d4P24A_eRvuwq(4C8qjso&HV+Q3`pS-upkPl9+&{D&+FgC2ge4Ct zPMbmJZ3=vAsHRV;{#YsS1wyh9!JLjRxc+oC%7hi5?@kR|TX+_yTsn&J@3)iO4LwHP zN0X+E<0YlJl{EZg7pc@2lG@{OWY?BNvm#tcul6&ImmEo&7e#6Gv8SXmYX-L5uEsX` zqQ0M9NXBE=lJVO46d;j8RneJ@{N5afS7tR89$Y{HFY;+{Z4kZ`noj(KW%0}1pENl5 z75=)+i;V|{W0Cnu8lpS|?};tJ`{f%*tuJ_3eZYZS(^rz3&^A(9nMo#fE+l9vNW#jY zxb$i=@qJYzm9=)1yr`4rTgZ}gL)>oRDZpvc@P zXcIZk5%s8%77!%i2(gt`oZN&R&d&4~g1tY-g56USPz}<7#O52Y&u1G{Y~_XK&dKok zPB;$ET8=XQmr*k}702H+MuU4YC~f@#6*rsV$i~|^+2$k8S3ZFY&Q3+Mg^F10phzDb zy=h^666ALJ!jg)GkiYajyuTTT6Wp)h67x0;Tm1vI(>!s+s$KZ~{4+CSD|s^^fe~~j z`8Zhw22=1iK{69_qP2cCaG1ra^-fEKKi?yWv%z`kwlN5RE@zbe~*vLBvbLR%)@kKePI)R6H zwmA^=>XTts3LQ%jrR?c8GFzm205}a2zp8;L(Gq06wETlx$}-9%mE!JIBY^uh)E6F4YZ9H%%fM}s%s zs9^CNMYYDjv5Hox{~m!FsWRlHH=W)afSJyYcC&FyHJNc!Pt&8zV;SD6Uv%GkIHerw zpiPClX@R0VX;>wZc-mLScpJ=JevpiY{$wuPEn}p^t0{5wQ$i<6 z!kH5I=|c^6~uAdV$oDAY3% z1+w$ufI$+J6-VNdeMfQL!e?mU-wHo|?nIuFHk59D0#^g>ax(ThX=iAfmj#is88Gfd z17yA*4Z){oLdvB?IL&VaO(SfeJ~$Vi#7&1DO-bYx&O~w1G~^GihF@n5;HOy^yqa5# zqHju3=IJ)PXuOv$E6Cuoj$2TxVTdoJvyj!fB9G2=Pr<#>Up`_;}}G?1(R+<>f1={b?w@wAev*4>d@&=@V&I z9%WBiSpF%A#6Hf){F~KS$#cTA!{Ri4Q1!vv3-w9a<0rW*oBcLOUucR7SzB=Uo)V~h zH3@dL)S$tjN{l@pi&JM?;jj~7sP26n1)G+^gXbSmqA?jo`8%M}fPqybL_j+>3(Ahw zgQlVvh}(o{i{$FodO^Ij=-0j<;e5?4DuzLqoT%Glv3_S(Z#vA z#UYq357xpD!#}~U{cX_tc^wKH2*asFEoh##ALY$;aMZq&$mdbQj?UoOl@hpku?z)| zRKX2HYvg~INLjoG>CD`Dl(or_E-vh4@~jRqzarl=8Y^q)RZ}S=8ZXTlh&*IO`=*H~ z2UnWq5J_5VCgJ_PhcTu^57kdx#k7zwOj^4Rb9Ln&I$5@yqIY_emz5q(pKDK6b(J(` zvkggKa>9l?efm>YPXhRwq)bQQ*Gor9eYiZG%nfCfx>PAm#-B8=<&p&}fwbhxNa&O< z4K{Mdhn6jPNI3$Zy4*pp+B}>bh8nO)qsOecW^RvzRrhGZ88#4zfK2ri3T{IGLfS%G1E&p z>t7jzhQgiPX86=pfr9tqagh2ulr4`#iL(+Yen|nkPA74~UFr6Ea+Xu4joTZcEmav( z?fbloXEVzE`T}=arEvI-bd>MZ!NFnY;qmDUa5tg?j_$}tKE3Ji{Q6qtn=l_`*GN*f z_+iS5)uM=|U6g%OpHUgT-b|^e+{|t72S%^#AzcW%&&+WSWrj?el1YbX@wQ;nskA4(36Jq^-UF;3r$|C6AF!@T0?!Pc ziATh4p;KilZknD6ar(yiI=7sxht2zK5I*${XwwAfpQg*X(8UM8!|m#YXq}Xd7mMa% zK*B7Xzexlqbge-#I)D=MU&HIi+aM<<8dgQ`W0jlVfo#`haGX~H`6X7uo7ZRI%Jf!v zD(!_5TjX&h5{~|&hRO~KIKFH+vh18u*XS5Zo}GpsGXv>(g)th7pNCx$E8yO-3(z*i z3Wu(bhT{hk;k)~E6qoJdL|NyyRKcb)D>%~F1vidq!;je8@Xq2Gj!N^UWivv^&iFmW z3_DMs<<2uhYV?`WTT9L6D%>=ieQ*;a;B$phv23DuO=l@LweI-&SsjaX+v1}jKq4Hv9xCu@mtQtvzptntM?{0jI6;kcY`t0 z?mq6T48sM{gd<(t$fhXZcOFs_ZUIFRK6oPA@m;ilpqIDfnqC0A`DfrBeC-)boa zw{1oyRR+gO@gc82AH16#3g@24gPoKfxTrjZ^Fce{*4#+QzEKXX&%Ptit5VLqKg41l zj*(o8Q)IP~g80zf#RY929z$)qhm$4eQpiVb+MGQV%Oce@$hgN9~8 z5KqN(?8$?|$2~N5R~2dSMpNGXUgco1^2R~v3ccMY&%zq8G1*E|LIHIoLG-t+KVxA z)H&jp{`lJ8zfQ$up>;UYq6hgx!%^BTg$5mO$HKM)m>eyP4yG~e#1XnR zZJ?@Ai?bTczF;$FNysS~#6u7h)H-@>(sFivh0 zyP}qp?~L)C48J66ahSCgY?PZ0-km^`nj#pHR9k8=x=sr_6KT$>QgZ8fNZ$U#$w!lq z*{T=HT)vgWNV#V-Li;~4v1bNTqv8@gw=&b;wli_B<;ZExaeP^`8Q(v# zAhCz9NoYYiemWU}fyo6}XWfm>H|zYWrJ zqy(~Te!#bYtytL?(s{XvprCIw>h<)c{%arN8Ie8 zlM9R~#O)KZtWDwLFJ|S%E^(Sp*=eI+**yKnl{p0-xzY9J%hHr-9b;Z z^_kDFo|vt(E;o}4h&GcQ+hsQX#X?4N?|U--x`T=0v1Z;cNTHHcKhGw(KynA%Nz=T%91vnyEy4Q9r@dCZi}Q=)GVwo&|uAC%CmM2pov z600(vY(yB69MXveWqsLwej({}ttWG_pESc?n517CkiGdOth#&%ze*g(C&Rw~))0-3 zGmw!W3XQF`IJ5meR%*MU^w-I_hbNxKEPsi26ego<9-^x9Ea=j?M zbB#i;rF9q)CxtdfN${|4Fh}Uwi;b%e--V4YrbCLrWyoB!86K-1!9h8EXrSam!#}u^ zsP}2OeMt*$u5p1A#b=>>$6-hs?Fb7NyoB{~6%dgo4d)h(1_53X&SItAj~6gc%m6E# ze$m`dELPp|RPg;Shf)vUpa%2MZ2PCWk3bB(+oxjoM$r%R`p6gR-8tOF3gaxrMc*1`Uz5fJSk zj;;=4@N%FY_V(<CF^TuzGrMJz8G|eN6!|fl#!J1T*%_HMnHfw{)~&=Z ze~XlR2a}`WamHogE3*KXKr;c;E0n(8k2c{~%IIB28y!?B-t{cGw|$`vdtOn5CyVUu zddPg^U;-6y((o3=JqM&gg~kAI4?vLvAL$q8tGYlQsWJ}C0$ zGb!?4p@V6C5mK`q%4|)-A%^==yyYHz6>5a{Rb!z{bUfsiUV^2Oi4b}B8eC}Cgu^~z z7(6@#zt3u+p#{>|S*L)91q1P_ctl$exV#dayF8JkJ!`5*CPW)(hNR*Mg3 zeWPhfqNM%g0LiiT5)??#g6ErQg4TN4P+0KWAf!GNibwoHm2vgZkY)f)j%Vm}qz_$I zX3$2_7VRT>&@fdNMUFgyTF(hk>BtMa&wPgUEj}=7s~TjmbiuA70er^Xh8P1=uO`0P@ETLX#$syvBHCXXO9_WdAgIn4RJMe245Ij?;1920 zPe-gfLjyrpGy~jqV_@Sf502+7mR*dK=BeYvM@k?wVGWoR7qY`)ayc%`1Sz_i@%~t9 zHuCLMGu_?e%^>EknPbErGZ%M%Gv)Dm%&FV2ne`GLjO=|inw8j!O^}Cy#-3;pJp`Xt znBr--Q8dJ?0pG+fpj%_g87*52`qlD`4xetInZ0Al;p1MKxIU7~biOd9n@%xlKC(1M z%@^yR<&aqXR}xhjiI1N(V~{rw)<)mM6G|pDQl*w;g3?I3=^4g9-a{JJ&*;F3os?D5 zK+C+tC}WiqYZX8@LSp^4dS~wTq_02(WKhMv(aD8_#RAwDP{+v$ay}SS#r;UNV5pr#b6%(9TrEI07#~R>8g`p zVYV5jI7&m9HVcg$QpsGegVvWjQQir8%30({`$rF@f*>oi3Ci!xCK)B0jgu-e8|m`I zOjLLzb1nKM6FRJjUIgwSyCzF=v2|wTE#I0MsiiS1A4Q=0xwo7ZiljzE^q%z)qt3OV z-X%4hn|U5IHYp4qY;VA}&P&)DV~7v0=V0Ub zDlAs&!l>u%SUUa@@eBFk>%E0E?aXDGwDTLb)qg>Aslga=V+F19lxHMPexp0SeO}od zPCFcT(RtOgzYU^dCjl?#&cYSBe5iSAKHNN5Kn0G|NTs(0M-6|2)>dL@lJ^;jI%>*mib>31nIig9BzJ5LVp@bA*B*_3c^YO}YcEF55VFG<%)vSYk8$)NV#T(PFrN z>?KZ^a0`dq_M)!eWV}afNg!@0)`rU92bD_V9TZ7}(wb;EiILo3UDBAELgqH_XhxGE z&E4)ws}-zB?Rhc@4!0%CO-Lph1vKKUA@(N4F+4M+X@S-8qeyZNKM8F*)0dCcW8Rh}7`L+(&k5Z`qoqBlcF+Np9k<}xA+9)8ISP~Czs4>0 zBT3{;3JK+@k@WK>I%0H>W)3IpPa! zvh_uaFmGISNDYkOBzU~M1UVlT-~&dDc#Rg4;KD^DQ1A*LKQYHTcM-g?(+?wG?18VL zA8^CZlcd9%!~|4}G1JG6V+1On(|x`h6j8VWpFhdL{WS`>e@Pe?Fm^cI?GkQ#uozRb zr{kf^=kdzRootWBd?;p;e?NfXAj;ncZCC}%YU1sZ~&>+(?ck~-{5w`DJzz_8##II%ni zCr&6vv+pM$T3!g;y*5IIY7U%v_YqniTA}b%Mb!DCkKUhx;k|1)%;~iRK2)Xmcj|ES`ufrhNcaZAZ`x zmWFM+X5!rzUlR9k$0vt+v8$WGE^#4FlJ@ku1qs#|lk(pEG-ca$+I}#ci8~m_INeEO zMEEB$Cag?mw{9x4TVV~OzkDopgdM^!KI*vd!C<`bS^*EuYQT$ayNU0r1ZIbB#Uk$` zn8)*)vp{$?W)VqtsS&RING2Y}cxl)Rl5TY;`99J1n7xE9jPGEES2WN(ol>uTB{ zHa3w{+;A=NF?<^sEm{FGyK+Iz zJr}0Ui3h8p7deF%^KZT7?B%oblEozp;CZ?Su6DP_4bCc@Y8ZAcz?th$@qO)GQrhT8 zJnCmiF#IdND*aBP2hz#VEP$qbOQI=5!^v;;bGqWK$D~Z>W#o$9(stH7%6QLEzRqoC zjKnEscBL!RdOecaY?X?!vG?)k6)oKAB280PhLKnCFp{3Nl}sMVlcAsiEn8DXt|I&t z`qi1zhiZ`N$jNFY_YXRdF}aF{Bk1x z6znF~>J)1K#m~gg_G98o&3_w2sB#l5)hXfV!L>f2seiVR_-&-wtG}q$gwCsdig;S@&j_#A}Adz0a0l09767YDa4D8kR- zQW$iq3MP9jC|B1kYr$ z%(cSnLP!QJs*H_$Cv)YHHZx?HCM~wTOfq*@<8A*iJpQB_pFC*6_N|g+!FQPU3aK%M zCAXLz4p0BIr+zM-Q<>QJ7p6_V$LPl5bEq?<3r7q0MFxf^(O}jnwbq1y(yZLz$}wnW{)(&!ix%xmb&@*Ey4NTqg}OUqM6X zz9F61WXfKw%NRU*#*8X0rUU#$d7~Z3=CvEP?y$vfT0(H4n3}`7sbj}dGx2s==E21o zq*@w74#OB+ZzPCM&2)d5tpMbmtAz^l zCg92=`!G(a9+Jba0h6&CGUQw!t0fM3XX&B%FG7v+4^aK#CDb*PMA~Fe!?b6kcAh8> z8tDNyXIq2%!vn0(St78yyAp!i3OG5D?ZTCuKx1~uNw$c?$#49yogbj`yE}Wm4chOw zaQ4@SeUO6U2mz=%w3ozP+lcSdZqhKiPNFV+crU<-1V&g8@Ac=jD&QzHIis7t)IB5f z6fZKUi=l0YRY^@@EDgC*OU|m7sVF9#iltd3eN&OK_$tF(jqWD7VY2vf+6_E-YB)A6 zai?ARX3VMa1&p9u423?NLRXn`hIdyaUF{B_=mt4*NPkTyv-B9hZ5B+hxetZqiR1gw z`K0*RhID0ik@(~Y65pnY&3Zd&)R+CV@OT}q@4iN<-`nZ_gFh8pza6av(~iZ!kuiph z!nF5T?`i-$wZu6kIo~p*kS}2>G#BoImU0iyBI2FnV({r)CrVu`gGTwSsM4bjHnRB~ zfww==C+rEUpkzcO{5UldKV>N8t|qFfu@q(+MagT8a9T zAEK18Iu2@FhddDy$a_2yZdkRznPayhRq-Tymk>4Y2*DH?S8!%0dUg$*I^Nocr;#_I z9%USF;OM!$s1tqyr_6sxS|xW$FnJx6bobry{8QlGrHhbtOc43C1mIK0SUBYO1ABWvDee^`Eyf}4ERZ>H&oGq zfRD7}O&8@>9isA*Z}ek+4u$y|6L0GSQr}!g;B}u|gSu$_hRc-jas;W*vBMXtOH1%V>CGDDgxeBBp2z#rH0xF&{-q z&U6d@n%hiXPBp&`!mdCx-(5^U3MbI9wZ~9L=_4w}o1rvyqwq9Q6v-EZ`|T!h*)tLL zpE?W~_pZWz0e`stb_^7He!%ewE|A`I4up?(u=ge)=%^^{Jg^3?2W&_2{FTVc@kjGn zM#yS5B(ch2r1f+jw4@RA=r|y6b~cK?af42QB~Y-cime+^Ah{kA1MY+8#ujjx+RF-A zd;sRGv4P`5FT=NlW=;`CpSZ)7UzV_D+zlu>F&BU4g_BKe1tt32p?H@^v{6!o;!}F5 zP$QGdBSi_EWk~6@IB6F|6HYr!YVYRI^nk(S_tS|)D~qv1;1G?ORYr^6E~3n~bP86A zWW28yGfigS$U@%<1Ggt&i2GI&N%vr)UPv?VpBFM_A%?WF{VW}9pT|ggb~3sXe=&kK zdbE@O33<-AMSel{bn5CGI<5bb7FgPooX}_dvgSM))%nrIy`yOJy$W)glt+B;w$Wgp zy}u2@j{RdRtr$iBNK%@YkFxF#@V!(EZXM2pX3ZG59sdEI^1OtSaCvALTMDfuR`4?E z6sJ`5!eR?l++hZHLV96U@iR_=1AF)Iy!LE(cti_c9j!+tr*9aiFdH`=t;E9}!)dwi zWgI0m10{vVqiW4yXbx$Iqx^-iAKpXtQgcp!NzthbFkSUHjHoh# zGt@bI>lI~#IRVB_5lMKiqe222mH5i(E}5*IL-EefX`E#qEt~e95<05rh@TaiY8<6u z`HD2fH3Ylt9BF7(2+fvmqSc#uC}!nBG9K}rCcbT>Sd%o`uK101yQk7-ux8S(j${@+ zJBlVsiRdbS7azDVWGZ`-iT}Qhx&7Lk;c|ox{nNPmk2?4SySCyNxFuwn$;* zGXyvV2p87vffT!^P{ZGV!ZTWNxa>#H#Xl#%7p~fUhhHak;nH(gPLXv;=@}d{Hl0&C z%8tHHKd|6TM>lU=fJb)e$SaeKZq?41$vX#&lCtrh{R!GQt%OG0@j)wBa}@DQfb!c> zP%T;e#`KWvFbi`;*Pf4(jCF}^_g|Zi3qQm|ibLmWAm8_PLD}OJ8`xkqhjztbVg-7}j)qXjAE=M74&Po*W<6Z=AP6`Hj) zh60*yP=?J+@>;iqyx!a`?L#>i?90+gU}kIVbs{}jwR$~z_C;MJ; zZzQBBYM|J;Zzy}h2ku*C!`&=H{9%qU>Z2yG>0*~PZqb!>D zXdXFbtfMI^jikJ%lN?1ZP)f}bT0N$X#FQ#YJT{-q!w-;FA`kJtT0+`CX3%55b~^U6 z2CpZU;hBmge5*L0#OKD+(8sDY>3KC{qCJs`;2%nYKZat%N@FtJ_nCA*kEgE-9-8sZ zoWOkdnn>5(HEB!0Guo1-P97%{X-1YLxm^jNCDz|4woIGG)iZR!G?C6KUZ-KHOHl0aEgUN(g1)io>;gSV>OxLC z1bc~O?Ta3GUJwAwPOJml`h#E>KMK4R0@!7(BslXu`6%SjiW?`$RDhp`Bo)#W-&WeQ zc?Ipf*F-BSSCL}Xbdoq3OGCmAW6x4kl21NKT2;N2I8A|$S5L=z)^0doHwGI`?P-;H zG9^x4N|E}LskGo0b0zjOvnKrr7Fyep@SzD<-Kk9zN&@NPj8ZcrHzTIbuY%&Q@1n^~ zTBK|IigYD3Xlalo&2F-y)H&B^OZh&AkM9>{zn7+^otG(0U!3Np45w4_9h9$Afp4`m z{=*<>`i>txe=WxR(ND41`T*`Vi^Uiw2Ax};;P_Mn_+|0|sxPnPEGDwouQaYzvA&lG zak6ST!R2gE!7)L}pl`g6U3`taa`C8qpchRfuHdXi9=OPV1&+Jc0QVBlp{jia`VSsN zJCl*j%^l#)%t!F`c{!rsD6}>li{AXoI48G~Z6LU8^ORH2&F;op!fOFDq|d^smSWBh zC%e0g{r_>vIgM=`{Bb9k%pUaRBi*f}Fe`&>7zYY-m!R!oeL_D)kD{tqk!|}9Vv>f? z$U_aJvc1n^3}Q+1WEIUfzQ+vFi=wMOi;2m5M#jal6wAt|6XFNy+L23i``IJLuya3i zJm@_8d(c)_4R^X(k?4qzG}V796Y^<}+2CCnlymejP0Qa$PT$s()3x(7ea07>w{I$y zSm#o6Y#JlAyMr>9?xz(x`zYk_Aj;GWrs9?kYS@2}tThk*Hpn#-Q@pe#0OOW##ED9t zsPf_yO5_Eg+?RSBYpILU+78gZQ4ww|xyha$AZ!0?b}biYYqc+2W^W{+@UL+gD1rvtb;$9 zZ%L=p-t7#Hen=sFy5z|?(~0-t)Zw>*5z5`qh;==qH(w7k#Zqa^6wMY?@m9hedn2)Y zRy%%J)J_)ar_K0WhcJZ)nrNlxDdMktL^_8hXzJAEbkU8%HU9hFYms8(6K4c4LYn9#O;qT-C$-)5H z8>`@b>Q}gDx*vHA)zKm@jFuNIr0r^o7d|VS_#g(u^_ySF^ETi!&wMp^VA{sQOn7u?#{6C+NRaG)Gw)7DrwWpqH^{nWE zbvs??SwK&++^PFk5Y^caV%F3hWO`z5&_?mI`25IMyjwGhL^iUprlpBl?<2=Ny~rYE z-D$Yn)`l#dZqO;=a>ikb6C<<4fI4~;>E*;lbbPNft<_{G-|Q*16uo1{?+9Z&UgcA6 z1^;h@u-EAos2Z*>e9h?v&;B9?Yt}4y=@CCf6pVT zDs6Pfvo(8h`J=UH7AuOXVz*It-&>R${|i2CeZ|hmA>-|2SeL#ZW^W6D8Qb2AU%|Do!; z<9cl0uuEwGEkM%0NrML3nn*kPp67Xg(=Z}4v$99_iil8DqM{*0L!zXlLDEtpWRFM@ z5s~Z};{9IN^L}2x_m4iFkDgNZbzkQ=&f_@FH+$=;M@0vzhCU|cp(98V1xO<+lzOYn z5{i$|{)Tcv%Dj?}>K4*wmqE09%bXTQ2+yXJTN%`P;3B=Vt*6ry*OA}- z7Zh>(33V*#D}3}y7t8EOp}7C{UH*3<5kF($n`RN5PfUS>c4A1)_h*ncR5_Z%y>cTc zE6#&-D;>_+Wl5duLQLBEA?b&H5rsI96}|6k0fygKaWGJlS2+2AA+${9mUE~Z6fUfQ zyCc59>yGKDVe}LIY#!4+?K}ASz%_ITn+6Y!g5glqbXc{}j}azNdrvK#3U!5OVLR+z zdkHd9oFMN`H_jj1mbM7=%yi(qXC9mZS;!P#;pso~Fi!O;ruw&1mzj?ECpnA)))v!f z?@UbW?8FRTIefjh9~mz&Bb7IMNqJj3^*n4%YUi>@Yj-pC%^6D8RVS(F-c3rs>rBz5 zKPk!R3Ei@3peKucDdfv&a_M(cxDi|-JaPO=YNt+OXaD}xb#EAH+sISM{CJ^ZlDiOn zX0tF>R$mAldY8WDv{In`Ns3GNq!zFD)OJplj_-US4q;+ibD8dg$PqTHRej1>sq1J{ zZ{MEt!`Sh+Mzs5I56~GL077vlza^~8-l<~F^UhPnOsG7t0qzGE!}GXCR1@7tw-beQ zBTj}EbU`R>o5v5F=@SnJ^n&{uF{nPN9G)7#gag^XAk8NU9({^}lgWP|#dj}k&HOK? z6ge*BCGxS-C~RpPhc{HOlZ^EvGHMDU_~;A>MTUUQ=$ zTTn|Eu8ybO)tR*NiX(NqJ%!f!qOdyokML`cHtovX@b5rQE%8I`EfcxgsaN$E>}NX! zyUj|6I=jnIRMiL}IbFCQXUgp`@J!N&P4jkx+0RsdAT{Qz*ewAw>nKrtbr7s|w1wcw zc`)0@kC&vbiwyaEBll)KR5X5P>=w9K*cYX1rqJ9Nb82$l1-~WpVhNoJ>L>aBnN}DQKrMPgD9lC537?C6xn#5 z_Pnp9LcfLd>QN2Vygo%WBUFX%k(t8aX$J*Kk1K3(sTLmGFBN{a*^%k503xOCB(=et z+@mJZ#z&8Z3zNqPug9*X3uC8~K|~fQze}SLuRqX^B~JteR1ms89Ysf$-lUT=5(!?7 zp&rL_=-Ggg!qu`R!nAY$sHEh4(H&QMRiL@0CMsph!1t#O@WQnO9+}%f{_T7?u&|tm zpZJ3lV53JqxZOW2deqP#?5FhZ)6a|Q&4wtuR}9YeV#wGRw%)@nHgv}XaE{G zO@_2S0&)jh}lOim5wuN)(_LpCI1u>#xe9@2_s_sK48GWE?J zO@^OlP^k1vO4;E>yM857+ynzkxH*{)hIUf&G%re&Li;a+5z7Na)h~a8 zZnrOt`Xl=BA^^-1`h(q%X*{p&U-pTMK$$-Yv)s6np}@yR)tyz6LHuIb4lhUw>wBJ33^2MBO|7oLo_0Jc{cepJ^(g z!pTCYJYtSAK^gE?qmc}z{vzGWmq>Z@6!3pN z9C4U7rgouvKS40iA1ic^iJ%K6iz(-!mTpOamy)gYy|k{p8m2y&hV zXf?AN%1=9p$`%FCY&f7K~*e|@a^bJX?cf)&!E3kQr2K9eY zN#evYG^6KG3QqH;S&4CXZ(u@N4Zf(5R8l76u<3g`>Amho4$-9)lC+#cUiGI~m0py(CzSS! zlBhD-TTp*EgBsgx=yHyxAphi-U^}>{06PZ=($n)P-!ew5bWKexBeujQzk7I3sQ{N> zRl<^tQW}t2FU0Bp7J`0QklBYCQhqj>Hg~$wvAIctRF#t;@03iH<)u^_vzH3m8-%Xy zS;FS5mxAimf5ri@J|Kv;C7#DucP~QQ^<=nSy#Y>(S|O)12U5!x^AHqi?adt<5`!*5 ze~ulV&xc}^MC_J~(Y%So1OyYr^-``H&(z7On=qhKpW5pt8w^ z!}s;KyE8%_6ieSDXO$#Dc5Zj3i-$W_i{Z`2kvKrv4`nvwqC?+g?5o%tdr$FWNG}dj z3&ep28mRvH7~1~$i*^gl(Lr)EM$|dd>J4Rda)=jQU4IlWWQ@epgx&boFoJWb3U*Ax z8Y_Vu2CSzpCE<9ZT#MAa{*t2Q5VA49N20||)W7#UvMzl|ewrmzzS4@yjTK2%tS$swaLy5}t1r?Q%4>g0tHEt7@e zmX#DdX)`5k^`%Sir3LL1Lj|+<5rR>`LqWz=i)OeSpuNBQ)0~F+*m{4(zY%WD%QVWi zkSBfn*YM$_EW8?agAbz$7N>Rsw#zo#^`u9ATd^=II0oi!Y+_J-q4PvQKd$7t#i zDJV?{L&L&*XqdbnwM~@Z)5yPkX0Nxk6s_;uq4`h;RP*VDLV)B}rI$hb+zNE}U4b^Y zs!&mNK2*9*M3u-HI5KTIE;Q4Juvu$4VD$#$9iOUU+( z;6t}NS2n*`y3=i7I3}&t;QEjd&1=4*t0qahrchxEgeUpRX7B7P0MjY$s6)^ z4iE+xW{B<1)nmr^l`zq83>U(-_!nT~%w=RTz>yq|UnJ*^J?ZT5)8gF;d4k!F)o>so zo^uC|nft-h6n9v!a`s;i(d@rUkk;r<`!3w!+K+3oo^Zrh2GYuR!iJLr`GK&6%^r4G zpcQW(7av9@hZb3_Zi3L398qrVY8JM*dTQ7Nc_=K| z|0`x`6gRy|Oo>Ew)7OH!aVy3hwc!uz;rFBPQj+8iMagKk(h<#y{cu3!Xg*4wGg;C| zdMZNaKx6bNID_uT55xPd1o7<_@O6X?_B?S6*L92Jx0Jyw%)TRL{V+}?ntpL9N|~4< zrOic|(rr9Xem?vMuBAU`+-8XJn9flKcKefB!Z~Eb*fk8$l(Yb-J17tB0&KBIxCIow ze$Tkh=(~B8@VV`Pc;C84x__}J4VRIG$yo;6l8Qx^ROKqzkv59)5GYt}s5tRZoDf=3 zB`Qq41%WjmU|!xgD3Y;4ZMBL24n%zWJZ*BhPH~avq21*N>{D@tguYF1sQwz48blsk zz+HeO4-fXneCFB>mfSVgO>v42F@y1o-t&Jzl<&a%5|&RE+;wL*PB`?*AyoBHg3QPE z%nX1DKOy;iS1yK9tL0+N z;t-M!w8JO66|ni44axmHNj>7GlX-eDO+USyG{;V$qZMJ4-LRL+hg_nIwjp#rdpGqK zwJ@g%(JSL+Fk`$aCVohQa_<@7dLakq>u=`Hdq);rfY^?je+Lqt8ICa)52@(351GlE zK~~o-jCcThN`qi$$|vq|5^Y)x11X=6Q+6GJW)parB^rA^oBpNAJVyNmv?6W|O-FFn?A|GwE)wh7`nA`l2KgrHP zIcaA+VBv*k?xAqo*PNLmKtoop=$*-CNKy5M;^^t9`=9^=O4q^uAtxasHvx8yiH4H} z@o+FQ0XB9Xh2vf3p+m6?DLMb(IdW&KJ##lg^L@6Ms4xtRl^^5mv4`N~B{R78&H=IQ z8uh&Lob0!jQ;*N)kQvuT-kz@XNOKCEQXNU3M?5B6t%aJ&zd6%#fTx(f#1I{GK^Q!E z26Y&O#)dP@lm=piG%g0JJaJF-cTWj7 z%_(_0f&r-{HP{#Tl-|JuK0EMuSSB9J(!el(e{@K>fooS6V2-L0Cc3TyowVzs&+4t9 zY`LE2A!h!W>95S^L+9Ex;N|^ljQ{eN66e|at^J9>8(G_Uso+a8&sd-Eej{dfLwYF6+T$x0eVMhRtBO(C;< zd6*NgPNjGgU%}002x&d-B1j!uE{J0<3u7Y`DE*>8np>{`k59`*`6(Tsw>uvEVsr7k z!8gHWl|3DB=!DMXpYUtxHZ(Puj20^6gJ|t=8sV1wVk`I z8qE~=e1-K`w>;EfSxpgmUAfOZC;9DX9OUFhq40B)19Mj8GQF1eaNO{)AN+l1$ht`lR(g2V&lF z31fPk!D5A1M6u`K+vx{fbGT~jJ@9rZ0*8jfkn*h>8lH4T<>VP)(pk*hf7l4u>C}eR zENqFodn^-mG+yQOAV!SdX*ZV+F8LrlH8DqvvO3)6IUBK13Ozao3yXGo2!ltPfZv}n zqMuui^M}9y$-n{E$bYk+U{G3!8W&BW>gQv0cbSH(cWU6o13p+7wGiElj-m11u{inO z9*pj4i>9x0xVP77^*yMs@fqV2r{Ni;UU2U320m}!S6slWB^J6E`Hmfue9jwQd@u}- zAr9S2mb2nSboKNT_M&kh_1k(6QEIU>BLIq~Z960SRTIa*holX_E>BF*SY;6QTx!Vr zHtf1;A-@^JXPsqHL-h4e8vojuNxgU*z$(mld6%jCbR&4PPK9%s87Ry03Ik!;3!bbO z^Y1|1GLK`pT_;v7Tqm4M_7W;iEr7UVMm+II-Vj*kV#wy{sMQb&S5uvsSFUJfzqL+( zo*!W*D(p{=0EasEf@J{-ka=f66K|rS*BT7Ju?g>k*Cc*s353IXegI&H|!{D=f@lZenmLK1T*`8u_vR{JZ%B?X* zaUSlP-NpzIsBlgWN@m_b>qUuJWqu!PRt(|@^U-)~R5SPHtQKZ97_Sz@UzY!?V`Mo{ zbmOnWe`Jk>eXLUCA@1)Fchn1a;s?PVaDB!LQO*<-?n%kydwH$_DD!hP>Wyz>J}bN# zW z`HxO;W+uNKlCUMpdRzyNKmBmwn!Y&wQ3U37dQq2cCxmdR0>L(PkD$NzIkv2e7pr_Z zC|o#V$ch9$6KBkOCcKd}n2?q0K)b!S2(qJ3;b_g(xVv&I=K3h(1N#N&eB}~n{zZU{eE zRwyy$!5WV(=+KD&4rF&;70!G!1v5SsQm|yPkd? z{sG=et%rDf5epF0f8h20Xc+ZV(KY>cF_Z+asRlZnKZ|E1iy?*1D0c`$9opYN>Ex*d$VX zvrkx9J)P#&ufo~A3Sj$TPhRq}t6bU~%nyWxs?&2ESve|9@7f`ZxUvzKd^Z#ZzZobL zwalQ32TqiKa1AZd*^E=2UQ^Ot9ny2tfuSY_q6#~0QBfCU)eINyFxkOF`{_box$R`0 zk%`lEE@IltZ1hP?fXh!$@B>M;D8>-I@2I&X0CHYEwcp9q8xLl0w0)O70;fb9ouROmbvhrN?!N8CwbJo?LD2~AAE@$x5>#`Aa-^NG1elUJBQr-h-Pr1VyN#=db z=Rzk(CBcuT9%y0HfcFO8#APbP+Xf~|vX0!#@Ij%pdOMxasQh;z$2LWv;hZlR{&A2H zaXdjR9Xm^ipVT3=z-J0`i5Cz3s4FZJD)7oVXR3g-6pt_<%+|OXU z;Iiccf~pbxJkgFnLtF&~?{*>TS~fj>Fod!?YU%vx8q!|4m5e>Ek)Z7jOIn&aY|qL= z7CxL`nDOM-oRbW{N2!r(aGlVrml1lLc!I~zq~L^kl}tl|_7C0h^}1>d-sB3M8XsU= zyDi+CwhBr;Z==cRct*`Zg#deQF(_xB57$j!Fc}`2WsT;p60CG(1q3V8nS#rzi@+RK zabv6u6Y^u^%{sI@yA|aI$KuFa!I=F~2h-MT;D$qOxY;frW0Tk7*;Rm*{nlW%a2l_V z-$SxpgYeh5w;1!|J*T*ymfy_792?NFmLWO&6-v4HWo~%l-j`j4AG2=2^;g@VyQHZu zw;C(dMrjLbHsAgo$gd(Boc*gi#&~!2w83h6dBQ1r0=^dtYbAYVOA_3yC?$~fcYK0~qDD^)P) zF_Dh?1Y+QcV^~(_i&K9jFslsF{b%P`LKl71@#8=xOT6DrB2nXuJ%cI)6aB9k>pvK; zOzMt4BP5NYj|oN{*QGv{Y3S}Wo_FWZdq=}nr+7X`!5gjAy>&3E=Dk3z%Lh9z*@(9&AL-C3K7x`BUd4IwZJZoNI>U zrIp6R$39^6K`o3L@e%#XwQ)*r7)GY85mJV}5E_0e<2~;ioN+VFESBdbR`Sk&{E)F@ z;EANM)F@OETikIL%bp#_dN5unNWvzHAGCmCQ1rh8xo9{A7tGMXFtrRkG-d(ax?N1~ zo2KH@L4R?h${lpSZih=QY{%heY_RXLH!P7t^CvMHBwFy8*{^ISPKp>uW=3I{vaSv- zdfa9uAG!vu##74|k&dh(*$+;jdgg$e=(Jaub8%RmjDAF*WcKKx9GWv0y7J$p8KR2Y%w z06jFzIfl;5W%(usCZW*p+yD<2$BN387BCSIckE^ab5_;8oGi=AaVT?r%8L?7{{-Xa z{^gb*H{V(y=$D}PenL;(J=inkCkHTlM=0*3Fg5bpb zDA={*EaatJ=Q$~+`2e??46}8%zey zZ}G~sBE0vloUR!L3SHI*vP6YjOE2L2v`5&I;fmRQm3TEJ4sR^?zZlbY zS3HsQ(sq!{#=ST^bRTZ9Lv)$rhD)}Hab9jas_$?R?$=Eg=Fb@nJG-d!Mo{v>X7Vtk z<1UQtx)5Y`j^}2Ytm2U50NlIjJl(i=Ly+pM!jy@17z{D^=$jG^E53zR6J)WU;bC+M zFGY>1OHe8I09rLW;K~Q4WEAU-&iAEphTTR~+BAgwMSA|~%O4D*FR>8;6N|DxJ9dcB zAUz!W-F*%xNS2qidIfnLW?=%PkfxbjLg$@(6hj{LMZ4eUQFg=$xI8cyjYPNcjDikn z4O}UR0+tEmFFE4TvkmBA(Fxl(9fXX}D`C=2PhLGTvWKLq!XBCGIA+jzPK#w;$Z>Cr zIj8N~ovCOx=D`09sJ70;w@2DAfA?p6sp(16tX|TU$15=3+mX>~@Xk3ce0y{f{tUWH z>b6g?`T9C6cX7vS17~CEHDzpee?upx4WT_p^+=fCgG}`%;YGhec)mgkXLh=>9V3E| zb*SL5#ZmNUycX-uMO%XVFfbB!1bt)bUH)b=5vbqgE*x@@{32Acxrbm7l7PBNV%!?^ znMTY~M{h*|jn)`p@BBN=$N;`-NgBdX1N0bq85b&qkloOH-05)|XAL&M?$)&^w?Gr8 zAK%NZ)L9c|HSa297F5IGk;M#4g7q;A*(jR!HaOBFqY)hTu%FhG^(7DzcZ1LF-z_`F z?99=7U=c1_`wG*XCXt-wUB+;MOVI-%HZYE>$5#G1#0|?jI`HDu`=M)cC0e|zMD@oRD1Ck=cRFRXA{;UR=hhA-hp5Y# zH(@Q>x*SBcG%*XG3}WGLER)4C22D$GHFhaI441OPISATO(nCnG%@T?{@4(7EB@kUN z1FL##a!~cWe?Rz9_ZsDN7jw_xh#i)AXJs9p4mgHN-E6ts!m#8t4zW!~``5*i)b_i)KeM@rJ57DG4S1DkzB`plyLp$LuSvHrDzQrk;^ps~@+7#BY5>r<0o4APaOSBy;eQS;)>< zrpA~o9GYN^6=loyyf=~bHuzRcQ?RZ&gVU5pQnc|GY}$7fcghaJsrK$@2FqDBgFU)W zz%EJ3ICNJf2J9&yVNC~~+*gk}FT28{dS$e)F~Ch13-~esMy#v+7|v%k17e>st3no@ z(B#`y-uSV?f>BhN=L#e2)ekkh`Z^A3pOhPg;#4*IY`O;Tyzud;6l9y zxQ^WWFJws?Knx)zEX{Ah$EWj2KXfKp)a#M6!3SELmP}3~p3vhCRtePo&mw?R4^KJ5BZ3&zw)0V+6mZ z{X>)a#O&+c_q;o1;e_E#7P}tu$)u!6VDuu=jnfwfxg^r^>slo1naY1TmATp=({mzP zSq*@5f;$eF_MFXIaPjhXjJ>2yldWH1xo-d(Ul|Hj^OvF9DsK#Z=?t$v1~L)_m?L&~btTq73q2A6&OM^d4l5^@Z=1M(}n~PxxV%glYqnQLAbhnxr&B zqt!P!x=GTtFE7N&m1X#WK9EkEudv;BpwKRMhnnNku+3J13sq0(Ho&E?J+No@i5PUL zoG+=_Uh`e3c1#qm2Kh2R9E7i^_;*9BYFFd3C7L+S>=y2nzJ=}Q74d$F5^0*Y(fCbO zH2ZHM#lDiGJz^0pZ7U{gVK`pwJp@xUr{MiBU&%2pggz&Jq%os)@IhJ&UVk)~Y`4zA z^CCYiAK3*82Rz_l8e^-QbQW`6Jln3L-WT`|smN{~9CBa}g2i>_9w#Y>spOcsFu<@D z9Qqi*Bx6f(?H(wqI^2_sbsovoz~w%-xxuF1$PbusLlRD|ou?&^me}m-g%kbn@XyCY zy}^Ssr}G5Q^ofa;BRO!y)7p*jMM&u>8E5!z^+-im%Znh}0ohmebs8C{2E2L+=<2EB(ebxWnkn2)4IPC0W^idFDN^LW~ z6lUOKzec<(+J=uC>hMcsJ^8N~N-<+i>F_FbI^w;Gta9Js0aaHFxaNkxCPtB?W*w#M zokF4ee5tqb6MR@E!kd9TFzKZyv(MmgeZb5NUZ1nD@Havqofcl^)*4Y`{&2ug^44&f z9J?v*t%-tOb|+mPwTYLiFZC=y%565Ey{NkPhO=i@v3WV`*3s?x58N~AEei7E2m?3Z zB}vJyTMY2}MgwNKD&vmvt|%Md6%H#V@_5Z2TIgD79A~`+70o6?wboib_m?zqu+(un zoHngs!cxYzWMKyTth^7;Hcf`o#dA^D{S;b94~O4grkv+|E7*eX!D2HVwk6_4uVzrH zO9e0gq44qZdAPJ;C|3xaw9I3iH4Bd zh^tzkVBq=wn64&^&m^7e=U_dOifhID@&zRS;0xI;3L(|286=21soeiPrEc^jePdb7 z-BE=3wz|}%x`gKM$)$r^%gFwK0!HZ0BrW&h_|Ww&tDsm9z%eUDMR^Weu=&OAogyf< zFW@YBHa}pKIR=@txx$~qO@jNvS)}Ue!~IVf_{WsEEK#zz_({nVs8R_ zztKeJsARG_eHzQ2e?hG!fKrmKThcvqLos$k7}x8x+b>vNl*3S2^wKKDUfcaS7S)#S z04E-#@)7Q#=cl<5=eyn~;ELmT2CAZ2DsU%lMkHr06W^w+L~F+!jNX)t)-hq67Nt4C zmNPjSLN0@HQ1Rjvx*t4%n+Is)dW9iqA-2Qj%mCq(OQKlcOAp+yjOWM!`+kkD|ECX8 zJHHYaOxMil*aD2<8jz?Fk;&=l{7lBV!3)4}rtDe8ip9na=0EU`w1 znBn)fRN;LuJE0Q;@LpdX4$wuP@)HIYMpF{}H>qO+R5tHsFvqr$at!Wf$sFqAmg7YE zA$WBD7d%?G5;v?IM-FWRvG!9nn%|wyt!}sH?Zy?hooF>_7@W&*fcg`|pkUx82Hl|f z5G(BGyAsv*TB1TyB>z0yGQ>ho;fzE$Su86{EDxcj0Dat(-&mj z!H7Yf{Gqbd9gO9Y=NbS-VTFu9!TrX@zqH56C(qJNgA`#IMoU8l?|2>#ecOCc3Dv?!L5I#|Si(~(cz-}HN(NAh9jtd`# zW8+mh=Jj&LWhh-E=Dzih^e1!s!rt;*QSDtes^1?Ab)#Q#J$#bx2JS}j*rgNxJV?VC z*TY$(%bPi-w`9IxZ?G1-7;V6+R}N^gVG!i+S;~usAEP9dm|`xpDfzRzAG3``yY*AOll~+x7r4`oS&l7VppW<**Ix= z0j9LN)3A{Zw9#}BEnl>jmNgtD%NruRHD@4=SAI&JQstDfNfs|IzkvI4caW@}Bl*p) zrqfOpR4tSVP8IzGsU;7=W~?92AB>s83@%x%&)y_SzQB#g!<=RRta6U<*A{o{?=HlC z9w1~b>A<>aQY?&#e%{{9>-BffdvQpE>C)rZA7!zaYfTn?3&r>lcU+^m6AQP`qHa6Q z@pi~g)Vune19}5rpTxnNy5od5r=d-l41*u|!DLerf6(vtNy3fuA{5LlIXYkzQOF=0 z?g_@IK+Qit!TslDsJHRF5IgshFeXzZS6c)Z{}HmlTQ=DaMtqq>m>o(-mqd(%iS*NfEp z@1}E#$Ek5b2sz_^EM2w{_vqW;{XtG-qPT*B*R2+`e`N_8OXav+TGF-hki}*xb?b;r z-=uRg(V#g;@V@a{_;q6wjZqdclNpSCzXR`G`^iN_hp$z!wSMevXU6qhXHrlz2(_}G zW1p>?F>!G&W@##5z>_1`Y-536PcO&&qXU`AKc~d%<;mkD&j_4fUBg)Xyh>vOq=jV~ zD1UrAqq^eU&!#wJoCWJg(6pO6YIlvtowZMdY)g^2tI0YHvfVG%I`mrH-TyO36LV011d>k<{O2w%^u<8VnmBw4(Ob46_1Qehc7P-D1K22WjcvCvE!Pm z62tFsd01CU3Cd%9h5y-(pq~YM3(av@k~EpWxlH~lzvzLGE~H#MM$crmNUH7!ipqau zPGkig5*5?lo!QjYz<@@sG^3{j!>RPmEV4}2!{j9`=v8Hj+nU3%`tD^K(9$e)cN|Ys zhgGm0RV=BC7NmPHRth(~VLiwDoE|L6v+7B7S8}~$noppX(=*gq@Ro0kV>v@7?K)bo z)WTA`tC%gl2K8Eo;H>gaTy{Jk-?S*<>C^+b=0_RzEe*!IJI`Y4Y8U=sb(au#98DQ^ z5PfP~VW{g2R=qK3hR?Gn-SlK{Il9zr!<}`h7&*cWzw9?8^Q9W3n_MaG*7;H#a(pbC z=5YaP{G?B!eYd0duuAehP%H609vf)Idkr>DV2QoFX%1Q?X5g(8Rap6}f_B{)N_$+N zQa{zRR3Gq+d{b1|?Gk;SKMyx98%foYpff2pQQhh}=w+=If-*y7iqlX$T0 zI_}K1!-HSW;oaFFUXr{~9ML`)EcYGa3=YmS4I9!QbbnGZT$&~zhY{S~P zp`>im6-T~O!5!PGF~6{#Rf_PcVhmjV{gTbpMgGG-uxd#3Tt$ja3t7pF%MS+e-L*f= zAH$D5hn&7T+QNn28fc^G#kZfj+m`a1&T=6GA6YMBdgUVCIvPVEE2^-)H5c!W)4&H> zi?PUP1StgD2->L|sA|y#aywx}IiYSey00NPczZ+sY%k_RN%gVyl(q2}GzEy*0slJ? z21~QrdGFluIB)ty-WP1t8q6D=srwH@TZkL&?-MHwJiCWx?HfZEZ@bZiFG<+iYXEs2 zwxSD$6XQi@VZ!p5n0Ff~9EPu82#BBe{>q}0FSX_`*3#Uql@YdGbU^#KfiUKWtR zAByBa_^6a=Np7a^g6yMbA^vGHobNc!XVX5{LLuYsX58O;gC-pp1b6S*bjs}s9sRLY zz?wwy?W8Bpy+Q+p>Mhss$seRHZ)LcPQGEA%z)TsFL{aC%cFo57NV%SQqgvU zQqH_o42p&WJ7?g4`+wlQ=4^I*MG7^qIC?N+nHq;c7!_*a;%j`;#|BHxdxhOF>6GKN zId~}b-C0E{MsrDOP%KGhZlZ#9^Qh5fJs2ceqt_H^{uRAE&Cs!8ViaeEf*ppYnM}7v?o*rj4*&v{pQ1 zksH-+^};2uZ;^3DFct3t!Dg1Opj>;GmO1Rfiw8|G^=B=nt80;N>@u;-nZ?4{>Q>&& z?K%0IFA>XnT*u}id=|oF4F|5r^Td7oN;`++_ig#Wf2J)q8t`#aKE52Vj%-5fN&b!^ z{M~PchW-2C1{*K>d^p)zI!3`I3bec$`v%BK<=-F6i{+lL;)T70mW{#b) z!IRr!IuK04iY628a*R~sw0N&U~dikH7x|=XyRj#Qta?mEQ&y9#lp^V> z9>m{=>Uqt-z`B%ymeAXUfe^Rn`3_Y1?Li$_p@VCEP$#V8@kafZrGSJN9(7vR!6^}h| zODq)}2rDvVcx83jYXKcv>PTUm4)fK5Yj6J%J@irkpM@^X!3?fIqcUyCx_;$9Xn4_k zzO!(~qjbK-O%l%70%DY3?~mo>onns-L&e4Un+3VT8$uVg7ZjXrOwq411U0`|Lj15X zxNe{h>BZ_(*s3qM`dD8Gdo`Txs^T*O{kOL`ZJ)8ynXv%VFS79T*Psb<9d2;#XEZg96qm8^Z5NOAWBGB$XFi)O8% zy;EWZo%H$QUZDrX={8|>$+M95-TMMsmV+4;7DjijVjv}q64`-NS#RFdG3E=K%dr1P z^7G=e2Q8R4)(M-bC0KT;98WiGU?B*u`Z=?GSYo~X#o{oVkLXakg26zjHX@Z~ zM=W79Q6BQPt=KGf>zpPeIW&WvmH`iU_ivX|^c)2|C944MyG!xWxZ%=&WNTK6GC4D9 z&PwAj27@k^hSe|we7>h}f4Mqj6z+iU8s>1Q-)D%6X%kx+PZuYQd`C7;isV<@M1>zW zQ}~StvE4H_@qpadm{{7A`r7@5PSYN+BGMl;pC1OyH2r`1$~hvR(@xnugehwCr}RO! z8MXK+K#NQd4kCkI+N7bRfwfs1aEY^+Pv6)t$S^4XBokf|vj&f0UssyEXzJHYT1Mqq zxxyHZWh{iW*D{o)=px>3VlGZG-y+VhaTQm*J%Vv_L;1mG@fo)y zmq41SG2i~9Q?UV8?2zH4xBQ)s@Ot4nXxv^1g%kZzd5kaW2ar%Zatc$r0(zux$8p#Ll|q#H%gBID z$t~k1&8vEDha1o2@$EAK_q{s->y+;BXR^aH5NJ*y=YTQp7WH<+;O!I8uvZhR)ZOE| zxk92B;CTB;?qkpNC~S&&_U%`SyE=y!B)9W$6;bmF@2@Q+Tb+$)*6xQrG%_Le(rl&z z_;(IkY+%H8sWT}Xqvx>AHWA-L`4`6(mNAQQp)7U3*!*}BTd5~3 z@4HDnp|*yOtPBtv_5UokyRZtb40t2DI;DYgXWN5JL`^~r-)7*K^N^#icb{*78@oQD z(sETSlH{+JDP3^*-ZgyDq~<0`ZxAyU6XxhM+YWv^qGK+gMSm+Umte}Samt&S(K6b9 zs-=iV2O4GQh_=bo`5yO5Ggs(&;y0?CZA1ND%W(1N&2TWbKNJhckqX~om_nA=-A5!| zxwIJ7o5!PO<1lh`{EpccOL$n3Bv}}Hrw_NRmBfD0??p+_q`4mMIyW+D9`@)mft&C! z$@i1FL(ul}WmGRq;m#F|+`zuRrf?%EmnM)geTTYREs&726#kgq!Qxq~@bj(jIQ-OL zl+QT9sY8w>yZ#*rd-!KRy@seJfzKisyus`?`ZUIpw#-!+YLkvu-#PvI4{oC)p-9UgD~cj)%oMYtTpM z2A-2iAUWd>>Y0BU>#oS7^FvisHGYXb7CuK6olO|!cM;u(&)_$eP4lu9uAt%eK9K5k z29|4G!W$ak;YX)E`-+oiWQa%U%HstKXPmrsDj6?#X1xqoVaepbVe$r) zbF=4k)Zw$v!u&~&oUk$HaT^rg=*x{kS@>i5H({s;b$i#3LKjWO;KNgK@YOR=;4_6! zH$%mhnA!ahT4-6I^1AcfDO^$k{_E74Gs6DeO`Isr6h&;z$`Dz`fn|ak)(5bCP<#`P ztrRzc**Q58k1bLquOn-qE|u`y!$iD#bSsH#9K|tCW5jr8E2r&C!V6n70fk5RbMm!K zULa1KxDZDTsDKaAN8#(?cqlz<15XAogbT-JaYN;HLu*vhO2Y9g`=H^?$&4?|LuHwN zC%hUskk6^TO;<53BbLny$C`pJq;%&wK6lE){S9wXqvH@xF!;Oyz#L4Eo;teKWTQ-#=vn9G}$zM>5@@!ZL&s&VLfr2DGBZg8+OU?M-IuTB$AX zF`b`~L8?n0z^#G7Y^5C(`^3PHKLzLr+u`1vZE*icIsd!loyBmjODor)FwqUuFP{25 zfz2VX+h95DZdi*t*H`oTGo$ujS5o2gjDu_4;9{R!ShJ@A8xKWtX32Z6o20Eg8pFzd zV1Q`=BU;0wv9hc)W-I(aI`jziT9yJSHtSH~(r>oY92Gtq5i8>XtI;KSh>VT zxG6Ih?F)XeO?E6Q;FDfw#pkXp6f3NghPxBG9?ZGGPj%r;Liz;MEP|8L^7!K>bAsi+v93T~nAO zP4JsxAvD_dLMdNeGQRwYjx5EpGx74*lTz#j>O7@G|%dcYmnpTY++8>fqubT{yoein|K2(6e1e4{e-Y zBv&xTvPb1t~F zoTJnFuH|T&?StwHYq)ai-M6#w^+Pc{9W{ha4pBwT1C3vY)0_jd#IlzL2o0P2kob!^ z{*9STYtMF2_~uJu#e!V1)?Q7{uHTpTm(x%Sl>SiGt|}TD*agnJsbim4SJ7~gX)mXk_ z@6O;TUK}u|pQFjoVgJ`qSknC!R9b(AmY_+DIKZ_PoAWX_1xHQ29}iPZ3A}A`C?r3( z#DW!?c(@>m@2E=KZjSSP%Sa`4JDzCmf>W!GqyNi!XmT(Rj-~0tH^tdJPX?bZgFnBj zaB$*m^wx3OjjWoi?ji=GQ)`lB;=7;r$W43>wQ|clbyNsaW17o4! z^LGe5G?2Pw+`|RYD;N)phk3@|y_YQm#aA?#gn|rQbMVZpwWK%Bl@3I8AWm6}Gewip z5_E_}EjGTaWK;*X1|2RXUwb{y4ywHm(@1 zyOf}r{V6;$>^Q55+zR+bq0-qrmu;R;UCA{z{gD5{;$enq8ZlqGhCnXq?ik6jO`27)WDHD zA_$&4lk2Wo6URcz^%5s~Y}P8w{P{v06*5Y!6{UtN4Gg(QO2zw?VB$59Pu8Nlw?cd6 zZnn*U&%fDDTLEkglk-W94}0hd90?i*NBz~|T)*3J+4nBAKN|_Jy7)trO8{JJc>}rK z7D4r}CCt-=%}h|wBnpQt&%%fw7I+}U7w2Z}!kIOCT=X)+q_2&=0^|6TdzTZtd_gm9iNuHAl-ovZXgTH#$yHtQl0&~ zAzm-D`PywpwPdWlGwxkElY+t3j9+wZtsnP!51+dV&P6;s%5o~OrI9H(`%P+sajJgu@vZT;i$OnC}GG{OAyJD~Vc2b@q) zhkD;L(6Czy9*j+4#A#^lc**zdew`YGc7MO&L>*Tg){u{0YsWBif7W56bG8ExLTj#S z%KWqlEmOy1<<-j=rZo_4zKC(^*+I-E9V)ycAmjOZD71OQ^T^4cyHH+vDJm#jf@&*C zrFK*XzUIEc{I1`qyUZvENz~!Y6UG6%{opV;>+Yv0(*@W(^(LIN%Ec!=(lG1h09H7V zy>2@n`La<$pKI^2>_#*`c$LYv$ni=saQry(EjxqV?RVntds8vO>=-VX)QQ7??dN-0 zjC7CSphXHeKD`6SOmq8pAZN^^K~2(wM3kK4wtWZ9)!AYfl$&rGdoJ9F-D;Pzs|rQa zW0)0%@FR4FxN*!uo;O-4)zBlmYVBfk9g8W7qtifR?zq70 z&L4MIyc9H8TjQii6uw)QS==w2m`zV}1yx5p8$ zJ8r|$LsxNoL^$?)T!+&ARzPQ9G0KY%!e6%^tS$I=Ajdz~@uqD>Wh^6Xu{s;#XK#b6 zy1P-{pcGw=cHqGCVJO$6!p5G0nT(|viv97ldM@8^aAQ#?TZhCCrTe6zC?#vm!LBJ3{Ys$Vs?a*cX`#o^T;#AY@ z@ag=PmjBA*vaw4SAG7ip`;XG+jP;aLWvG!nhFJ!pdPoBr7mq}<5=|xz)-TQ<#N4qSs^5itoIPmEnCZoN2qiKi=-%-M#niJ^OL*IlpsG604dshCTDbCN}H6JT@hD2pa@FY|x7%;jIBT zNdw}T5Z^Bl9D2PqX+?9I7Os@x!UGr||oj=Y$RU>m~dyu?3lT5yb~zD@Y05GJ6HQy&xGbwj?7# zkOb9ST?5;Xje=9}bg;S49%S1NPY(nRb+9*2TtNQ~*x33Gh41KY9?q7W*u|3n@De;f z@Ku_xVPquKPsN5+vLCw&$%H#}wGA`5L92flo461HLkDVXZQk2#-lV-ud~7}&|I(L~ zUw*(xqbW*X&Z{PVhx!GACQ|f-MMJ&bwHWHghKQ8>>9PPDSbB<$8?~8@p56+#vTm{Q zKe-;*I3*)6e(N}>YG?x2$>UVv=Ihn)fwqGp>AZGiI_z&ufOWghK;w)Q%AT{MXce>^ z{u`UbRLtK;C8wX4ul26amsVX1r%8K8{z zhj7;K|K#vgQfFh>4xDge9mw=6YdA@B9!xId+S5d4UX1aMg9&t`L2DoKvS?0p|8WfA zLCJ|Rq#*LPFw+hW-!US?D;Y7UosWSgL zrv3~W$57{n+6atJgn<rKuy1%89KBY@CO$QZ*$#9PjdiE{G#gR7g%vE>&!){Rf@`e; ztgY;UtVK^@dh&jOAi)9s`>M}s@bb8k(6XTnF3z6AhGu`rzV|bcB!BhbV)RB*R@5Y> zXQpLB?LFI((M@S9UVp*|bqNttZdD_8)%fpaoP(s(?~)P_&Bs3)R?!=on+pcnl(wDSaHe4m zB^B5$YI*=lX9D^X>HQ>O-xN!v5 z&pS^EoY5eLZ=jX?b71|Ubf|=-P_8eBic7DeI{x<&(n`%jhwMo$)D6>i=bnGBm!5Ii z=aIj@M98vFvdM0r?l)vtJO`u8ZJ_tZlBYY%ugxr z-NYLuS&VAMygFWoh^1Q8;5+dDiXRWg*IEky^SUPLrq+9alIHgd zWl`NUHU4k4u&I_Rl0$a6CBMvD)6|H#PzyU0InCj9yZnAhpfaN*77c2=c0Eav9WFn= zKfPLtH0~ygs)tP0~_iSrEQp9V8Ma~ Z3l=O`uwcQ01q*iw{{njY%!dF#0RWf+Lm~hG literal 0 HcmV?d00001 diff --git a/raster/r.sim/r.sim.water/testsuite/data/depth_default.pack b/raster/r.sim/r.sim.water/testsuite/data/depth_default.pack new file mode 100644 index 0000000000000000000000000000000000000000..579863ecca98c84a9fa757a0de79d72b5b76aafa GIT binary patch literal 25906 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≦u6EvM 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ⅆOz+Ls8Z=_1PLz6z_KzK9Ad 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 Z95`^`z<~n?4jgV0{0l^7P67Zx0RS0g#c2Ql literal 0 HcmV?d00001 diff --git a/raster/r.sim/r.sim.water/testsuite/data/discharge_complex.pack b/raster/r.sim/r.sim.water/testsuite/data/discharge_complex.pack new file mode 100644 index 0000000000000000000000000000000000000000..01810de0cb695a428f15ec647b919853a8c4d9d1 GIT binary patch literal 38375 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

    |DgMIs~v0XA8ey6gz?x@?` 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 636500 && y() > 223500, 0.3, 0.01)", + ) + cls.runModule( + "r.mapcalc", + expression=f"{cls.infil} = if (x() < 636500 && y() > 223500, 0.001, 0)", + ) + cls.runModule( + "r.unpack", + input="data/depth_complex.pack", + output=cls.reference_depth_complex, + ) + cls.runModule( + "r.unpack", + input="data/discharge_complex.pack", + output=cls.reference_discharge_complex, + ) + + @classmethod + def tearDownClass(cls): + """Clean up test environment""" + cls.runModule( + "g.remove", + flags="f", + type="raster", + name=[ + cls.elevation, + cls.dx, + cls.dy, + cls.reference_depth_default, + cls.reference_discharge_default, + cls.rain, + cls.mannings, + cls.infil, + cls.reference_depth_complex, + cls.reference_discharge_complex, + ], + ) + + def tearDown(self): + """Clean up test environment""" + self.runModule( + "g.remove", + flags="f", + type="raster", + pattern=f"{self.depth}*,{self.discharge}*", + ) + + def test_default(self): + """Test r.sim.water execution with defaults""" + # Run the r.sim.water simulation + self.assertModule( + "r.sim.water", + elevation=self.elevation, + dx=self.dx, + dy=self.dy, + depth=self.depth, + discharge=self.discharge, + random_seed=1, + ) + + # Assert that the output rasters exist + self.assertRasterExists(self.depth) + self.assertRasterExists(self.discharge) + # Assert that the output rasters are the same + self.assertRastersEqual( + self.depth, reference=self.reference_depth_default, precision="0.000001" + ) + self.assertRastersEqual( + self.discharge, + reference=self.reference_discharge_default, + precision="0.000001", + ) + + def test_complex(self): + """Test r.sim.water execution with more complex inputs""" + # Run the r.sim.water simulation + self.assertModule( + "r.sim.water", + flags="t", + elevation=self.elevation, + dx=self.dx, + dy=self.dy, + rain=self.rain, + man=self.mannings, + infil=self.infil, + depth=self.depth, + discharge=self.discharge, + niterations=15, + output_step=5, + diffusion_coeff=0.9, + hmax=0.25, + halpha=3.9, + hbeta=0.6, + random_seed=1, + ) + + # Assert that the output rasters exist + self.assertRasterExists(f"{self.depth}.05") + self.assertRasterExists(f"{self.depth}.10") + self.assertRasterExists(f"{self.depth}.15") + # Assert that the output rasters are the same + self.assertRastersEqual( + f"{self.depth}.15", + reference=self.reference_depth_complex, + precision="0.000001", + ) + self.assertRastersEqual( + f"{self.discharge}.15", + reference=self.reference_discharge_complex, + precision="0.000001", + ) + + +@unittest.skip("runs too long") +class TestRSimWaterLarge(TestCase): + """Test r.sim.water with large region""" + + # Set up the necessary raster maps for testing + elevation = "elevation" + dx = "tmp_dx" + dy = "tmp_dy" + depth = "tmp_depth" + + @classmethod + def setUpClass(cls): + """Set up region, create necessary data""" + cls.runModule("g.region", raster=cls.elevation) + cls.runModule("r.slope.aspect", elevation=cls.elevation, dx=cls.dx, dy=cls.dy) + + @classmethod + def tearDownClass(cls): + """Clean up test environment""" + cls.runModule( + "g.remove", + flags="f", + type="raster", + name=[cls.elevation, cls.dx, cls.dy, cls.depth], + ) + + def test_default(self): + """Test r.sim.water execution with defaults""" + # Run the r.sim.water simulation + self.assertModule( + "r.sim.water", + elevation=self.elevation, + dx=self.dx, + dy=self.dy, + depth=self.depth, + random_seed=1, + ) + self.assertRasterFitsUnivar( + self.depth, reference="sum=30364.327529", precision=1e-6 + ) + + +if __name__ == "__main__": + from grass.gunittest.main import test + + test() From 690534d037511a8ad386ef4d67ec83fa82a90d20 Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Mon, 21 Oct 2024 11:09:02 -0400 Subject: [PATCH 424/471] wxGUI: Fixed E722 in modules/ (#4546) --- .flake8 | 3 +-- gui/wxpython/modules/colorrules.py | 4 ++-- gui/wxpython/modules/histogram.py | 4 ++-- gui/wxpython/modules/import_export.py | 2 +- gui/wxpython/modules/mcalc_builder.py | 6 +++--- 5 files changed, 9 insertions(+), 10 deletions(-) diff --git a/.flake8 b/.flake8 index 5d2d792b0c3..3a146f34ebd 100644 --- a/.flake8 +++ b/.flake8 @@ -24,8 +24,7 @@ per-file-ignores = doc/python/m.distance.py: E501 gui/scripts/d.wms.py: E501 gui/wxpython/image2target/g.gui.image2target.py: E501 - gui/wxpython/modules/*: F841, E722 - gui/wxpython/nviz/*: E722, F403, F405 + gui/wxpython/nviz/*: E266, E722, F403, F405 gui/wxpython/photo2image/*: F841, E722, E265 gui/wxpython/photo2image/g.gui.photo2image.py: E501, F841 gui/wxpython/psmap/*: F841, E266, E722 diff --git a/gui/wxpython/modules/colorrules.py b/gui/wxpython/modules/colorrules.py index 3dfdde8c42b..6f48006db44 100644 --- a/gui/wxpython/modules/colorrules.py +++ b/gui/wxpython/modules/colorrules.py @@ -325,7 +325,7 @@ def LoadRules(self): else: value = float(self.ruleslines[item][self.attributeType]) self.mainPanel.FindWindowById(item + 2000).SetValue(value) - except: + except Exception: continue if message: @@ -407,7 +407,7 @@ def _initLayer(self): layer = sel else: layer = self.layerTree.FindItemByData(key="type", value=self.mapType) - except: + except (AttributeError, TypeError): layer = None if layer: mapLayer = self.layerTree.GetLayerInfo(layer, key="maplayer") diff --git a/gui/wxpython/modules/histogram.py b/gui/wxpython/modules/histogram.py index c8001858dff..61d41809858 100644 --- a/gui/wxpython/modules/histogram.py +++ b/gui/wxpython/modules/histogram.py @@ -258,7 +258,7 @@ def UpdateHistDone(self): return try: id = self.imagedict[self.img] - except: + except KeyError: return # paint images to PseudoDC @@ -524,7 +524,7 @@ def OnCloseWindow(self, event): """ try: self.propwin.Close(True) - except: + except Exception: pass self.Map.Clean() self.Destroy() diff --git a/gui/wxpython/modules/import_export.py b/gui/wxpython/modules/import_export.py index c3106d0467b..6810484bfdd 100644 --- a/gui/wxpython/modules/import_export.py +++ b/gui/wxpython/modules/import_export.py @@ -527,7 +527,7 @@ def OnRun(self, event): if nBandsStr: try: nBands = int(nBandsStr.rstrip("\n")) - except: + except ValueError: pass if nBands < 0: GWarning(_("Unable to determine number of raster bands"), parent=self) diff --git a/gui/wxpython/modules/mcalc_builder.py b/gui/wxpython/modules/mcalc_builder.py index b0f2f280409..52630e55820 100644 --- a/gui/wxpython/modules/mcalc_builder.py +++ b/gui/wxpython/modules/mcalc_builder.py @@ -608,7 +608,7 @@ def _addSomething(self, what): if newmcalcstr[-1] != " ": newmcalcstr += " " position_offset += 1 - except: + except IndexError: pass newmcalcstr += what @@ -617,7 +617,7 @@ def _addSomething(self, what): try: if newmcalcstr[-1] != " " and mcalcstr[position] != " ": newmcalcstr += " " - except: + except IndexError: newmcalcstr += " " newmcalcstr += mcalcstr[position:] @@ -632,7 +632,7 @@ def _addSomething(self, what): try: if newmcalcstr[position + position_offset] == " ": position_offset += 1 - except: + except IndexError: pass self.text_mcalc.SetInsertionPoint(position + position_offset) From 15134e14f181c9c042a74486132cba52ea8d90e6 Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Mon, 21 Oct 2024 11:10:58 -0400 Subject: [PATCH 425/471] temporal: Fixed E265/E266 in t.rast.what/ (#4550) --- .flake8 | 3 +-- temporal/t.rast.what/t.rast.what.py | 16 ---------------- 2 files changed, 1 insertion(+), 18 deletions(-) diff --git a/.flake8 b/.flake8 index 3a146f34ebd..90686370f67 100644 --- a/.flake8 +++ b/.flake8 @@ -114,8 +114,7 @@ per-file-ignores = scripts/*/*.py: E501 temporal/t.rast.to.vect/t.rast.to.vect.py: E501 temporal/t.vect.algebra/t.vect.algebra.py: E501 - # ## used (##% key: r etc) - temporal/t.rast.what/t.rast.what.py: E265, E266, E501 + temporal/t.rast.what/t.rast.what.py: E501 # Line too long (esp. module interface definitions) temporal/*/*.py: E501 diff --git a/temporal/t.rast.what/t.rast.what.py b/temporal/t.rast.what/t.rast.what.py index 4d13c6a924c..24190a6a716 100755 --- a/temporal/t.rast.what/t.rast.what.py +++ b/temporal/t.rast.what/t.rast.what.py @@ -98,22 +98,6 @@ # % description: Use stdin as input and ignore coordinates and point option # %end -## Temporary disabled the r.what flags due to test issues -##%flag -##% key: f -##% description: Show the category labels of the grid cell(s) -##%end - -##%flag -##% key: r -##% description: Output color values as RRR:GGG:BBB -##%end - -##%flag -##% key: i -##% description: Output integer category values, not cell values -##%end - # %flag # % key: v # % description: Show the category for vector points map From a4786e1272e741865c2c42371056c718e8388ea3 Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Mon, 21 Oct 2024 15:29:22 -0400 Subject: [PATCH 426/471] wxGUI: Fixed F405 in nviz/ (#4567) --- .flake8 | 2 +- gui/wxpython/nviz/wxnviz.py | 197 ++++++++++++++++++++++++++++++++++-- 2 files changed, 188 insertions(+), 11 deletions(-) diff --git a/.flake8 b/.flake8 index 90686370f67..8211d9e03d4 100644 --- a/.flake8 +++ b/.flake8 @@ -24,7 +24,7 @@ per-file-ignores = doc/python/m.distance.py: E501 gui/scripts/d.wms.py: E501 gui/wxpython/image2target/g.gui.image2target.py: E501 - gui/wxpython/nviz/*: E266, E722, F403, F405 + gui/wxpython/nviz/*: E722 gui/wxpython/photo2image/*: F841, E722, E265 gui/wxpython/photo2image/g.gui.photo2image.py: E501, F841 gui/wxpython/psmap/*: F841, E266, E722 diff --git a/gui/wxpython/nviz/wxnviz.py b/gui/wxpython/nviz/wxnviz.py index 47cf99c079d..5a223747277 100644 --- a/gui/wxpython/nviz/wxnviz.py +++ b/gui/wxpython/nviz/wxnviz.py @@ -22,9 +22,9 @@ @author Anna Kratochvilova (Google SoC 2011) """ -import sys import locale import struct +import sys from math import sqrt try: @@ -42,26 +42,203 @@ import wx try: - from ctypes import * + from ctypes import ( + CFUNCTYPE, + UNCHECKED, + byref, + c_char_p, + c_double, + c_float, + c_int, + c_ubyte, + create_string_buffer, + pointer, + ) except KeyError as e: print("wxnviz.py: {}".format(e), file=sys.stderr) try: - from grass.lib.gis import * - from grass.lib.raster3d import * - from grass.lib.vector import * - from grass.lib.ogsf import * - from grass.lib.nviz import * - from grass.lib.raster import * + from grass.lib.gis import ( + G_find_raster2, + G_find_raster3d, + G_find_vector2, + G_free, + G_fully_qualified_name, + G_gisinit, + G_set_error_routine, + G_set_percent_routine, + G_unset_error_routine, + G_unset_percent_routine, + G_unset_window, + G_warning, + ) + from grass.lib.nviz import ( + ATT_COLOR, + ATT_EMIT, + ATT_MASK, + ATT_SHINE, + ATT_TOPO, + ATT_TRANSP, + CONST_ATT, + MAP_ATT, + MAP_OBJ_SITE, + MAP_OBJ_SURF, + MAP_OBJ_UNDEFINED, + MAP_OBJ_VECT, + MAP_OBJ_VOL, + Colors, + Nviz_change_exag, + Nviz_color_from_str, + Nviz_del_texture, + Nviz_delete_arrow, + Nviz_delete_scalebar, + Nviz_draw_all, + Nviz_draw_arrow, + Nviz_draw_cplane, + Nviz_draw_fringe, + Nviz_draw_image, + Nviz_draw_model, + Nviz_draw_quick, + Nviz_draw_scalebar, + Nviz_flythrough, + Nviz_get_bgcolor, + Nviz_get_cplane_rotation, + Nviz_get_cplane_translation, + Nviz_get_current_cplane, + Nviz_get_exag, + Nviz_get_exag_height, + Nviz_get_focus, + Nviz_get_longdim, + Nviz_get_max_texture, + Nviz_get_modelview, + Nviz_get_viewpoint_height, + Nviz_get_viewpoint_position, + Nviz_get_xyrange, + Nviz_get_zrange, + Nviz_has_focus, + Nviz_init_data, + Nviz_init_rotation, + Nviz_init_view, + Nviz_load_image, + Nviz_look_here, + Nviz_new_map_obj, + Nviz_num_cplanes, + Nviz_off_cplane, + Nviz_on_cplane, + Nviz_resize_window, + Nviz_set_2D, + Nviz_set_arrow, + Nviz_set_attr, + Nviz_set_bgcolor, + Nviz_set_cplane_here, + Nviz_set_cplane_rotation, + Nviz_set_cplane_translation, + Nviz_set_fence_color, + Nviz_set_focus, + Nviz_set_focus_map, + Nviz_set_fringe, + Nviz_set_light_ambient, + Nviz_set_light_bright, + Nviz_set_light_color, + Nviz_set_light_position, + Nviz_set_rotation, + Nviz_set_scalebar, + Nviz_set_surface_attr_default, + Nviz_set_viewpoint_height, + Nviz_set_viewpoint_persp, + Nviz_set_viewpoint_position, + Nviz_set_viewpoint_twist, + Nviz_unset_attr, + Nviz_unset_rotation, + nv_data, + ) + from grass.lib.ogsf import ( + GS_clear, + GS_delete_surface, + GS_get_cat_at_xy, + GS_get_distance_alongsurf, + GS_get_rotation_matrix, + GS_get_selected_point_on_surface, + GS_get_surf_list, + GS_get_trans, + GS_get_val_at_xy, + GS_get_viewdir, + GS_libinit, + GS_num_surfs, + GS_set_att_const, + GS_set_drawmode, + GS_set_drawres, + GS_set_rotation_matrix, + GS_set_trans, + GS_set_viewdir, + GS_set_wire_color, + GS_setall_drawmode, + GS_setall_drawres, + GS_surf_exists, + GS_write_ppm, + GS_write_tif, + GVL_delete_vol, + GVL_get_trans, + GVL_init_region, + GVL_isosurf_add, + GVL_isosurf_del, + GVL_isosurf_move_down, + GVL_isosurf_move_up, + GVL_isosurf_num_isosurfs, + GVL_isosurf_set_att_const, + GVL_isosurf_set_att_map, + GVL_isosurf_set_drawmode, + GVL_isosurf_set_drawres, + GVL_isosurf_set_flags, + GVL_isosurf_unset_att, + GVL_libinit, + GVL_set_draw_wire, + GVL_set_trans, + GVL_slice_add, + GVL_slice_del, + GVL_slice_move_down, + GVL_slice_move_up, + GVL_slice_num_slices, + GVL_slice_set_drawmode, + GVL_slice_set_drawres, + GVL_slice_set_pos, + GVL_slice_set_transp, + GVL_vol_exists, + ) + from grass.lib.raster import Rast__init_window, Rast_unset_window, Vect_read_colors + from grass.lib.raster3d import ( + GP_delete_site, + GP_get_sitename, + GP_select_surf, + GP_set_style, + GP_set_style_thematic, + GP_set_trans, + GP_set_zmode, + GP_site_exists, + GP_unselect_surf, + GP_unset_style_thematic, + ) + from grass.lib.vector import ( + GV_delete_vector, + GV_get_vectname, + GV_select_surf, + GV_set_style, + GV_set_style_thematic, + GV_set_trans, + GV_surf_is_selected, + GV_unselect_surf, + GV_unset_style_thematic, + GV_vect_exists, + ) except (ImportError, OSError, TypeError) as e: print("wxnviz.py: {}".format(e), file=sys.stderr) +import grass.script as gs from core.debug import Debug -from core.utils import autoCropImageFromFile from core.gcmd import DecodeString from core.globalvar import wxPythonPhoenix +from core.utils import autoCropImageFromFile from gui_core.wrap import Rect -import grass.script as gs log = None progress = None From 9d699d57ce29f16865e734ca8ee6c0454add9244 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Bartoletti?= Date: Mon, 21 Oct 2024 21:44:58 +0200 Subject: [PATCH 427/471] r3.showdspf: fix a typo: case 3 instead of case3 (#3867) --- raster3d/r3.showdspf/main_ogl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/raster3d/r3.showdspf/main_ogl.c b/raster3d/r3.showdspf/main_ogl.c index 55b3097e2cf..da6e90a3721 100644 --- a/raster3d/r3.showdspf/main_ogl.c +++ b/raster3d/r3.showdspf/main_ogl.c @@ -1159,7 +1159,7 @@ void do__draw(file_info *Headp, struct dspec *D_spec) fdraw_polys(D_spec); break; case 2: - case3: + case 3: gdraw_polys(D_spec); break; } From 67a160931aa01d29a896def827d5dd687e08bfd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Mon, 21 Oct 2024 17:07:17 -0400 Subject: [PATCH 428/471] style: Fix if-else-block-instead-of-if-exp (SIM108) (part 2) (#4562) * style: Fix if-else-block-instead-of-if-exp (SIM108) in scripts/ Ruff rule: https://docs.astral.sh/ruff/rules/if-else-block-instead-of-if-exp * style: Fix if-else-block-instead-of-if-exp (SIM108) in python/ Ruff rule: https://docs.astral.sh/ruff/rules/if-else-block-instead-of-if-exp * checks: Rename inner variable shadowing type to _type in python/grass/temporal/gui_support.py * style: Manual fixes for if-else-block-instead-of-if-exp (SIM108) in python/ Ruff rule: https://docs.astral.sh/ruff/rules/if-else-block-instead-of-if-exp --- python/grass/app/runtime.py | 10 +---- python/grass/benchmark/app.py | 6 +-- python/grass/benchmark/plots.py | 10 +---- python/grass/exceptions/__init__.py | 10 ++--- python/grass/gunittest/case.py | 14 ++----- python/grass/gunittest/invoker.py | 5 +-- python/grass/gunittest/multireport.py | 5 +-- python/grass/gunittest/reporters.py | 22 +++-------- python/grass/jupyter/map3d.py | 5 +-- python/grass/pydispatch/signal.py | 7 +--- .../pygrass/modules/interface/parameter.py | 5 +-- python/grass/pygrass/vector/__init__.py | 5 +-- python/grass/pygrass/vector/geometry.py | 5 +-- python/grass/pygrass/vector/table.py | 5 +-- python/grass/script/core.py | 15 ++------ python/grass/script/raster.py | 5 +-- python/grass/script/task.py | 10 +---- .../data/script_using_temporary_region.py | 10 +---- python/grass/script/utils.py | 20 ++-------- python/grass/script/vector.py | 10 +---- python/grass/temporal/abstract_map_dataset.py | 5 +-- .../temporal/abstract_space_time_dataset.py | 10 +---- .../grass/temporal/c_libraries_interface.py | 11 +----- python/grass/temporal/datetime_math.py | 15 ++------ python/grass/temporal/gui_support.py | 23 ++++++------ python/grass/temporal/list_stds.py | 5 +-- python/grass/temporal/register.py | 10 +---- python/grass/temporal/sampling.py | 17 ++------- python/grass/temporal/temporal_algebra.py | 37 ++++--------------- python/grass/temporal/temporal_granularity.py | 9 ++--- .../temporal/temporal_raster_base_algebra.py | 5 +-- python/grass/temporal/univar_statistics.py | 5 +-- scripts/d.rast.edit/d.rast.edit.py | 5 +-- scripts/d.rast.leg/d.rast.leg.py | 10 +---- scripts/db.droptable/db.droptable.py | 10 +---- scripts/db.out.ogr/db.out.ogr.py | 5 +-- scripts/db.univar/db.univar.py | 5 +-- scripts/g.extension/g.extension.py | 15 ++------ scripts/g.manual/g.manual.py | 5 +-- scripts/i.in.spotvgt/i.in.spotvgt.py | 6 +-- scripts/i.oif/i.oif.py | 10 +---- scripts/i.spectral/i.spectral.py | 5 +-- scripts/m.proj/m.proj.py | 10 +---- scripts/r.buffer.lowmem/r.buffer.lowmem.py | 5 +-- scripts/r.in.aster/r.in.aster.py | 5 +-- scripts/r.in.srtm/r.in.srtm.py | 5 +-- scripts/r.in.wms/wms_cap_parsers.py | 10 +---- scripts/r.in.wms/wms_drv.py | 5 +-- scripts/r.out.xyz/r.out.xyz.py | 5 +-- scripts/r.reclass.area/r.reclass.area.py | 5 +-- scripts/r.unpack/r.unpack.py | 5 +-- scripts/r3.in.xyz/r3.in.xyz.py | 10 +---- scripts/v.build.all/v.build.all.py | 5 +-- .../v.db.reconnect.all/v.db.reconnect.all.py | 5 +-- scripts/v.dissolve/tests/conftest.py | 5 +-- .../tests/v_dissolve_aggregate_test.py | 10 +---- .../tests/v_dissolve_layers_test.py | 5 +-- scripts/v.in.e00/v.in.e00.py | 5 +-- scripts/v.in.lines/v.in.lines.py | 7 +--- scripts/v.in.mapgen/v.in.mapgen.py | 10 +---- scripts/v.pack/v.pack.py | 5 +-- scripts/v.rast.stats/v.rast.stats.py | 15 ++------ scripts/v.report/v.report.py | 5 +-- scripts/v.unpack/v.unpack.py | 15 ++------ 64 files changed, 124 insertions(+), 445 deletions(-) diff --git a/python/grass/app/runtime.py b/python/grass/app/runtime.py index 3f3805c9dfd..27f35760b8f 100644 --- a/python/grass/app/runtime.py +++ b/python/grass/app/runtime.py @@ -79,10 +79,7 @@ def append_left_addon_paths(paths, config_dir, env): # addons (base) addon_base = env.get("GRASS_ADDON_BASE") if not addon_base: - if MACOS: - name = "Addons" - else: - name = "addons" + name = "addons" if not MACOS else "Addons" addon_base = os.path.join(config_dir, name) env["GRASS_ADDON_BASE"] = addon_base @@ -174,10 +171,7 @@ def set_python_path_variable(install_path, env): """Set PYTHONPATH to find GRASS Python package in subprocesses""" path = env.get("PYTHONPATH") etcpy = os.path.join(install_path, "etc", "python") - if path: - path = etcpy + os.pathsep + path - else: - path = etcpy + path = etcpy + os.pathsep + path if path else etcpy env["PYTHONPATH"] = path diff --git a/python/grass/benchmark/app.py b/python/grass/benchmark/app.py index 09cdf3f9481..835fc0d544b 100644 --- a/python/grass/benchmark/app.py +++ b/python/grass/benchmark/app.py @@ -47,11 +47,7 @@ def join_results_cli(args): def select_only(result): return result.label == args.only - if args.only: - select_function = select_only - else: - select_function = None - + select_function = select_only if args.only else None results = join_results_from_files( source_filenames=args.results, prefixes=args.prefixes, diff --git a/python/grass/benchmark/plots.py b/python/grass/benchmark/plots.py index 9483fda9116..24afcbcfbae 100644 --- a/python/grass/benchmark/plots.py +++ b/python/grass/benchmark/plots.py @@ -25,10 +25,7 @@ def get_pyplot(to_file): """ import matplotlib as mpl # pylint: disable=import-outside-toplevel - if to_file: - backend = "agg" - else: - backend = None + backend = "agg" if to_file else None if backend: mpl.use(backend) @@ -124,10 +121,7 @@ def num_cells_plot(results, filename=None, title=None, show_resolution=False): x_ticks = set() for result in results: - if show_resolution: - x = result.resolutions - else: - x = result.cells + x = result.resolutions if show_resolution else result.cells x_ticks.update(x) plt.plot(x, result.times, label=result.label) if hasattr(result, "all_times"): diff --git a/python/grass/exceptions/__init__.py b/python/grass/exceptions/__init__.py index b22d0a45c1d..deec48ffd96 100644 --- a/python/grass/exceptions/__init__.py +++ b/python/grass/exceptions/__init__.py @@ -71,13 +71,9 @@ def __init__(self, module, code, returncode, errors=None): """ # CalledProcessError has undocumented constructor super().__init__(returncode, module) - if not module or module in code: - # No need to include module name if it is directly in code - # of if it is not set. - executed = code - else: - # Make sure module name is there if provided and not in code. - executed = f"{module} {code}" + # No need to include module name if it is directly in code of if it is not set. + # Otherwise, make sure module name is there if provided and not in code. + executed = code if not module or module in code else f"{module} {code}" if errors: # We assume actual errors, e.g., captured stderr. err = _("See the following errors:\n{errors}").format(errors=errors) diff --git a/python/grass/gunittest/case.py b/python/grass/gunittest/case.py index 1277ee542b5..4e0ee3ab24d 100644 --- a/python/grass/gunittest/case.py +++ b/python/grass/gunittest/case.py @@ -691,10 +691,7 @@ def assertFileMd5(self, filename, md5, text=False, msg=None): at the end of file (as for example, Git or PEP8 requires). """ self.assertFileExists(filename, msg=msg) - if text: - actual = text_file_md5(filename) - else: - actual = file_md5(filename) + actual = text_file_md5(filename) if text else file_md5(filename) if actual != md5: standardMsg = ( "File <{name}> does not have the right MD5 sum.\n" @@ -1339,12 +1336,9 @@ def runModule(cls, module, expecting_stdout=False, **kwargs): errors = " The errors are:\n" + module.outputs.stderr else: errors = " There were no error messages." - if module.outputs.stdout: - # this is not appropriate for translation but we don't want - # and don't need testing to be translated - got = "only whitespace." - else: - got = "nothing." + # This is not appropriate for translation but we don't want + # and don't need testing to be translated + got = "only whitespace." if module.outputs.stdout else "nothing." raise RuntimeError( "Module call " + module.get_python() diff --git a/python/grass/gunittest/invoker.py b/python/grass/gunittest/invoker.py index 2fe9c898b8b..29b62830e25 100644 --- a/python/grass/gunittest/invoker.py +++ b/python/grass/gunittest/invoker.py @@ -54,10 +54,7 @@ def update_keyval_file(filename, module, returncode): keyval["name"] = module.name keyval["tested_dir"] = module.tested_dir if "status" not in keyval.keys(): - if returncode is None or returncode: - status = "failed" - else: - status = "passed" + status = "failed" if returncode is None or returncode else "passed" keyval["status"] = status keyval["returncode"] = returncode keyval["test_file_authors"] = test_file_authors diff --git a/python/grass/gunittest/multireport.py b/python/grass/gunittest/multireport.py index c5f36a6e0db..5a0350f1786 100644 --- a/python/grass/gunittest/multireport.py +++ b/python/grass/gunittest/multireport.py @@ -119,10 +119,7 @@ def median(values): smedian = median(successes) smax = max(successes) - if successes[-1] < smedian: - color = "r" - else: - color = "g" + color = "r" if successes[-1] < smedian else "g" # another possibility is to color according to the gradient, ideally # on the whole curve but that's much more complicated diff --git a/python/grass/gunittest/reporters.py b/python/grass/gunittest/reporters.py index 09f371dfffc..57b0a8fc643 100644 --- a/python/grass/gunittest/reporters.py +++ b/python/grass/gunittest/reporters.py @@ -234,11 +234,8 @@ def get_svn_path_authors(path, from_date=None): :returns: a set of authors """ - if from_date is None: - # this is the SVN default for local copies - revision_range = "BASE:1" - else: - revision_range = "BASE:{%s}" % from_date + # "BASE:1" is the SVN default for local copies + revision_range = "BASE:1" if from_date is None else "BASE:{%s}" % from_date try: # TODO: allow also usage of --limit p = subprocess.Popen( @@ -487,10 +484,7 @@ def tail(filename, n): def returncode_to_html_text(returncode, timed_out=None): if returncode: - if timed_out is not None: - extra = f" (timeout >{timed_out}s)" - else: - extra = "" + extra = f" (timeout >{timed_out}s)" if timed_out is not None else "" return f'FAILED{extra}' # alternatives: SUCCEEDED, passed, OK return 'succeeded' @@ -857,10 +851,7 @@ def finish(self): # this shoul be moved to some additional meta passed in constructor svn_info = get_svn_info() - if not svn_info: - svn_revision = "" - else: - svn_revision = svn_info["revision"] + svn_revision = "" if not svn_info else svn_info["revision"] summary = {} summary["files_total"] = self.test_files @@ -1025,10 +1016,7 @@ def end_file_test( num_failed = test_summary.get("failures", 0) num_failed += test_summary.get("errors", 0) if num_failed: - if num_failed > 1: - text = " ({f} tests failed)" - else: - text = " ({f} test failed)" + text = " ({f} tests failed)" if num_failed > 1 else " ({f} test failed)" self._stream.write(text.format(f=num_failed)) self._stream.write("\n") # TODO: here we lost the possibility to include also file name diff --git a/python/grass/jupyter/map3d.py b/python/grass/jupyter/map3d.py index dd373f315a7..99253251406 100644 --- a/python/grass/jupyter/map3d.py +++ b/python/grass/jupyter/map3d.py @@ -210,10 +210,7 @@ def render(self, **kwargs): with Display( size=(self._width, self._height), **additional_kwargs ) as display: - if has_env_copy: - env = display.env() - else: - env = os.environ.copy() + env = display.env() if has_env_copy else os.environ.copy() self._region_manager.set_region_from_command(env=env, **kwargs) self.overlay.region_manager.set_region_from_env(env) gs.run_command(module, env=env, **kwargs) diff --git a/python/grass/pydispatch/signal.py b/python/grass/pydispatch/signal.py index 1e968e99dca..51808e01ee3 100644 --- a/python/grass/pydispatch/signal.py +++ b/python/grass/pydispatch/signal.py @@ -7,7 +7,7 @@ from grass.pydispatch import dispatcher -def _islambda(function): +def _islambda(function) -> bool: """ Tests if object is a lambda function. @@ -146,10 +146,7 @@ class connects to the signal:: will print """ if weak is None: - if _islambda(handler): - weak = False - else: - weak = True + weak = not _islambda(handler) dispatcher.connect(receiver=handler, signal=self, weak=weak) def disconnect(self, handler, weak=True): diff --git a/python/grass/pygrass/modules/interface/parameter.py b/python/grass/pygrass/modules/interface/parameter.py index 3b58ea2b372..1ec5466bc74 100644 --- a/python/grass/pygrass/modules/interface/parameter.py +++ b/python/grass/pygrass/modules/interface/parameter.py @@ -337,10 +337,7 @@ def __doc__(self): .. """ if hasattr(self, "values"): - if self.isrange: - vals = self.isrange - else: - vals = ", ".join([repr(val) for val in self.values]) + vals = self.isrange or ", ".join([repr(val) for val in self.values]) else: vals = False if self.keydescvalues: diff --git a/python/grass/pygrass/vector/__init__.py b/python/grass/pygrass/vector/__init__.py index 461050807b0..6723b2e44b3 100644 --- a/python/grass/pygrass/vector/__init__.py +++ b/python/grass/pygrass/vector/__init__.py @@ -859,10 +859,7 @@ def features_to_wkb_list(self, bbox=None, feature_type="point", field=1): ok = libvect.Vect_cat_get( ctypes.byref(line_c), field, ctypes.byref(cat) ) - if ok < 1: - pcat = None - else: - pcat = cat.value + pcat = None if ok < 1 else cat.value wkb_list.append((f_id, pcat, ctypes.string_at(barray, size.value))) libgis.G_free(barray) diff --git a/python/grass/pygrass/vector/geometry.py b/python/grass/pygrass/vector/geometry.py index 10ecb1d2d13..b734161d1ea 100644 --- a/python/grass/pygrass/vector/geometry.py +++ b/python/grass/pygrass/vector/geometry.py @@ -810,10 +810,7 @@ def extend(self, line, forward=True): """ # set direction - if forward: - direction = libvect.GV_FORWARD - else: - direction = libvect.GV_BACKWARD + direction = libvect.GV_FORWARD if forward else libvect.GV_BACKWARD # check if is a Line object if isinstance(line, Line): c_points = line.c_points diff --git a/python/grass/pygrass/vector/table.py b/python/grass/pygrass/vector/table.py index 9c8d0c3f9a2..5442d2f1e51 100644 --- a/python/grass/pygrass/vector/table.py +++ b/python/grass/pygrass/vector/table.py @@ -1261,10 +1261,7 @@ def create(self, cols, name=None, overwrite=False, cursor=None): """ cur = cursor or self.conn.cursor() coldef = ",\n".join(["%s %s" % col for col in cols]) - if name: - newname = name - else: - newname = self.name + newname = name or self.name try: cur.execute(sql.CREATE_TAB.format(tname=newname, coldef=coldef)) self.conn.commit() diff --git a/python/grass/script/core.py b/python/grass/script/core.py index 4f70f525133..0b51e7fdbe9 100644 --- a/python/grass/script/core.py +++ b/python/grass/script/core.py @@ -334,10 +334,7 @@ def get_module_and_code(args, kwargs): args = make_command(*args, **kwargs) # Since we are in error handler, let's be extra cautious # about an empty command. - if args: - module = args[0] - else: - module = None + module = args[0] if args else None code = " ".join(args) return module, code @@ -1699,10 +1696,7 @@ def mapsets(search_path=False, env=None): :return: list of mapsets """ - if search_path: - flags = "p" - else: - flags = "l" + flags = "p" if search_path else "l" mapsets = read_command("g.mapsets", flags=flags, sep="newline", quiet=True, env=env) if not mapsets: fatal(_("Unable to list mapsets")) @@ -2033,10 +2027,7 @@ def create_environment(gisdbase, location, mapset, env=None): f.write("GISDBASE: {g}\n".format(g=gisdbase)) f.write("LOCATION_NAME: {l}\n".format(l=location)) f.write("GUI: text\n") - if env: - env = env.copy() - else: - env = os.environ.copy() + env = env.copy() if env else os.environ.copy() env["GISRC"] = f.name # remove mapset-specific env vars env = sanitize_mapset_environment(env) diff --git a/python/grass/script/raster.py b/python/grass/script/raster.py index 54c1219dbaf..d82780d7c5c 100644 --- a/python/grass/script/raster.py +++ b/python/grass/script/raster.py @@ -218,10 +218,7 @@ def raster_what(map, coord, env=None, localized=False): query :param env: """ - if isinstance(map, (bytes, str)): - map_list = [map] - else: - map_list = map + map_list = [map] if isinstance(map, (bytes, str)) else map coord_list = [] if isinstance(coord, tuple): diff --git a/python/grass/script/task.py b/python/grass/script/task.py index cf398c59214..9867b3d8c49 100644 --- a/python/grass/script/task.py +++ b/python/grass/script/task.py @@ -345,14 +345,8 @@ def _process_params(self): for ki in node_key_desc.findall("item"): key_desc.append(ki.text) - if p.get("multiple", "no") == "yes": - multiple = True - else: - multiple = False - if p.get("required", "no") == "yes": - required = True - else: - required = False + multiple = p.get("multiple", "no") == "yes" + required = p.get("required", "no") == "yes" if ( self.task.blackList["enabled"] diff --git a/python/grass/script/testsuite/data/script_using_temporary_region.py b/python/grass/script/testsuite/data/script_using_temporary_region.py index 5bb5a4cda26..839bf3d0a72 100755 --- a/python/grass/script/testsuite/data/script_using_temporary_region.py +++ b/python/grass/script/testsuite/data/script_using_temporary_region.py @@ -65,15 +65,9 @@ def main(): argument = sys.argv[1] sizes = argument.split(",", 1) size = sizes[0] - if len(sizes) > 1: - remaining = sizes[1] - else: - remaining = None + remaining = sizes[1] if len(sizes) > 1 else None nesting = int(sys.argv[2]) - if len(sys.argv) == 4: - map_name = sys.argv[3] - else: - map_name = None + map_name = sys.argv[3] if len(sys.argv) == 4 else None call_use_temp_region( script=this_file, size=size, diff --git a/python/grass/script/utils.py b/python/grass/script/utils.py index 448289b57aa..25eb70190c5 100644 --- a/python/grass/script/utils.py +++ b/python/grass/script/utils.py @@ -190,10 +190,7 @@ def decode(bytes_, encoding=None): if isinstance(bytes_, str): return bytes_ if isinstance(bytes_, bytes): - if encoding is None: - enc = _get_encoding() - else: - enc = encoding + enc = _get_encoding() if encoding is None else encoding return bytes_.decode(enc) # only text should be used raise TypeError("can only accept types str and bytes") @@ -221,10 +218,7 @@ def encode(string, encoding=None): if isinstance(string, bytes): return string if isinstance(string, str): - if encoding is None: - enc = _get_encoding() - else: - enc = encoding + enc = _get_encoding() if encoding is None else encoding return string.encode(enc) # if something else than text raise TypeError("can only accept types str and bytes") @@ -276,10 +270,7 @@ def parse_key_val(s, sep="=", dflt=None, val_type=None, vsep=None): for line in lines: kv = line.split(sep, 1) k = decode(kv[0].strip()) - if len(kv) > 1: - v = decode(kv[1].strip()) - else: - v = dflt + v = decode(kv[1].strip()) if len(kv) > 1 else dflt if val_type: result[k] = val_type(v) @@ -353,10 +344,7 @@ def convert(text): return int(text) if text.isdigit() else text.lower() def alphanum_key(actual_key): - if key: - sort_key = key(actual_key) - else: - sort_key = actual_key + sort_key = key(actual_key) if key else actual_key return [convert(c) for c in re.split("([0-9]+)", sort_key)] items.sort(key=alphanum_key) diff --git a/python/grass/script/vector.py b/python/grass/script/vector.py index ca3caf18471..2d484f7d590 100644 --- a/python/grass/script/vector.py +++ b/python/grass/script/vector.py @@ -129,10 +129,7 @@ def vector_columns(map, layer=None, getDict=True, env=None, **kwargs): s = read_command( "v.info", flags="c", map=map, layer=layer, quiet=True, env=env, **kwargs ) - if getDict: - result = {} - else: - result = [] + result = {} if getDict else [] i = 0 for line in s.splitlines(): ctype, cname = line.split("|") @@ -377,10 +374,7 @@ def vector_what( if "LC_ALL" in env: env["LC_ALL"] = "C" - if isinstance(map, (bytes, str)): - map_list = [map] - else: - map_list = map + map_list = [map] if isinstance(map, (bytes, str)) else map if layer: if isinstance(layer, (tuple, list)): diff --git a/python/grass/temporal/abstract_map_dataset.py b/python/grass/temporal/abstract_map_dataset.py index ecbf761d139..f6bec2bfef3 100644 --- a/python/grass/temporal/abstract_map_dataset.py +++ b/python/grass/temporal/abstract_map_dataset.py @@ -820,10 +820,7 @@ def temporal_buffer(self, increment, update=False, dbif=None): else: start, end, unit = self.get_relative_time() new_start = start - increment - if end is None: - new_end = start + increment - else: - new_end = end + increment + new_end = start + increment if end is None else end + increment if update: self.update_relative_time(new_start, new_end, unit, dbif=dbif) diff --git a/python/grass/temporal/abstract_space_time_dataset.py b/python/grass/temporal/abstract_space_time_dataset.py index d17baaab6ae..8140a2da261 100644 --- a/python/grass/temporal/abstract_space_time_dataset.py +++ b/python/grass/temporal/abstract_space_time_dataset.py @@ -779,10 +779,7 @@ def sample_by_dataset(self, stds, method=None, spatial=False, dbif=None): # print(relations) tb = SpatioTemporalTopologyBuilder() - if spatial: - spatial = "2D" - else: - spatial = None + spatial = "2D" if spatial else None mapsA = self.get_registered_maps_as_objects(dbif=dbif) mapsB = stds.get_registered_maps_as_objects_with_gaps(dbif=dbif) @@ -1429,10 +1426,7 @@ def get_registered_maps_as_objects_with_gaps( start1, end1 = maps[i].get_temporal_extent_as_tuple() start2, end2 = maps[i + 1].get_temporal_extent_as_tuple() end = start2 - if end1 is not None: - start = end1 - else: - start = start1 + start = end1 if end1 is not None else start1 map = self.get_new_map_instance(None) diff --git a/python/grass/temporal/c_libraries_interface.py b/python/grass/temporal/c_libraries_interface.py index 85a19a3ec6e..0d24fc0f732 100644 --- a/python/grass/temporal/c_libraries_interface.py +++ b/python/grass/temporal/c_libraries_interface.py @@ -256,11 +256,7 @@ def _get_driver_name(lock, conn, data): :returns: Name of the driver or None if no temporal database present """ mapset = data[1] - if not mapset: - mapset = libgis.G_mapset() - else: - mapset = encode(mapset) - + mapset = libgis.G_mapset() if not mapset else encode(mapset) drstring = libtgis.tgis_get_mapset_driver_name(mapset) conn.send(decode(drstring.data)) @@ -280,10 +276,7 @@ def _get_database_name(lock, conn, data): dbstring = None try: mapset = data[1] - if not mapset: - mapset = libgis.G_mapset() - else: - mapset = encode(mapset) + mapset = libgis.G_mapset() if not mapset else encode(mapset) dbstring = libtgis.tgis_get_mapset_database_name(mapset) dbstring = dbstring.data diff --git a/python/grass/temporal/datetime_math.py b/python/grass/temporal/datetime_math.py index 1031582fe23..b986637162a 100644 --- a/python/grass/temporal/datetime_math.py +++ b/python/grass/temporal/datetime_math.py @@ -694,10 +694,7 @@ def compute_datetime_delta(start, end): else: d += 24 * 60 * day_diff elif d == 0: - if comp["hour"]: - d = 60 * comp["hour"] - else: - d = 24 * 60 * day_diff + d = 60 * comp["hour"] if comp["hour"] else 24 * 60 * day_diff comp["minute"] = d @@ -914,10 +911,7 @@ def datetime_to_grass_datetime_string(dt): # Check for time zone info in the datetime object if dt.tzinfo is not None: tz = dt.tzinfo.utcoffset(0) - if tz.seconds > 86400 / 2: - tz = (tz.seconds - 86400) / 60 - else: - tz = tz.seconds / 60 + tz = (tz.seconds - 86400) / 60 if tz.seconds > 86400 / 2 else tz.seconds / 60 string = "%.2i %s %.2i %.2i:%.2i:%.2i %+.4i" % ( dt.day, @@ -999,10 +993,7 @@ def create_numeric_suffix(base, count, zeros): if len(spli) == 2: suff = spli[1] if suff.isdigit(): - if int(suff[0]) == 0: - zero = suff - else: - zero = "0{nu}".format(nu=suff) + zero = suff if int(suff[0]) == 0 else "0{nu}".format(nu=suff) else: zero = "05" else: diff --git a/python/grass/temporal/gui_support.py b/python/grass/temporal/gui_support.py index 41f22edc6b7..d2cbca13d68 100644 --- a/python/grass/temporal/gui_support.py +++ b/python/grass/temporal/gui_support.py @@ -37,16 +37,14 @@ def tlist_grouped(type, group_type=False, dbif=None): :return: directory of mapsets/elements """ result = {} + _type = type dbif, connection_state_changed = init_dbif(dbif) mapset = None - if type == "stds": - types = ["strds", "str3ds", "stvds"] - else: - types = [type] - for type in types: + types = ["strds", "str3ds", "stvds"] if _type == "stds" else [_type] + for _type in types: try: - tlist_result = tlist(type=type, dbif=dbif) + tlist_result = tlist(type=_type, dbif=dbif) except gs.ScriptError as e: gs.warning(e) continue @@ -65,10 +63,10 @@ def tlist_grouped(type, group_type=False, dbif=None): result[mapset] = [] if group_type: - if type in result[mapset]: - result[mapset][type].append(name) + if _type in result[mapset]: + result[mapset][_type].append(name) else: - result[mapset][type] = [ + result[mapset][_type] = [ name, ] else: @@ -90,19 +88,20 @@ def tlist(type, dbif=None): :return: a list of space time dataset ids """ + _type = type id = None - sp = dataset_factory(type, id) + sp = dataset_factory(_type, id) dbif, connection_state_changed = init_dbif(dbif) mapsets = get_available_temporal_mapsets() output = [] temporal_type = ["absolute", "relative"] - for type in temporal_type: + for _type in temporal_type: # For each available mapset for mapset in mapsets.keys(): # Table name - if type == "absolute": + if _type == "absolute": table = sp.get_type() + "_view_abs_time" else: table = sp.get_type() + "_view_rel_time" diff --git a/python/grass/temporal/list_stds.py b/python/grass/temporal/list_stds.py index 9f6b6867766..22a2708123d 100644 --- a/python/grass/temporal/list_stds.py +++ b/python/grass/temporal/list_stds.py @@ -325,10 +325,7 @@ def _get_get_registered_maps_as_objects_delta_gran( msgr.fatal(_("Empty entry in map list, this should not happen")) start, end = map_object.get_temporal_extent_as_tuple() - if end: - delta = end - start - else: - delta = None + delta = end - start if end else None delta_first = start - first_time if map_object.is_time_absolute(): diff --git a/python/grass/temporal/register.py b/python/grass/temporal/register.py index 92be36a3547..d8705765d02 100644 --- a/python/grass/temporal/register.py +++ b/python/grass/temporal/register.py @@ -158,10 +158,7 @@ def register_maps_in_space_time_dataset( # Read the map list from file if file: - if hasattr(file, "readline"): - fd = file - else: - fd = open(file) + fd = file if hasattr(file, "readline") else open(file) line = True while True: @@ -639,10 +636,7 @@ def register_map_object_list( string = f"{id}|{start}|{end}\n" register_file.write(string) - if output_stds: - output_stds_id = output_stds.get_id() - else: - output_stds_id = None + output_stds_id = output_stds.get_id() if output_stds else None register_maps_in_space_time_dataset( type, output_stds_id, unit=unit, file=filename, dbif=dbif diff --git a/python/grass/temporal/sampling.py b/python/grass/temporal/sampling.py index 425ca484da5..5209ce6a80c 100644 --- a/python/grass/temporal/sampling.py +++ b/python/grass/temporal/sampling.py @@ -82,19 +82,11 @@ def sample_stds_by_stds_topology( sts = [] for input in inputs: - if input.find("@") >= 0: - id = input - else: - id = input + "@" + mapset - + id = input if input.find("@") >= 0 else input + "@" + mapset st = dataset_factory(intype, id) sts.append(st) - if sampler.find("@") >= 0: - sid = sampler - else: - sid = sampler + "@" + mapset - + sid = sampler if sampler.find("@") >= 0 else sampler + "@" + mapset sst = dataset_factory(sampletype, sid) dbif = SQLDatabaseInterfaceConnection() @@ -156,10 +148,7 @@ def sample_stds_by_stds_topology( map = entry["granule"] start, end = map.get_temporal_extent_as_tuple() - if end: - delta = end - start - else: - delta = None + delta = end - start if end else None delta_first = start - first_time if map.is_time_absolute(): diff --git a/python/grass/temporal/temporal_algebra.py b/python/grass/temporal/temporal_algebra.py index e4717eb3326..2429b61e35e 100644 --- a/python/grass/temporal/temporal_algebra.py +++ b/python/grass/temporal/temporal_algebra.py @@ -992,10 +992,7 @@ def generate_map_name(self): same object for map name generation in multiple threads. """ self.count += 1 - if self.pid is not None: - pid = self.pid - else: - pid = os.getpid() + pid = self.pid if self.pid is not None else os.getpid() name = "tmp_map_name_%i_%i" % (pid, self.count) self.names[name] = name return name @@ -1234,10 +1231,7 @@ def check_stds(self, input, clear=False, stds_type=None, check_type=True): """ if isinstance(input, str): # Check for mapset in given stds input. - if input.find("@") >= 0: - id_input = input - else: - id_input = input + "@" + self.mapset + id_input = input if input.find("@") >= 0 else input + "@" + self.mapset # Create empty spacetime dataset. if stds_type: stds = dataset_factory(stds_type, id_input) @@ -1634,7 +1628,7 @@ def build_spatio_temporal_topology_list( def assign_bool_value( self, map_i, temporal_topo_list=["EQUAL"], spatial_topo_list=[] - ): + ) -> bool: """Function to assign boolean map value based on the map_values from the compared map list by topological relationships. @@ -1668,10 +1662,7 @@ def assign_bool_value( str(relationmap.get_temporal_extent_as_tuple()) + str(boolean), ) - if all(condition_value_list): - resultbool = True - else: - resultbool = False + resultbool = bool(all(condition_value_list)) map_i.condition_value = [resultbool] return resultbool @@ -2296,20 +2287,14 @@ def recurse_compare(conditionlist): ele_index = conditionlist.index(ele) right = conditionlist.pop(ele_index) left = conditionlist.pop(ele_index - 2) - if any([left, right]): - result = True - else: - result = False + result = bool(any([left, right])) conditionlist[ele_index - 2] = result recurse_compare(conditionlist) if ele == "&&": ele_index = conditionlist.index(ele) right = conditionlist.pop(ele_index) left = conditionlist.pop(ele_index - 2) - if all([left, right]): - result = True - else: - result = False + result = bool(all([left, right])) conditionlist[ele_index - 2] = result recurse_compare(conditionlist) @@ -2643,10 +2628,7 @@ def p_expr_tmap_function(self, t): input = t[3] if not isinstance(input, list): # Check for mapset in given stds input. - if input.find("@") >= 0: - id_input = input - else: - id_input = input + "@" + self.mapset + id_input = input if input.find("@") >= 0 else input + "@" + self.mapset # Create empty map dataset. map_i = dataset_factory(self.maptype, id_input) # Check for occurrence of space time dataset. @@ -3079,10 +3061,7 @@ def p_expr_t_select_operator(self, t): # Evaluate temporal operator. operators = self.eval_toperator(t[2], optype="select") # Check for negative selection. - if operators[2] == "!:": - negation = True - else: - negation = False + negation = operators[2] == "!:" # Perform selection. selectlist = self.perform_temporal_selection( maplistA, maplistB, topolist=operators[0], inverse=negation diff --git a/python/grass/temporal/temporal_granularity.py b/python/grass/temporal/temporal_granularity.py index b4043054520..173e4e715ea 100644 --- a/python/grass/temporal/temporal_granularity.py +++ b/python/grass/temporal/temporal_granularity.py @@ -494,7 +494,7 @@ def compute_absolute_time_granularity(maps): # Keep the temporal extent to compare to the following/next map previous_start, previous_end = start, end - # Create a list with a single time unit only + # Create a set with a single time unit only dlist = set() assigned_time_unit = None time_unit_multipliers = { @@ -529,11 +529,8 @@ def compute_absolute_time_granularity(maps): if not dlist: return None - if len(dlist) > 1: - # Find greatest common divisor - granularity = gcd_list(dlist) - else: - granularity = dlist.pop() + # Find greatest common divisor to get a single time unit + granularity = gcd_list(dlist) if len(dlist) > 1 else dlist.pop() if granularity is None: return None diff --git a/python/grass/temporal/temporal_raster_base_algebra.py b/python/grass/temporal/temporal_raster_base_algebra.py index 8ff156cbed5..cb2122f4a37 100644 --- a/python/grass/temporal/temporal_raster_base_algebra.py +++ b/python/grass/temporal/temporal_raster_base_algebra.py @@ -960,10 +960,7 @@ def p_expr_spmap_function(self, t): input = t[3] if not isinstance(input, list): # Check for mapset in given stds input. - if input.find("@") >= 0: - id_input = input - else: - id_input = input + "@" + self.mapset + id_input = input if input.find("@") >= 0 else input + "@" + self.mapset # Create empty map dataset. map_i = dataset_factory(self.maptype, id_input) # Check for occurrence of space time dataset. diff --git a/python/grass/temporal/univar_statistics.py b/python/grass/temporal/univar_statistics.py index dd334e7e9f1..b6a9c1ac508 100755 --- a/python/grass/temporal/univar_statistics.py +++ b/python/grass/temporal/univar_statistics.py @@ -304,10 +304,7 @@ def print_vector_dataset_univar_statistics( mapset = get_current_mapset() - if input.find("@") >= 0: - id = input - else: - id = input + "@" + mapset + id = input if input.find("@") >= 0 else input + "@" + mapset sp = dataset_factory("stvds", id) diff --git a/scripts/d.rast.edit/d.rast.edit.py b/scripts/d.rast.edit/d.rast.edit.py index ea6cc1e46bf..3464941e825 100755 --- a/scripts/d.rast.edit/d.rast.edit.py +++ b/scripts/d.rast.edit/d.rast.edit.py @@ -295,10 +295,7 @@ def paint_cell(self, dc, r, c): px, py = -dy, dx r, g, b, a = wx.Colour(fill).Get() - if r + g + b > 384: - line = "black" - else: - line = "white" + line = "black" if r + g + b > 384 else "white" dc.SetPen(wx.Pen(line)) dc.DrawLine(x0, y0, x1, y1) diff --git a/scripts/d.rast.leg/d.rast.leg.py b/scripts/d.rast.leg/d.rast.leg.py index 85cb423f9e9..74576113577 100755 --- a/scripts/d.rast.leg/d.rast.leg.py +++ b/scripts/d.rast.leg/d.rast.leg.py @@ -132,16 +132,10 @@ def main(): if not nlines: nlines = None - if rast: - lmap = rast - else: - lmap = map + lmap = rast or map kv = gs.raster_info(map=lmap) - if kv["datatype"] == "CELL": - leg_at = None - else: - leg_at = "%f,95,5,10" % VSpacing + leg_at = None if kv["datatype"] == "CELL" else "%f,95,5,10" % VSpacing # checking for histogram causes more problems than it solves # histfiledir = grass.find_file(lmap, 'cell_misc')['file'] diff --git a/scripts/db.droptable/db.droptable.py b/scripts/db.droptable/db.droptable.py index 571638748cf..39408221acc 100755 --- a/scripts/db.droptable/db.droptable.py +++ b/scripts/db.droptable/db.droptable.py @@ -56,14 +56,8 @@ def main(): gs.run_command("db.connect", flags="c", quiet=True) kv = gs.db_connection() - if options["database"]: - database = options["database"] - else: - database = kv["database"] - if options["driver"]: - driver = options["driver"] - else: - driver = kv["driver"] + database = options["database"] or kv["database"] + driver = options["driver"] or kv["driver"] # schema needed for PG? if force: diff --git a/scripts/db.out.ogr/db.out.ogr.py b/scripts/db.out.ogr/db.out.ogr.py index 4a840755b20..4fdfed23648 100755 --- a/scripts/db.out.ogr/db.out.ogr.py +++ b/scripts/db.out.ogr/db.out.ogr.py @@ -72,10 +72,7 @@ def main(): if format.lower() == "dbf": format = "ESRI_Shapefile" - if format.lower() == "csv": - olayer = basename(output, "csv") - else: - olayer = None + olayer = basename(output, "csv") if format.lower() == "csv" else None # is there a simpler way of testing for --overwrite? dbffile = input + ".dbf" diff --git a/scripts/db.univar/db.univar.py b/scripts/db.univar/db.univar.py index 97dfbdb7dbe..4654e3011a0 100755 --- a/scripts/db.univar/db.univar.py +++ b/scripts/db.univar/db.univar.py @@ -118,10 +118,7 @@ def main(): perc = [float(p) for p in perc.split(",")] if not output_format: - if shellstyle: - output_format = "shell" - else: - output_format = "plain" + output_format = "shell" if shellstyle else "plain" elif shellstyle: # This can be a message or warning in future versions. # In version 9, -g may be removed. diff --git a/scripts/g.extension/g.extension.py b/scripts/g.extension/g.extension.py index 194f498d49c..f14a742ca06 100644 --- a/scripts/g.extension/g.extension.py +++ b/scripts/g.extension/g.extension.py @@ -1608,10 +1608,7 @@ def install_extension_win(name): source, url = resolve_source_code(url="{0}/{1}.zip".format(base_url, name)) # to hide non-error messages from subprocesses - if gs.verbosity() <= 2: - outdev = open(os.devnull, "w") - else: - outdev = sys.stdout + outdev = open(os.devnull, "w") if gs.verbosity() <= 2 else sys.stdout # download Addons ZIP file os.chdir(TMPDIR) # this is just to not leave something behind @@ -1961,10 +1958,7 @@ def install_extension_std_platforms(name, source, url, branch): path_to_src_code_message = _("Path to the source code:") # to hide non-error messages from subprocesses - if gs.verbosity() <= 2: - outdev = open(os.devnull, "w") - else: - outdev = sys.stdout + outdev = open(os.devnull, "w") if gs.verbosity() <= 2 else sys.stdout os.chdir(TMPDIR) # this is just to not leave something behind srcdir = os.path.join(TMPDIR, name) @@ -2578,10 +2572,7 @@ def resolve_known_host_service(url, name, branch): ) return None, None if match: - if not actual_start: - actual_start = match["url_start"] - else: - actual_start = "" + actual_start = match["url_start"] if not actual_start else "" if "branch" in match["url_end"]: suffix = match["url_end"].format( name=name, diff --git a/scripts/g.manual/g.manual.py b/scripts/g.manual/g.manual.py index f4ecf961076..345a6d1714a 100755 --- a/scripts/g.manual/g.manual.py +++ b/scripts/g.manual/g.manual.py @@ -132,10 +132,7 @@ def main(): elif flags["t"]: special = "topics" - if flags["m"]: - start = start_man - else: - start = start_browser + start = start_man if flags["m"] else start_browser entry = options["entry"] gisbase = os.environ["GISBASE"] diff --git a/scripts/i.in.spotvgt/i.in.spotvgt.py b/scripts/i.in.spotvgt/i.in.spotvgt.py index cd744d4c252..666f3721761 100755 --- a/scripts/i.in.spotvgt/i.in.spotvgt.py +++ b/scripts/i.in.spotvgt/i.in.spotvgt.py @@ -136,11 +136,7 @@ def main(): spotdir = os.path.dirname(infile) spotname = gs.basename(infile, "hdf") - - if rast: - name = rast - else: - name = spotname + name = rast or spotname if not gs.overwrite() and gs.find_file(name)["file"]: gs.fatal(_("<%s> already exists. Aborting.") % name) diff --git a/scripts/i.oif/i.oif.py b/scripts/i.oif/i.oif.py index 0dabbe7cb38..cb156675927 100755 --- a/scripts/i.oif/i.oif.py +++ b/scripts/i.oif/i.oif.py @@ -90,10 +90,7 @@ def main(): stddev[band] = float(kv["stddev"]) else: # run all bands in parallel - if "WORKERS" in os.environ: - workers = int(os.environ["WORKERS"]) - else: - workers = len(bands) + workers = int(os.environ["WORKERS"]) if "WORKERS" in os.environ else len(bands) proc = {} pout = {} @@ -142,10 +139,7 @@ def main(): _("The Optimum Index Factor analysis result (best combination shown first):") ) - if shell: - fmt = "%s,%s,%s:%.4f\n" - else: - fmt = "%s, %s, %s: %.4f\n" + fmt = "%s,%s,%s:%.4f\n" if shell else "%s, %s, %s: %.4f\n" if not output or output == "-": for v, p in oif: diff --git a/scripts/i.spectral/i.spectral.py b/scripts/i.spectral/i.spectral.py index 171eac7359c..07efd8af31f 100755 --- a/scripts/i.spectral/i.spectral.py +++ b/scripts/i.spectral/i.spectral.py @@ -136,10 +136,7 @@ def draw_gnuplot(what, xlabels, output, img_format, coord_legend): cmd = [] for i, row in enumerate(what): - if not coord_legend: - title = "Pick " + str(i + 1) - else: - title = str(tuple(row[0:2])) + title = "Pick " + str(i + 1) if not coord_legend else str(tuple(row[0:2])) x_datafile = os.path.join(tmp_dir, "data_%d" % i) cmd.append(" '%s' title '%s'" % (x_datafile, title)) diff --git a/scripts/m.proj/m.proj.py b/scripts/m.proj/m.proj.py index 4540b3dcd0a..a018606fff9 100755 --- a/scripts/m.proj/m.proj.py +++ b/scripts/m.proj/m.proj.py @@ -236,14 +236,8 @@ def main(): gcore.debug("output file=[%s]" % outfile) # set up output style - if not decimal: - outfmt = ["-w5"] - else: - outfmt = ["-f", "%.8f"] - if not copy_input: - copyinp = [] - else: - copyinp = ["-E"] + outfmt = ["-w5"] if not decimal else ["-f", "%.8f"] + copyinp = [] if not copy_input else ["-E"] # do the conversion # Convert cs2cs DMS format to GRASS DMS format: diff --git a/scripts/r.buffer.lowmem/r.buffer.lowmem.py b/scripts/r.buffer.lowmem/r.buffer.lowmem.py index 2916ad95e7d..8866c21faeb 100755 --- a/scripts/r.buffer.lowmem/r.buffer.lowmem.py +++ b/scripts/r.buffer.lowmem/r.buffer.lowmem.py @@ -92,10 +92,7 @@ def main(): s = gs.read_command("g.proj", flags="j") kv = gs.parse_key_val(s) - if kv["+proj"] == "longlat": - metric = "geodesic" - else: - metric = "squared" + metric = "geodesic" if kv["+proj"] == "longlat" else "squared" gs.run_command( "r.grow.distance", input=input, metric=metric, distance=temp_dist, flags="m" diff --git a/scripts/r.in.aster/r.in.aster.py b/scripts/r.in.aster/r.in.aster.py index 24722293dde..626d27e7976 100755 --- a/scripts/r.in.aster/r.in.aster.py +++ b/scripts/r.in.aster/r.in.aster.py @@ -152,10 +152,7 @@ def main(): # Band 3b is not included ASTER L1T if proctype == "L1T": allbands.remove("3b") - if band == "all": - bandlist = allbands - else: - bandlist = band.split(",") + bandlist = allbands if band == "all" else band.split(",") # initialize datasets for L1A, L1B, L1T if proctype in {"L1A", "L1B", "L1T"}: diff --git a/scripts/r.in.srtm/r.in.srtm.py b/scripts/r.in.srtm/r.in.srtm.py index 1e4c585ac02..b9f51415ca0 100755 --- a/scripts/r.in.srtm/r.in.srtm.py +++ b/scripts/r.in.srtm/r.in.srtm.py @@ -177,10 +177,7 @@ def main(): infile = infile[:-4] (fdir, tile) = os.path.split(infile) - if not output: - tileout = tile - else: - tileout = output + tileout = output or tile if ".hgt" in input: suff = ".hgt" diff --git a/scripts/r.in.wms/wms_cap_parsers.py b/scripts/r.in.wms/wms_cap_parsers.py index 56b1d0ce48c..723a63f0b23 100644 --- a/scripts/r.in.wms/wms_cap_parsers.py +++ b/scripts/r.in.wms/wms_cap_parsers.py @@ -501,10 +501,7 @@ def _find(self, etreeElement, tag, ns=None): """!Find child element. If the element is not found it raises xml.etree.ElementTree.ParseError. """ - if not ns: - res = etreeElement.find(tag) - else: - res = etreeElement.find(ns(tag)) + res = etreeElement.find(tag) if not ns else etreeElement.find(ns(tag)) if res is None: raise ParseError( @@ -521,10 +518,7 @@ def _findall(self, etreeElement, tag, ns=None): """!Find all children element. If no element is found it raises xml.etree.ElementTree.ParseError. """ - if not ns: - res = etreeElement.findall(tag) - else: - res = etreeElement.findall(ns(tag)) + res = etreeElement.findall(tag) if not ns else etreeElement.findall(ns(tag)) if not res: raise ParseError( diff --git a/scripts/r.in.wms/wms_drv.py b/scripts/r.in.wms/wms_drv.py index 531112a1a6c..e1131918c4a 100644 --- a/scripts/r.in.wms/wms_drv.py +++ b/scripts/r.in.wms/wms_drv.py @@ -995,10 +995,7 @@ def _parseTilePattern(self, group_t_patts, bbox, region): res["y"] = (bbox["maxy"] - bbox["miny"]) / region["rows"] res["x"] = (bbox["maxx"] - bbox["minx"]) / region["cols"] - if res["x"] < res["y"]: - comp_res = "x" - else: - comp_res = "y" + comp_res = "x" if res["x"] < res["y"] else "y" t_res = {} best_patt = None diff --git a/scripts/r.out.xyz/r.out.xyz.py b/scripts/r.out.xyz/r.out.xyz.py index ae8e04de4d6..6df97f3c202 100755 --- a/scripts/r.out.xyz/r.out.xyz.py +++ b/scripts/r.out.xyz/r.out.xyz.py @@ -48,10 +48,7 @@ def main(): output = options["output"] donodata = flags["i"] - if donodata: - statsflags = "1g" - else: - statsflags = "1gn" + statsflags = "1g" if donodata else "1gn" parameters = { "flags": statsflags, "input": options["input"], diff --git a/scripts/r.reclass.area/r.reclass.area.py b/scripts/r.reclass.area/r.reclass.area.py index 5d6fc1292bc..01e474659bc 100755 --- a/scripts/r.reclass.area/r.reclass.area.py +++ b/scripts/r.reclass.area/r.reclass.area.py @@ -152,10 +152,7 @@ def reclass(inf, outf, lim, clump, diag, les): if len(f) < 5: continue hectares = float(f[4]) * 0.0001 - if lesser: - test = hectares <= limit - else: - test = hectares >= limit + test = hectares <= limit if lesser else hectares >= limit if test: rules += "%s = %s %s\n" % (f[0], f[2], f[3]) if rules: diff --git a/scripts/r.unpack/r.unpack.py b/scripts/r.unpack/r.unpack.py index 575d465ebb5..dd595b4722b 100644 --- a/scripts/r.unpack/r.unpack.py +++ b/scripts/r.unpack/r.unpack.py @@ -92,10 +92,7 @@ def main(): return 0 - if options["output"]: - map_name = options["output"] - else: - map_name = data_names[0].split("@")[0] + map_name = options["output"] or data_names[0].split("@")[0] gfile = grass.find_file(name=map_name, element="cell", mapset=".") if gfile["file"]: diff --git a/scripts/r3.in.xyz/r3.in.xyz.py b/scripts/r3.in.xyz/r3.in.xyz.py index b17d59506b7..5b2c8be4a65 100755 --- a/scripts/r3.in.xyz/r3.in.xyz.py +++ b/scripts/r3.in.xyz/r3.in.xyz.py @@ -226,10 +226,7 @@ def main(): addl_opts["flags"] = "i" if scan_only or shell_style: - if shell_style: - doShell = "g" - else: - doShell = "" + doShell = "g" if shell_style else "" grass.run_command( "r.in.xyz", flags="s" + doShell, @@ -243,10 +240,7 @@ def main(): ) sys.exit() - if dtype == "float": - data_type = "FCELL" - else: - data_type = "DCELL" + data_type = "FCELL" if dtype == "float" else "DCELL" region = grass.region(region3d=True) diff --git a/scripts/v.build.all/v.build.all.py b/scripts/v.build.all/v.build.all.py index 69b0837209f..8691ecddc32 100755 --- a/scripts/v.build.all/v.build.all.py +++ b/scripts/v.build.all/v.build.all.py @@ -31,10 +31,7 @@ def main(): vectors = grass.list_grouped("vect")[mapset] num_vectors = len(vectors) - if grass.verbosity() < 2: - quiet = True - else: - quiet = False + quiet = grass.verbosity() < 2 i = 1 for vect in vectors: diff --git a/scripts/v.db.reconnect.all/v.db.reconnect.all.py b/scripts/v.db.reconnect.all/v.db.reconnect.all.py index b42b8006673..9a9d46356a9 100755 --- a/scripts/v.db.reconnect.all/v.db.reconnect.all.py +++ b/scripts/v.db.reconnect.all/v.db.reconnect.all.py @@ -255,10 +255,7 @@ def main(): schema = "" table = schema_table - if new_schema: - new_schema_table = "%s.%s" % (new_schema, table) - else: - new_schema_table = table + new_schema_table = "%s.%s" % (new_schema, table) if new_schema else table gs.debug( "DATABASE = '%s' SCHEMA = '%s' TABLE = '%s' ->\n" diff --git a/scripts/v.dissolve/tests/conftest.py b/scripts/v.dissolve/tests/conftest.py index 9b069330488..e5e9a38818d 100644 --- a/scripts/v.dissolve/tests/conftest.py +++ b/scripts/v.dissolve/tests/conftest.py @@ -13,10 +13,7 @@ def updates_as_transaction(table, cat_column, column, column_quote, cats, values): """Create SQL statement for categories and values for a given column""" sql = ["BEGIN TRANSACTION"] - if column_quote: - quote = "'" - else: - quote = "" + quote = "'" if column_quote else "" for cat, value in zip(cats, values): sql.append( f"UPDATE {table} SET {column} = {quote}{value}{quote} " diff --git a/scripts/v.dissolve/tests/v_dissolve_aggregate_test.py b/scripts/v.dissolve/tests/v_dissolve_aggregate_test.py index 68ca178b864..90dc3de075e 100644 --- a/scripts/v.dissolve/tests/v_dissolve_aggregate_test.py +++ b/scripts/v.dissolve/tests/v_dissolve_aggregate_test.py @@ -101,10 +101,7 @@ def test_aggregate_column_result(dataset, backend): for stats_column in stats_columns: assert stats_column in columns column_info = columns[stats_column] - if stats_column.endswith("_n"): - correct_type = "integer" - else: - correct_type = "double precision" + correct_type = "integer" if stats_column.endswith("_n") else "double precision" assert ( columns[stats_column]["type"].lower() == correct_type ), f"{stats_column} has a wrong type" @@ -221,10 +218,7 @@ def test_sqlite_agg_accepted(dataset): for method, stats_column in zip(stats, expected_stats_columns): assert stats_column in columns column_info = columns[stats_column] - if method == "count": - correct_type = "integer" - else: - correct_type = "double precision" + correct_type = "integer" if method == "count" else "double precision" assert ( columns[stats_column]["type"].lower() == correct_type ), f"{stats_column} has a wrong type" diff --git a/scripts/v.dissolve/tests/v_dissolve_layers_test.py b/scripts/v.dissolve/tests/v_dissolve_layers_test.py index 702aa8f8496..f6986d35917 100644 --- a/scripts/v.dissolve/tests/v_dissolve_layers_test.py +++ b/scripts/v.dissolve/tests/v_dissolve_layers_test.py @@ -50,10 +50,7 @@ def test_layer_2(dataset_layer_2): for method, stats_column in zip(stats, expected_stats_columns): assert stats_column in columns column_info = columns[stats_column] - if method == "count": - correct_type = "integer" - else: - correct_type = "double precision" + correct_type = "integer" if method == "count" else "double precision" assert ( columns[stats_column]["type"].lower() == correct_type ), f"{stats_column} has a wrong type" diff --git a/scripts/v.in.e00/v.in.e00.py b/scripts/v.in.e00/v.in.e00.py index 4d63636199f..4258fa3181a 100755 --- a/scripts/v.in.e00/v.in.e00.py +++ b/scripts/v.in.e00/v.in.e00.py @@ -86,10 +86,7 @@ def main(): ) merging = True - if vect: - name = vect - else: - name = e00name + name = vect or e00name # do import diff --git a/scripts/v.in.lines/v.in.lines.py b/scripts/v.in.lines/v.in.lines.py index 44112b297a3..35991988f6b 100755 --- a/scripts/v.in.lines/v.in.lines.py +++ b/scripts/v.in.lines/v.in.lines.py @@ -50,12 +50,7 @@ def main(): fs = separator(options["separator"]) threeD = flags["z"] - - if threeD: - do3D = "z" - else: - do3D = "" - + do3D = "z" if threeD else "" tmp = grass.tempfile() # set up input file diff --git a/scripts/v.in.mapgen/v.in.mapgen.py b/scripts/v.in.mapgen/v.in.mapgen.py index 404fb7ec6b2..efe7af1075d 100755 --- a/scripts/v.in.mapgen/v.in.mapgen.py +++ b/scripts/v.in.mapgen/v.in.mapgen.py @@ -73,18 +73,12 @@ def main(): if not os.path.isfile(infile): grass.fatal(_("Input file <%s> not found") % infile) - if output: - name = output - else: - name = "" + name = output or "" if threeD: matlab = True - if threeD: - do3D = "z" - else: - do3D = "" + do3D = "z" if threeD else "" tmp = grass.tempfile() diff --git a/scripts/v.pack/v.pack.py b/scripts/v.pack/v.pack.py index 4468c888bcb..e39d52dbafe 100755 --- a/scripts/v.pack/v.pack.py +++ b/scripts/v.pack/v.pack.py @@ -73,10 +73,7 @@ def main(): infile = infile.split("@")[0] # output name - if options["output"]: - outfile = options["output"] - else: - outfile = infile + ".pack" + outfile = options["output"] or infile + ".pack" # check if exists the output file if os.path.exists(outfile): diff --git a/scripts/v.rast.stats/v.rast.stats.py b/scripts/v.rast.stats/v.rast.stats.py index 803fc8691ad..4096aa49caf 100644 --- a/scripts/v.rast.stats/v.rast.stats.py +++ b/scripts/v.rast.stats/v.rast.stats.py @@ -116,10 +116,7 @@ def main(): # Get mapset of the vector vs = vector.split("@") - if len(vs) > 1: - vect_mapset = vs[1] - else: - vect_mapset = mapset + vect_mapset = vs[1] if len(vs) > 1 else mapset # does map exist in CURRENT mapset? if vect_mapset != mapset or not gs.find_file(vector, "vector", mapset)["file"]: @@ -373,10 +370,7 @@ def set_up_columns(vector, layer, percentile, colprefix, basecols, dbfdriver, c) perc = b if perc: # namespace is limited in DBF but the % value is important - if dbfdriver: - perccol = "per" + percentile - else: - perccol = "percentile_" + percentile + perccol = "per" + percentile if dbfdriver else "percentile_" + percentile percindex = basecols.index(perc) basecols[percindex] = perccol @@ -424,10 +418,7 @@ def set_up_columns(vector, layer, percentile, colprefix, basecols, dbfdriver, c) + _("Use -c flag to update values in this column.") ) else: - if i == "n": - coltype = "INTEGER" - else: - coltype = "DOUBLE PRECISION" + coltype = "INTEGER" if i == "n" else "DOUBLE PRECISION" addcols.append(currcolumn + " " + coltype) if addcols: diff --git a/scripts/v.report/v.report.py b/scripts/v.report/v.report.py index cec84582155..29d9d3c914e 100755 --- a/scripts/v.report/v.report.py +++ b/scripts/v.report/v.report.py @@ -91,10 +91,7 @@ def main(): isConnection = False colnames = ["cat"] - if option == "coor": - extracolnames = ["x", "y", "z"] - else: - extracolnames = [option] + extracolnames = ["x", "y", "z"] if option == "coor" else [option] if units == "percent": unitsp = "meters" diff --git a/scripts/v.unpack/v.unpack.py b/scripts/v.unpack/v.unpack.py index ea75d443b57..c3a7a55deb7 100644 --- a/scripts/v.unpack/v.unpack.py +++ b/scripts/v.unpack/v.unpack.py @@ -93,10 +93,7 @@ def main(): return 0 # set the output name - if options["output"]: - map_name = options["output"] - else: - map_name = data_name + map_name = options["output"] or data_name # grass env gisenv = grass.gisenv() @@ -233,19 +230,13 @@ def main(): # for each old connection for t in dbnlist: # it split the line of each connection, to found layer number and key - if len(t.split("|")) != 1: - values = t.split("|") - else: - values = t.split(" ") + values = t.split("|") if len(t.split("|")) != 1 else t.split(" ") from_table = values[1] layer = values[0].split("/")[0] # we need to take care about the table name in case of several layer if options["output"]: - if len(dbnlist) > 1: - to_table = "%s_%s" % (map_name, layer) - else: - to_table = map_name + to_table = "%s_%s" % (map_name, layer) if len(dbnlist) > 1 else map_name else: to_table = from_table From af55fa7eaeeaabd3c4db99f4622e9849b71da80a Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Tue, 22 Oct 2024 10:20:23 -0400 Subject: [PATCH 429/471] d.rast.edit: Fixed E722 in d.rast.edit/ (#4568) --- .flake8 | 1 - scripts/d.rast.edit/d.rast.edit.py | 21 +-------------------- 2 files changed, 1 insertion(+), 21 deletions(-) diff --git a/.flake8 b/.flake8 index 8211d9e03d4..ffa7d162eda 100644 --- a/.flake8 +++ b/.flake8 @@ -108,7 +108,6 @@ per-file-ignores = scripts/i.pansharpen/i.pansharpen.py: E722, E501 scripts/r.in.srtm/r.in.srtm.py: E722 scripts/r.fillnulls/r.fillnulls.py: E722 - scripts/d.rast.edit/d.rast.edit.py: E722 scripts/v.what.strds/v.what.strds.py: E501 # Line too long (esp. module interface definitions) scripts/*/*.py: E501 diff --git a/scripts/d.rast.edit/d.rast.edit.py b/scripts/d.rast.edit/d.rast.edit.py index 3464941e825..102d7e447d8 100755 --- a/scripts/d.rast.edit/d.rast.edit.py +++ b/scripts/d.rast.edit/d.rast.edit.py @@ -644,28 +644,9 @@ def update_status(self, row, col): if self.angles: self.status["aspect"] = self.angles[row][col] - def force_color(self, val): - run("g.region", rows=1, cols=1) - run("r.mapcalc", expression="%s = %d" % (self.tempmap, val)) - run("r.colors", map=self.tempmap, rast=self.inmap) - run("r.out.ppm", input=self.tempmap, out=self.tempfile) - run("g.remove", flags="f", type="raster", name=self.tempmap) - - tempimg = wx.Image(self.tempfile) - gs.try_remove(self.tempfile) - - rgb = tempimg.get(0, 0) - color = "#%02x%02x%02x" % rgb - self.colors[val] = color - tempimg.delete() - def get_color(self, val): if val not in self.colors: - try: - self.force_color(val) - except: - self.colors[val] = "#ffffff" - + self.colors[val] = "#ffffff" return self.colors[val] def refresh_canvas(self): From c5c42cf440cd037976beb58eb9cd1b9cc23ccb3e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 23 Oct 2024 12:22:19 +0000 Subject: [PATCH 430/471] CI(deps): Update github/codeql-action action to v3.27.0 (#4575) --- .github/workflows/codeql-analysis.yml | 4 ++-- .github/workflows/python-code-quality.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 6672287794c..97f89578306 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -56,7 +56,7 @@ jobs: if: ${{ matrix.language == 'c-cpp' }} - name: Initialize CodeQL - uses: github/codeql-action/init@f779452ac5af1c261dce0346a8f964149f49322b # v3.26.13 + uses: github/codeql-action/init@662472033e021d55d94146f66f6058822b0b39fd # v3.27.0 with: languages: ${{ matrix.language }} config-file: ./.github/codeql/codeql-config.yml @@ -81,6 +81,6 @@ jobs: run: .github/workflows/build_ubuntu-22.04.sh "${HOME}/install" - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@f779452ac5af1c261dce0346a8f964149f49322b # v3.26.13 + uses: github/codeql-action/analyze@662472033e021d55d94146f66f6058822b0b39fd # v3.27.0 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index 9f71c28e922..15357764d6a 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -135,7 +135,7 @@ jobs: path: bandit.sarif - name: Upload SARIF File into Security Tab - uses: github/codeql-action/upload-sarif@f779452ac5af1c261dce0346a8f964149f49322b # v3.26.13 + uses: github/codeql-action/upload-sarif@662472033e021d55d94146f66f6058822b0b39fd # v3.27.0 with: sarif_file: bandit.sarif From 25757837f282466a31062335ef2d99d0879fb148 Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Wed, 23 Oct 2024 08:31:02 -0400 Subject: [PATCH 431/471] wxGUI: Fixed F841 in photo2image/ (#4573) * fixed 841 * fixed busyInfo --- .flake8 | 4 +- gui/wxpython/photo2image/g.gui.photo2image.py | 2 +- gui/wxpython/photo2image/ip2i_manager.py | 56 ++++++++----------- gui/wxpython/photo2image/ip2i_mapdisplay.py | 2 - 4 files changed, 27 insertions(+), 37 deletions(-) diff --git a/.flake8 b/.flake8 index ffa7d162eda..14224a97843 100644 --- a/.flake8 +++ b/.flake8 @@ -25,8 +25,8 @@ per-file-ignores = gui/scripts/d.wms.py: E501 gui/wxpython/image2target/g.gui.image2target.py: E501 gui/wxpython/nviz/*: E722 - gui/wxpython/photo2image/*: F841, E722, E265 - gui/wxpython/photo2image/g.gui.photo2image.py: E501, F841 + gui/wxpython/photo2image/*: E722 + gui/wxpython/photo2image/g.gui.photo2image.py: E501 gui/wxpython/psmap/*: F841, E266, E722 gui/wxpython/vdigit/*: F841, E722, F405, F403 gui/wxpython/animation/g.gui.animation.py: E501 diff --git a/gui/wxpython/photo2image/g.gui.photo2image.py b/gui/wxpython/photo2image/g.gui.photo2image.py index 65b4141873e..fe153b236cc 100755 --- a/gui/wxpython/photo2image/g.gui.photo2image.py +++ b/gui/wxpython/photo2image/g.gui.photo2image.py @@ -121,7 +121,7 @@ def main(): app = wx.App() - wizard = GCPWizard( + GCPWizard( parent=None, giface=StandaloneGrassInterface(), group=group, diff --git a/gui/wxpython/photo2image/ip2i_manager.py b/gui/wxpython/photo2image/ip2i_manager.py index 5a2a1f14f21..711ec4191af 100644 --- a/gui/wxpython/photo2image/ip2i_manager.py +++ b/gui/wxpython/photo2image/ip2i_manager.py @@ -1107,22 +1107,20 @@ def OnGeorect(self, event): else: flags = "a" - busy = wx.BusyInfo(_("Rectifying images, please wait..."), parent=self) - wx.GetApp().Yield() + with wx.BusyInfo(_("Rectifying images, please wait..."), parent=self): + wx.GetApp().Yield() - ret, msg = RunCommand( - "i.rectify", - parent=self, - getErrorMsg=True, - quiet=True, - group=self.xygroup, - extension=self.extension, - order=self.gr_order, - method=self.gr_method, - flags=flags, - ) - - del busy + ret, msg = RunCommand( + "i.rectify", + parent=self, + getErrorMsg=True, + quiet=True, + group=self.xygroup, + extension=self.extension, + order=self.gr_order, + method=self.gr_method, + flags=flags, + ) # provide feedback on failure if ret != 0: @@ -1130,21 +1128,19 @@ def OnGeorect(self, event): print(self.grwiz.src_map, file=sys.stderr) print(msg, file=sys.stderr) - busy = wx.BusyInfo( + with wx.BusyInfo( _("Writing output image to group, please wait..."), parent=self - ) - wx.GetApp().Yield() - - ret1, msg1 = RunCommand( - "i.group", - parent=self, - getErrorMsg=True, - quiet=False, - group=self.xygroup, - input="".join([self.grwiz.src_map.split("@")[0], self.extension]), - ) + ): + wx.GetApp().Yield() - del busy + ret1, msg1 = RunCommand( + "i.group", + parent=self, + getErrorMsg=True, + quiet=False, + group=self.xygroup, + input="".join([self.grwiz.src_map.split("@")[0], self.extension]), + ) if ret1 != 0: print("ip2i: Error in i.group", file=sys.stderr) @@ -1253,7 +1249,6 @@ def OnGROrder(self, event): elif self.gr_order == 2: minNumOfItems = 6 - diff = 6 - numOfItems # self.SetStatusText(_( # "Insufficient points, 6+ points needed for 2nd order")) @@ -1556,7 +1551,6 @@ def OnZoomToTarget(self, event): def OnZoomMenuGCP(self, event): """Popup Zoom menu""" - point = wx.GetMousePosition() zoommenu = Menu() # Add items to the menu @@ -2467,7 +2461,6 @@ def UpdateSettings(self): srcrender = False tgtrender = False - reload_target = False if self.new_src_map != src_map: # remove old layer layers = self.parent.grwiz.SrcMap.GetListOfLayers() @@ -2499,7 +2492,6 @@ def UpdateSettings(self): del layers[0] layers = self.parent.grwiz.TgtMap.GetListOfLayers() # self.parent.grwiz.TgtMap.DeleteAllLayers() - reload_target = True tgt_map["raster"] = self.new_tgt_map["raster"] if tgt_map["raster"] != "": diff --git a/gui/wxpython/photo2image/ip2i_mapdisplay.py b/gui/wxpython/photo2image/ip2i_mapdisplay.py index aa085f2b43a..732716a1a0c 100644 --- a/gui/wxpython/photo2image/ip2i_mapdisplay.py +++ b/gui/wxpython/photo2image/ip2i_mapdisplay.py @@ -466,7 +466,6 @@ def PrintMenu(self, event): """ Print options and output menu for map display """ - point = wx.GetMousePosition() printmenu = Menu() # Add items to the menu setup = wx.MenuItem(printmenu, wx.ID_ANY, _("Page setup")) @@ -510,7 +509,6 @@ def SaveDisplayRegion(self, event): def OnZoomMenu(self, event): """Popup Zoom menu""" - point = wx.GetMousePosition() zoommenu = Menu() # Add items to the menu From a05ed293ec9af66daa61f493e62611a6f179d2af Mon Sep 17 00:00:00 2001 From: Mohan Yelugoti Date: Wed, 23 Oct 2024 08:48:45 -0400 Subject: [PATCH 432/471] lib/db: Remove deprecated implementation of db_get_login() and replace it with that of db_get_login2() (#4302) `db_get_login()` is deprecated in 7.8.0 release with da399c52637ee5c81228459fa374a114c8fef06c. Change its function signature and implementation to match that of the currently preferred `db_get_login2()`. Technically, this is removing the old function and introducing a new one with the same name, but a different signature. Modify `db_get_login2()` to internally call `db_get_login()` function call. In the next major version, `db_get_login2()` should be removed to have only one method to get login details for simplicity. See also #4308. --------- Signed-off-by: Mohan Yelugoti --- db/drivers/mysql/db.c | 2 +- db/drivers/postgres/db.c | 4 ++-- db/drivers/postgres/listdb.c | 2 +- include/grass/defs/dbmi.h | 3 ++- lib/db/dbmi_base/connect.c | 10 +++++----- lib/db/dbmi_base/login.c | 16 +++++++--------- lib/vector/Vlib/open_pg.c | 4 ++-- vector/v.external/dsn.c | 2 +- vector/v.in.ogr/dsn.c | 2 +- vector/v.out.ogr/dsn.c | 2 +- 10 files changed, 23 insertions(+), 24 deletions(-) diff --git a/db/drivers/mysql/db.c b/db/drivers/mysql/db.c index 5c98c652e95..b83d3d7064d 100644 --- a/db/drivers/mysql/db.c +++ b/db/drivers/mysql/db.c @@ -51,7 +51,7 @@ int db__driver_open_database(dbHandle *handle) connpar.host, connpar.port, connpar.dbname, connpar.user, connpar.password); - db_get_login2("mysql", name, &user, &password, &host, &port); + db_get_login("mysql", name, &user, &password, &host, &port); connection = mysql_init(NULL); res = diff --git a/db/drivers/postgres/db.c b/db/drivers/postgres/db.c index 5343acf03d8..aa27d370419 100644 --- a/db/drivers/postgres/db.c +++ b/db/drivers/postgres/db.c @@ -54,7 +54,7 @@ int db__driver_open_database(dbHandle *handle) return DB_FAILED; } - db_get_login2("pg", name, &user, &password, &host, &port); + db_get_login("pg", name, &user, &password, &host, &port); pg_conn = PQsetdbLogin(host, port, pgconn.options, pgconn.tty, pgconn.dbname, user, password); @@ -241,7 +241,7 @@ int create_delete_db(dbHandle *handle, int create) pgconn.host, pgconn.port, pgconn.options, pgconn.tty, pgconn.dbname, pgconn.user, pgconn.password, pgconn.host, pgconn.port, pgconn.schema); - db_get_login2("pg", template_db, &user, &password, &host, &port); + db_get_login("pg", template_db, &user, &password, &host, &port); pg_conn = PQsetdbLogin(host, port, pgconn.options, pgconn.tty, pgconn.dbname, user, password); diff --git a/db/drivers/postgres/listdb.c b/db/drivers/postgres/listdb.c index 0efa0328c0f..974141f1e36 100644 --- a/db/drivers/postgres/listdb.c +++ b/db/drivers/postgres/listdb.c @@ -48,7 +48,7 @@ int db__driver_list_databases(dbString *dbpath, int npaths, dbHandle **dblist, pgconn.dbname, pgconn.user, pgconn.password, pgconn.host, pgconn.port, pgconn.options, pgconn.tty); - db_get_login2("pg", NULL, &user, &passwd, &host, &port); + db_get_login("pg", NULL, &user, &passwd, &host, &port); G_debug(1, "user = %s, passwd = %s", user, passwd ? "xxx" : ""); if (user || passwd) { diff --git a/include/grass/defs/dbmi.h b/include/grass/defs/dbmi.h index c5bf648414f..0eca1858489 100644 --- a/include/grass/defs/dbmi.h +++ b/include/grass/defs/dbmi.h @@ -389,7 +389,8 @@ unsigned int db_sizeof_string(const dbString *); int db_set_login(const char *, const char *, const char *, const char *); int db_set_login2(const char *, const char *, const char *, const char *, const char *, const char *, int); -int db_get_login(const char *, const char *, const char **, const char **); +int db_get_login(const char *, const char *, const char **, const char **, + const char **, const char **); int db_get_login2(const char *, const char *, const char **, const char **, const char **, const char **); int db_get_login_dump(FILE *); diff --git a/lib/db/dbmi_base/connect.c b/lib/db/dbmi_base/connect.c index 780d929dd3a..ced282405bd 100644 --- a/lib/db/dbmi_base/connect.c +++ b/lib/db/dbmi_base/connect.c @@ -87,11 +87,11 @@ int db_get_connection(dbConnection *connection) connection->group = (char *)G_getenv_nofatal2("DB_GROUP", G_VAR_MAPSET); /* try to get user/password */ - db_get_login2(connection->driverName, connection->databaseName, - (const char **)&(connection->user), - (const char **)&(connection->password), - (const char **)&(connection->hostName), - (const char **)&(connection->port)); + db_get_login(connection->driverName, connection->databaseName, + (const char **)&(connection->user), + (const char **)&(connection->password), + (const char **)&(connection->hostName), + (const char **)&(connection->port)); return DB_OK; } diff --git a/lib/db/dbmi_base/login.c b/lib/db/dbmi_base/login.c index d79fc84e6cf..bc84f78118c 100644 --- a/lib/db/dbmi_base/login.c +++ b/lib/db/dbmi_base/login.c @@ -346,22 +346,20 @@ static int get_login(const char *driver, const char *database, If driver/database is not found, output arguments are set to NULL. - \deprecated Use db_set_login2() instead. - - \todo: GRASS 8: to be replaced by db_set_login2(). - \param driver driver name \param database database name (can be NULL) \param[out] user name \param[out] password string + \param[out] host name + \param[out] port \return DB_OK on success \return DB_FAILED on failure */ -int db_get_login(const char *driver, const char *database, const char **user, - const char **password) +int db_get_login2(const char *driver, const char *database, const char **user, + const char **password, const char **host, const char **port) { - return get_login(driver, database, user, password, NULL, NULL); + return db_get_login(driver, database, user, password, host, port); } /*! @@ -379,8 +377,8 @@ int db_get_login(const char *driver, const char *database, const char **user, \return DB_OK on success \return DB_FAILED on failure */ -int db_get_login2(const char *driver, const char *database, const char **user, - const char **password, const char **host, const char **port) +int db_get_login(const char *driver, const char *database, const char **user, + const char **password, const char **host, const char **port) { return get_login(driver, database, user, password, host, port); } diff --git a/lib/vector/Vlib/open_pg.c b/lib/vector/Vlib/open_pg.c index 7db0113be99..293291066c2 100644 --- a/lib/vector/Vlib/open_pg.c +++ b/lib/vector/Vlib/open_pg.c @@ -535,11 +535,11 @@ void connect_db(struct Format_info_pg *pg_info) /* try connection settings for given database first, then try * any settings defined for pg driver */ - db_get_login2("pg", dbname, &user, &passwd, &host, &port); + db_get_login("pg", dbname, &user, &passwd, &host, &port); /* any settings defined for pg driver disabled - can cause problems when running multiple local/remote db clusters if (strlen(dbname) > 0 && !user && !passwd) - db_get_login2("pg", NULL, &user, &passwd, &host, &port); + db_get_login("pg", NULL, &user, &passwd, &host, &port); */ if (user || passwd || host || port) { char conninfo[DB_SQL_MAX]; diff --git a/vector/v.external/dsn.c b/vector/v.external/dsn.c index 1a0d6bc2a57..c56009a64e2 100644 --- a/vector/v.external/dsn.c +++ b/vector/v.external/dsn.c @@ -31,7 +31,7 @@ char *get_datasource_name(const char *opt_dsn, int use_ogr) /* add db.login settings (user, password, host, port) */ if (DB_OK == - db_get_login2("pg", database, &user, &passwd, &host, &port)) { + db_get_login("pg", database, &user, &passwd, &host, &port)) { if (user) { if (!G_strcasestr(opt_dsn, "user=")) { strcat(connect_str, " user="); diff --git a/vector/v.in.ogr/dsn.c b/vector/v.in.ogr/dsn.c index 2fecff7e9fb..7e483e71050 100644 --- a/vector/v.in.ogr/dsn.c +++ b/vector/v.in.ogr/dsn.c @@ -38,7 +38,7 @@ char *get_datasource_name(const char *opt_dsn, int use_ogr) /* add db.login settings (user, password, host, port) */ if (DB_OK == - db_get_login2("pg", database, &user, &passwd, &host, &port)) { + db_get_login("pg", database, &user, &passwd, &host, &port)) { if (user) { if (!G_strcasestr(opt_dsn, "user=")) { strcat(connect_str, " user="); diff --git a/vector/v.out.ogr/dsn.c b/vector/v.out.ogr/dsn.c index 31d258c05c7..bd7ce101ed2 100644 --- a/vector/v.out.ogr/dsn.c +++ b/vector/v.out.ogr/dsn.c @@ -37,7 +37,7 @@ char *get_datasource_name(const char *opt_dsn, int use_ogr) /* add db.login settings (user, password, host, port) */ if (DB_OK == - db_get_login2("pg", database, &user, &passwd, &host, &port)) { + db_get_login("pg", database, &user, &passwd, &host, &port)) { if (user) { if (!G_strcasestr(opt_dsn, "user=")) { strcat(connect_str, " user="); From 930d59a3c30235f6ac33ba53e10dd1a8e201f732 Mon Sep 17 00:00:00 2001 From: Mohan Yelugoti Date: Wed, 23 Oct 2024 08:50:28 -0400 Subject: [PATCH 433/471] lib/db: Remove deprecated implementation of db_set_login() and replace it with that of db_set_login2() (#4308) `db_set_login()` is deprecated in 7.8.0 release with da399c52637ee5c81228459fa374a114c8fef06c. Change its function signature and implementation to match that of the currently preferred `db_set_login2()`. Technically, this is removing the old function and introducing a new one with the same name, but a different signature. Modify `db_set_login2()` to internally call `db_set_login()` function call. In the next major version, `db_set_login2()` should be removed to have only one method to get login details for simplicity. See also #4308. --------- Signed-off-by: Mohan Yelugoti --- db/db.login/main.c | 6 +++--- include/grass/defs/dbmi.h | 3 ++- lib/db/dbmi_base/login.c | 21 +++++++++++---------- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/db/db.login/main.c b/db/db.login/main.c index 9147c0e7f1f..f073b654665 100644 --- a/db/db.login/main.c +++ b/db/db.login/main.c @@ -98,9 +98,9 @@ int main(int argc, char *argv[]) exit(EXIT_SUCCESS); } - if (db_set_login2(driver->answer, database->answer, user->answer, - password->answer, host->answer, port->answer, - G_get_overwrite()) == DB_FAILED) { + if (db_set_login(driver->answer, database->answer, user->answer, + password->answer, host->answer, port->answer, + G_get_overwrite()) == DB_FAILED) { G_fatal_error(_("Unable to set user/password")); } diff --git a/include/grass/defs/dbmi.h b/include/grass/defs/dbmi.h index 0eca1858489..5af0476849d 100644 --- a/include/grass/defs/dbmi.h +++ b/include/grass/defs/dbmi.h @@ -386,7 +386,8 @@ const char *db_whoami(void); void db_zero(void *, int); void db_zero_string(dbString *); unsigned int db_sizeof_string(const dbString *); -int db_set_login(const char *, const char *, const char *, const char *); +int db_set_login(const char *, const char *, const char *, const char *, + const char *, const char *, int); int db_set_login2(const char *, const char *, const char *, const char *, const char *, const char *, int); int db_get_login(const char *, const char *, const char **, const char **, diff --git a/lib/db/dbmi_base/login.c b/lib/db/dbmi_base/login.c index bc84f78118c..9c865233d2c 100644 --- a/lib/db/dbmi_base/login.c +++ b/lib/db/dbmi_base/login.c @@ -253,22 +253,23 @@ static int set_login(const char *driver, const char *database, const char *user, /*! \brief Set login parameters for driver/database - \deprecated Use db_set_login2() instead. - - \todo: GRASS 8: to be replaced by db_set_login2(). - \param driver driver name \param database database name \param user user name \param password password string + \param host host name + \param port + \param overwrite TRUE to overwrite existing connections \return DB_OK on success \return DB_FAILED on failure */ -int db_set_login(const char *driver, const char *database, const char *user, - const char *password) +int db_set_login2(const char *driver, const char *database, const char *user, + const char *password, const char *host, const char *port, + int overwrite) { - return set_login(driver, database, user, password, NULL, NULL, FALSE); + return db_set_login(driver, database, user, password, host, port, + overwrite); } /*! @@ -285,9 +286,9 @@ int db_set_login(const char *driver, const char *database, const char *user, \return DB_OK on success \return DB_FAILED on failure */ -int db_set_login2(const char *driver, const char *database, const char *user, - const char *password, const char *host, const char *port, - int overwrite) +int db_set_login(const char *driver, const char *database, const char *user, + const char *password, const char *host, const char *port, + int overwrite) { return set_login(driver, database, user, password, host, port, overwrite); } From 5cd687092b6bcdcaed1e52feed8cd7a312ac5e30 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 23 Oct 2024 18:47:36 +0000 Subject: [PATCH 434/471] CI(deps): Update actions/checkout action to v4.2.2 (#4578) --- .github/workflows/additional_checks.yml | 2 +- .github/workflows/clang-format-check.yml | 2 +- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/coverity.yml | 2 +- .github/workflows/create_release_draft.yml | 2 +- .github/workflows/docker.yml | 2 +- .github/workflows/gcc.yml | 2 +- .github/workflows/macos.yml | 2 +- .github/workflows/osgeo4w.yml | 2 +- .github/workflows/periodic_update.yml | 2 +- .github/workflows/pytest.yml | 2 +- .github/workflows/python-code-quality.yml | 2 +- .github/workflows/super-linter.yml | 2 +- .github/workflows/test-nix.yml | 2 +- .github/workflows/titles.yml | 2 +- .github/workflows/ubuntu.yml | 2 +- 16 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/workflows/additional_checks.yml b/.github/workflows/additional_checks.yml index a91d221f1e4..a2fbfadc36f 100644 --- a/.github/workflows/additional_checks.yml +++ b/.github/workflows/additional_checks.yml @@ -24,7 +24,7 @@ jobs: steps: - name: Checkout repository contents - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: fetch-depth: 31 diff --git a/.github/workflows/clang-format-check.yml b/.github/workflows/clang-format-check.yml index 13ba5bb3cd9..489d8197c09 100644 --- a/.github/workflows/clang-format-check.yml +++ b/.github/workflows/clang-format-check.yml @@ -16,7 +16,7 @@ jobs: name: Formatting Check runs-on: ubuntu-latest steps: - - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false - uses: DoozyX/clang-format-lint-action@c71d0bf4e21876ebec3e5647491186f8797fde31 # v0.18.2 diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 97f89578306..9c161300170 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -40,7 +40,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Set up Python uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 with: diff --git a/.github/workflows/coverity.yml b/.github/workflows/coverity.yml index 22a6b7a42a8..51ab83015ca 100644 --- a/.github/workflows/coverity.yml +++ b/.github/workflows/coverity.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-22.04 if: github.repository == 'OSGeo/grass' steps: - - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Get dependencies run: | diff --git a/.github/workflows/create_release_draft.yml b/.github/workflows/create_release_draft.yml index 3112fb8b33f..6c3597599fc 100644 --- a/.github/workflows/create_release_draft.yml +++ b/.github/workflows/create_release_draft.yml @@ -30,7 +30,7 @@ jobs: contents: write steps: - name: Checks-out repository - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: ref: ${{ github.ref }} fetch-depth: 0 diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 4de674ee9a2..6b2ad83629e 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -49,7 +49,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: fetch-depth: 0 - name: Docker meta diff --git a/.github/workflows/gcc.yml b/.github/workflows/gcc.yml index 651cb272fba..6b6286ef3f0 100644 --- a/.github/workflows/gcc.yml +++ b/.github/workflows/gcc.yml @@ -26,7 +26,7 @@ jobs: fail-fast: false steps: - - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Get dependencies run: | sudo apt-get update -y diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 41ac51df8ff..cdc02ad1432 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -44,7 +44,7 @@ jobs: -mindepth 1 -maxdepth 1 -type f -print -delete # Rehash to forget about the deleted files hash -r - - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Get current date cache key segment id: date # Year and week of year so cache key changes weekly diff --git a/.github/workflows/osgeo4w.yml b/.github/workflows/osgeo4w.yml index a7694fa9b30..30add8dde09 100644 --- a/.github/workflows/osgeo4w.yml +++ b/.github/workflows/osgeo4w.yml @@ -31,7 +31,7 @@ jobs: run: | git config --global core.autocrlf false git config --global core.eol lf - - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - uses: msys2/setup-msys2@ddf331adaebd714795f1042345e6ca57bd66cea8 # v2.24.1 with: path-type: inherit diff --git a/.github/workflows/periodic_update.yml b/.github/workflows/periodic_update.yml index 17855d6149e..d27b8664c55 100644 --- a/.github/workflows/periodic_update.yml +++ b/.github/workflows/periodic_update.yml @@ -21,7 +21,7 @@ jobs: - name: Create URL to the run output id: vars run: echo "run-url=https://github.com/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" >> $GITHUB_OUTPUT - - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: "Check that autoconf scripts are up-to-date:" run: | rm -f config.guess config.sub diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index e1b3b84b54e..b28fb40ad14 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -32,7 +32,7 @@ jobs: PYTHONWARNINGS: always steps: - - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Set up Python uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index 15357764d6a..94a8d0be26b 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -54,7 +54,7 @@ jobs: echo Bandit: ${{ env.BANDIT_VERSION }} echo Ruff: ${{ env.RUFF_VERSION }} - - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Set up Python uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 diff --git a/.github/workflows/super-linter.yml b/.github/workflows/super-linter.yml index 62328723f77..ab2168cd1a3 100644 --- a/.github/workflows/super-linter.yml +++ b/.github/workflows/super-linter.yml @@ -25,7 +25,7 @@ jobs: statuses: write steps: - - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: # super-linter needs the full git history to get the # list of files that changed across commits diff --git a/.github/workflows/test-nix.yml b/.github/workflows/test-nix.yml index ba14e0747ab..43a1b5d40ab 100644 --- a/.github/workflows/test-nix.yml +++ b/.github/workflows/test-nix.yml @@ -28,7 +28,7 @@ jobs: contents: read steps: - - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Install nix uses: DeterminateSystems/nix-installer-action@da36cb69b1c3247ad7a1f931ebfd954a1105ef14 # v14 diff --git a/.github/workflows/titles.yml b/.github/workflows/titles.yml index f202fdafe02..9821a209a87 100644 --- a/.github/workflows/titles.yml +++ b/.github/workflows/titles.yml @@ -21,7 +21,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout base repository (doesn't include the PR changes) - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Call PR title validation function run: python utils/generate_release_notes.py check "${PR_TITLE}" "" "" env: diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 966dbfe21ab..9023df7d1b3 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -59,7 +59,7 @@ jobs: fail-fast: false steps: - - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Invert inclusion list to an exclusion list id: get-exclude From 533070dbc9807657e96ec376f23ab6db64fbdc9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Wed, 23 Oct 2024 17:12:02 -0400 Subject: [PATCH 435/471] style: Fix unused-import (F401) (#4521) * t.rast.accumulate: Remove unused SimpleModule import in test_accumulation * temporal: Fix unused-import (F401) in testsuite * grass.temporal: Fix unused-import (F401) * grass.benchmark: Fix unused-import (F401) by defining __all__ * grass.pygrass.vector: Fix unused-import (F401) for testsuite * grass.pygrass.vector: Sort imports * vector: Fix unused-import (F401) for testsuite * scripts: Fix unused-import (F401) for testsuite * raster: Fix unused-import (F401) for testsuite * v.fill.holes: Fix unused-import (F401) * doc: Fix unused-import (F401) * grass.semantic_label: Fix unused-import (F401) by defining __all__ in __init__ * grass.pygrass.rpc: Fix unused-import (F401) in __init__ * grass.pygrass.modules.interface: Fix unused-import (F401) by defining __all__ in __init__ * grass.pygrass.modules.interface: Apply isort * grass.pygrass.modules.grid: Fix unused-import (F401) by defining __all__ in __init__ * grass.pygrass.modules: Fix unused-import (F401) by defining __all__ in __init__ * grass.pygrass.modules: Apply isort * grass.imaging: Fix unused-import (F401) by defining __all__ in __init__ * grass.imaging: Apply isort * grass.jupyter: Fix unused-import (F401) by defining __all__ in __init__ * grass.jupyter: Apply isort * pygrass: Ignore F401 (unused imports) Ignored inline where the import is justified and isn't to fix later, and ignored in a per-file exclusion where the imports seemed to be for commented-out test case lines * lib/gis: Fix F401 unused platform import in test_gis_lib_getl * grass.script: Ignore F401 in `__init__.py` for setup import * style: Remove exclusion for F401 ruff rule * style: Remove unneeded flake8 exclusions for F401 * Apply suggestions from code review --- .flake8 | 14 +++++--------- doc/notebooks/hydrology.ipynb | 1 - doc/notebooks/jupyter_tutorial.ipynb | 1 - doc/notebooks/parallelization_tutorial.ipynb | 1 - doc/notebooks/solar_potential.ipynb | 1 - doc/notebooks/viewshed_analysis.ipynb | 1 - lib/gis/testsuite/test_gis_lib_getl.py | 1 - pyproject.toml | 3 ++- python/grass/benchmark/__init__.py | 14 ++++++++++++++ python/grass/imaging/__init__.py | 15 +++++++++++++-- python/grass/jupyter/__init__.py | 13 ++++++++++++- python/grass/pygrass/modules/__init__.py | 9 ++++++++- python/grass/pygrass/modules/grid/__init__.py | 2 ++ .../pygrass/modules/interface/__init__.py | 18 ++++++++++++------ .../modules/testsuite/test_import_isolation.py | 9 ++++++--- python/grass/pygrass/rpc/__init__.py | 3 --- .../pygrass/vector/testsuite/test_filters.py | 1 - .../pygrass/vector/testsuite/test_geometry.py | 11 ++++------- .../vector/testsuite/test_geometry_attrs.py | 8 -------- .../testsuite/test_pygrass_vector_doctests.py | 3 --- .../pygrass/vector/testsuite/test_table.py | 5 ++--- .../pygrass/vector/testsuite/test_vector.py | 2 -- .../pygrass/vector/testsuite/test_vector3d.py | 6 +----- python/grass/script/__init__.py | 2 +- python/grass/semantic_label/__init__.py | 2 ++ .../unittests_temporal_algebra_grs.py | 1 - .../unittests_temporal_algebra_mixed_stds.py | 1 - .../unittests_temporal_conditionals.py | 1 - raster/r.in.ascii/testsuite/test_r_in_ascii.py | 1 - .../testsuite/test_r_object_geometry.py | 2 -- raster/r.viewshed/testsuite/test_r_viewshed.py | 1 - scripts/db.in.ogr/testsuite/test_db_in_ogr.py | 1 - .../r.fillnulls/testsuite/test_r_fillnulls.py | 2 -- scripts/r.grow/testsuite/test_r_grow.py | 1 - scripts/v.dissolve/v_dissolve.ipynb | 1 - .../testsuite/test_accumulation.py | 1 - .../testsuite/test_aggregation_absolute.py | 1 - .../test_aggregation_absolute_parallel.py | 1 - .../testsuite/test_aggregation_relative.py | 1 - .../testsuite/test_raster_algebra_operators.py | 1 - .../testsuite/test_t_rast_extract.py | 3 --- .../t.rast.gapfill/testsuite/test_gapfill.py | 2 -- .../testsuite/test_neighbors.py | 1 + .../t.rast.series/testsuite/test_series.py | 1 - .../testsuite/test_strds_to_rast3.py | 4 ---- .../t.rast.to.vect/testsuite/test_to_vect.py | 2 -- .../testsuite/test_t_rast_univar.py | 1 + .../testsuite/test_t_rast3d_extract.py | 3 --- .../testsuite/test_t_rast3d_univar.py | 1 + temporal/t.shift/testsuite/test_shift.py | 2 -- temporal/t.snap/testsuite/test_snap.py | 2 -- .../t.support/testsuite/test_support_str3ds.py | 2 -- .../t.support/testsuite/test_support_strds.py | 2 -- .../t.support/testsuite/test_support_stvds.py | 2 -- .../t.unregister/testsuite/test_unregister.py | 1 - vector/v.extract/testsuite/test_v_extract.py | 1 - vector/v.fill.holes/examples.ipynb | 2 -- vector/v.in.ogr/testsuite/test_v_in_ogr.py | 1 - 58 files changed, 87 insertions(+), 109 deletions(-) diff --git a/.flake8 b/.flake8 index 14224a97843..9985b8e5a42 100644 --- a/.flake8 +++ b/.flake8 @@ -15,11 +15,10 @@ per-file-ignores = # E501 line too long # E722 do not use bare 'except' # W605 invalid escape sequence - # F401 imported but unused # F821 undefined name 'unicode' # F841 local variable assigned to but never used # E741 ambiguous variable name 'l' - __init__.py: F401, F403 + __init__.py: F403 man/build_html.py: E501 doc/python/m.distance.py: E501 gui/scripts/d.wms.py: E501 @@ -66,10 +65,8 @@ per-file-ignores = python/grass/pygrass/vector/__init__.py: E402 python/grass/pygrass/raster/__init__.py: E402 python/grass/pygrass/vector/__init__.py: E402 - python/grass/pygrass/modules/interface/*.py: F401 - python/grass/pygrass/modules/grid/*.py: F401 python/grass/pygrass/raster/category.py: E721 - python/grass/pygrass/rpc/__init__.py: F401, F403 + python/grass/pygrass/rpc/__init__.py: F403 python/grass/pygrass/utils.py: E402 python/grass/temporal/abstract_space_time_dataset.py: E722 python/grass/temporal/c_libraries_interface.py: E722 @@ -82,17 +79,16 @@ per-file-ignores = python/grass/temporal/temporal_topology_dataset_connector.py: E722 # Current benchmarks/tests are changing sys.path before import. # Possibly, a different approach should be taken there anyway. - python/grass/pygrass/tests/benchmark.py: E402, F401, F821 + python/grass/pygrass/tests/benchmark.py: E402, F821 # Configuration file for Sphinx: # Ignoring import/code mix and line length. # Files not managed by Black python/grass/imaging/images2gif.py: E226 # Unused imports in init files - # F401 imported but unused # F403 star import used; unable to detect undefined names python/grass/*/__init__.py: F401, F403 - python/grass/*/*/__init__.py: F401, F403 - python/grass/*/*/*/__init__.py: F401, F403 + python/grass/*/*/__init__.py: F403 + python/grass/*/*/*/__init__.py: F403 # E402 module level import not at top of file scripts/r.in.wms/wms_gdal_drv.py: E722 scripts/r.in.wms/wms_drv.py: E402, E722 diff --git a/doc/notebooks/hydrology.ipynb b/doc/notebooks/hydrology.ipynb index b9e23acf3c1..f5c853de9a2 100644 --- a/doc/notebooks/hydrology.ipynb +++ b/doc/notebooks/hydrology.ipynb @@ -25,7 +25,6 @@ "outputs": [], "source": [ "# Import Python standard library and IPython packages we need.\n", - "import os\n", "import subprocess\n", "import sys\n", "import csv\n", diff --git a/doc/notebooks/jupyter_tutorial.ipynb b/doc/notebooks/jupyter_tutorial.ipynb index 71852c226e6..6dfdfb63625 100644 --- a/doc/notebooks/jupyter_tutorial.ipynb +++ b/doc/notebooks/jupyter_tutorial.ipynb @@ -30,7 +30,6 @@ "source": [ "# Import Python standard library and IPython packages we need.\n", "import subprocess\n", - "import time\n", "import sys\n", "\n", "# Ask GRASS GIS where its Python packages are.\n", diff --git a/doc/notebooks/parallelization_tutorial.ipynb b/doc/notebooks/parallelization_tutorial.ipynb index 2c5d902e9a4..4611f5d1c24 100644 --- a/doc/notebooks/parallelization_tutorial.ipynb +++ b/doc/notebooks/parallelization_tutorial.ipynb @@ -21,7 +21,6 @@ "metadata": {}, "outputs": [], "source": [ - "import os\n", "import subprocess\n", "import sys\n", "\n", diff --git a/doc/notebooks/solar_potential.ipynb b/doc/notebooks/solar_potential.ipynb index 98253ad6450..07da1af77e2 100644 --- a/doc/notebooks/solar_potential.ipynb +++ b/doc/notebooks/solar_potential.ipynb @@ -28,7 +28,6 @@ "outputs": [], "source": [ "# Import Python standard library and IPython packages we need.\n", - "import os\n", "import subprocess\n", "import sys\n", "\n", diff --git a/doc/notebooks/viewshed_analysis.ipynb b/doc/notebooks/viewshed_analysis.ipynb index a4f0b61744a..5cfe69c4dae 100644 --- a/doc/notebooks/viewshed_analysis.ipynb +++ b/doc/notebooks/viewshed_analysis.ipynb @@ -30,7 +30,6 @@ "outputs": [], "source": [ "# Import Python standard library and IPython packages we need.\n", - "import os\n", "import subprocess\n", "import sys\n", "\n", diff --git a/lib/gis/testsuite/test_gis_lib_getl.py b/lib/gis/testsuite/test_gis_lib_getl.py index 98092955ab2..72e8992ad39 100644 --- a/lib/gis/testsuite/test_gis_lib_getl.py +++ b/lib/gis/testsuite/test_gis_lib_getl.py @@ -5,7 +5,6 @@ import ctypes import pathlib -import platform import unittest import grass.lib.gis as libgis diff --git a/pyproject.toml b/pyproject.toml index 4b7856bab3b..a428c79106f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -131,7 +131,6 @@ ignore = [ "E722", # bare-except "E731", # lambda-assignment "E741", # ambiguous-variable-name - "F401", # unused-import "F403", # undefined-local-with-import-star "F405", # undefined-local-with-import-star-usage "F811", # redefined-while-unused @@ -319,7 +318,9 @@ ignore = [ "python/grass/jupyter/testsuite/map_test.py" = ["PGH004"] "python/grass/pydispatch/signal.py" = ["A005"] "python/grass/pygrass/modules/grid/grid.py" = ["SIM115"] +"python/grass/pygrass/modules/grid/testsuite/test_*_modules_grid_doctests.py" = ["F401"] "python/grass/pygrass/modules/interface/env.py" = ["SIM115"] +"python/grass/pygrass/modules/testsuite/test_pygrass_modules_doctests.py" = ["F401"] "python/grass/pygrass/raster/segment.py" = ["SIM115"] "python/grass/pygrass/tests/*.py" = ["SIM115"] "python/grass/pygrass/vector/geometry.py" = ["PYI024"] diff --git a/python/grass/benchmark/__init__.py b/python/grass/benchmark/__init__.py index a4a7bf4e991..62a6a85c3e1 100644 --- a/python/grass/benchmark/__init__.py +++ b/python/grass/benchmark/__init__.py @@ -34,3 +34,17 @@ save_results_to_file, ) from .runners import benchmark_nprocs, benchmark_resolutions, benchmark_single + +__all__ = [ + "benchmark_nprocs", + "benchmark_resolutions", + "benchmark_single", + "join_results", + "join_results_from_files", + "load_results", + "load_results_from_file", + "nprocs_plot", + "num_cells_plot", + "save_results", + "save_results_to_file", +] diff --git a/python/grass/imaging/__init__.py b/python/grass/imaging/__init__.py index 41626697f8e..7434942abe0 100644 --- a/python/grass/imaging/__init__.py +++ b/python/grass/imaging/__init__.py @@ -1,4 +1,15 @@ -from grass.imaging.images2gif import readGif, writeGif -from grass.imaging.images2swf import readSwf, writeSwf from grass.imaging.images2avi import readAvi, writeAvi +from grass.imaging.images2gif import readGif, writeGif from grass.imaging.images2ims import readIms, writeIms +from grass.imaging.images2swf import readSwf, writeSwf + +__all__ = [ + "readAvi", + "readGif", + "readIms", + "readSwf", + "writeAvi", + "writeGif", + "writeIms", + "writeSwf", +] diff --git a/python/grass/jupyter/__init__.py b/python/grass/jupyter/__init__.py index 91457f07ac3..21223c2cd47 100644 --- a/python/grass/jupyter/__init__.py +++ b/python/grass/jupyter/__init__.py @@ -109,6 +109,17 @@ from .interactivemap import InteractiveMap, Raster, Vector from .map import Map from .map3d import Map3D +from .seriesmap import SeriesMap from .setup import init from .timeseriesmap import TimeSeriesMap -from .seriesmap import SeriesMap + +__all__ = [ + "InteractiveMap", + "Map", + "Map3D", + "Raster", + "SeriesMap", + "TimeSeriesMap", + "Vector", + "init", +] diff --git a/python/grass/pygrass/modules/__init__.py b/python/grass/pygrass/modules/__init__.py index 61073791599..ce4d169fedd 100644 --- a/python/grass/pygrass/modules/__init__.py +++ b/python/grass/pygrass/modules/__init__.py @@ -1,2 +1,9 @@ -from grass.pygrass.modules.interface import Module, MultiModule, ParallelModuleQueue from grass.pygrass.modules import shortcuts +from grass.pygrass.modules.interface import Module, MultiModule, ParallelModuleQueue + +__all__ = [ + "Module", + "MultiModule", + "ParallelModuleQueue", + "shortcuts", +] diff --git a/python/grass/pygrass/modules/grid/__init__.py b/python/grass/pygrass/modules/grid/__init__.py index 1c971174998..0d770ed3ad2 100644 --- a/python/grass/pygrass/modules/grid/__init__.py +++ b/python/grass/pygrass/modules/grid/__init__.py @@ -1 +1,3 @@ from grass.pygrass.modules.grid.grid import GridModule + +__all__ = ["GridModule"] diff --git a/python/grass/pygrass/modules/interface/__init__.py b/python/grass/pygrass/modules/interface/__init__.py index a05211e21c3..bde59049c6e 100644 --- a/python/grass/pygrass/modules/interface/__init__.py +++ b/python/grass/pygrass/modules/interface/__init__.py @@ -4,14 +4,20 @@ @author: pietro """ -from grass.pygrass.modules.interface import flag -from grass.pygrass.modules.interface import parameter -from grass.pygrass.modules.interface import module -from grass.pygrass.modules.interface import typedict -from grass.pygrass.modules.interface import read - +from grass.pygrass.modules.interface import flag, module, parameter, read, typedict from grass.pygrass.modules.interface.module import ( Module, MultiModule, ParallelModuleQueue, ) + +__all__ = [ + "Module", + "MultiModule", + "ParallelModuleQueue", + "flag", + "module", + "parameter", + "read", + "typedict", +] diff --git a/python/grass/pygrass/modules/testsuite/test_import_isolation.py b/python/grass/pygrass/modules/testsuite/test_import_isolation.py index d1321a554d0..af643ec2fee 100644 --- a/python/grass/pygrass/modules/testsuite/test_import_isolation.py +++ b/python/grass/pygrass/modules/testsuite/test_import_isolation.py @@ -36,14 +36,17 @@ def test_import_isolation(self): isolate, check(*self.patterns), msg="Test isolation before any import." ) # same import done in __init__ file - from grass.pygrass.modules.interface import Module, ParallelModuleQueue - from grass.pygrass.modules import shortcuts + from grass.pygrass.modules.interface import ( + Module, # noqa: F401 + ParallelModuleQueue, # noqa: F401 + ) + from grass.pygrass.modules import shortcuts # noqa: F401 self.assertEqual( isolate, check(*self.patterns), msg="Test isolation after import Module." ) # test the other way round - from grass.pygrass.vector import VectorTopo + from grass.pygrass.vector import VectorTopo # noqa: F401 self.assertNotEqual( isolate, diff --git a/python/grass/pygrass/rpc/__init__.py b/python/grass/pygrass/rpc/__init__.py index 07cbfe6503a..a6ab19b9369 100644 --- a/python/grass/pygrass/rpc/__init__.py +++ b/python/grass/pygrass/rpc/__init__.py @@ -10,8 +10,6 @@ :authors: Soeren Gebbert """ -import time -import threading import sys from multiprocessing import Process, Lock, Pipe from ctypes import CFUNCTYPE, c_void_p @@ -24,7 +22,6 @@ from .base import RPCServerBase from grass.pygrass.gis.region import Region from grass.pygrass import utils -import logging ############################################################################### ############################################################################### diff --git a/python/grass/pygrass/vector/testsuite/test_filters.py b/python/grass/pygrass/vector/testsuite/test_filters.py index 9252b134c02..9adba4cdefc 100644 --- a/python/grass/pygrass/vector/testsuite/test_filters.py +++ b/python/grass/pygrass/vector/testsuite/test_filters.py @@ -6,7 +6,6 @@ from grass.gunittest.case import TestCase from grass.gunittest.main import test - from grass.pygrass.vector.table import Filters diff --git a/python/grass/pygrass/vector/testsuite/test_geometry.py b/python/grass/pygrass/vector/testsuite/test_geometry.py index b2a7a1e37df..6957a473165 100644 --- a/python/grass/pygrass/vector/testsuite/test_geometry.py +++ b/python/grass/pygrass/vector/testsuite/test_geometry.py @@ -6,18 +6,15 @@ import sys import unittest + import numpy as np +import grass.lib.vector as libvect from grass.gunittest.case import TestCase from grass.gunittest.main import test - -import grass.lib.vector as libvect -from grass.script.core import run_command - -from grass.pygrass.vector import Vector, VectorTopo -from grass.pygrass.vector.geometry import Point, Line, Node -from grass.pygrass.vector.geometry import Area, Boundary, Centroid +from grass.pygrass.vector import VectorTopo from grass.pygrass.vector.basic import Bbox +from grass.pygrass.vector.geometry import Area, Line, Node, Point class PointTestCase(TestCase): diff --git a/python/grass/pygrass/vector/testsuite/test_geometry_attrs.py b/python/grass/pygrass/vector/testsuite/test_geometry_attrs.py index 356a55ec4d7..cba638b1901 100644 --- a/python/grass/pygrass/vector/testsuite/test_geometry_attrs.py +++ b/python/grass/pygrass/vector/testsuite/test_geometry_attrs.py @@ -4,16 +4,8 @@ @author: pietro """ -import sys -import unittest -import numpy as np - from grass.gunittest.case import TestCase from grass.gunittest.main import test - -import grass.lib.vector as libvect -from grass.script.core import run_command - from grass.pygrass.vector import VectorTopo diff --git a/python/grass/pygrass/vector/testsuite/test_pygrass_vector_doctests.py b/python/grass/pygrass/vector/testsuite/test_pygrass_vector_doctests.py index b3d26220ec1..2cf0d0ba0c5 100644 --- a/python/grass/pygrass/vector/testsuite/test_pygrass_vector_doctests.py +++ b/python/grass/pygrass/vector/testsuite/test_pygrass_vector_doctests.py @@ -7,10 +7,7 @@ import grass.gunittest.case import grass.gunittest.main import grass.gunittest.utils - import grass.pygrass.vector as gvector -import grass.pygrass.utils as gutils - # doctest does not allow changing the base classes of test case, skip test case # and test suite, so we need to create a new type which inherits from our class diff --git a/python/grass/pygrass/vector/testsuite/test_table.py b/python/grass/pygrass/vector/testsuite/test_table.py index d87786fb3bd..c77233400b1 100644 --- a/python/grass/pygrass/vector/testsuite/test_table.py +++ b/python/grass/pygrass/vector/testsuite/test_table.py @@ -7,16 +7,15 @@ import os import sqlite3 import tempfile as tmp -from string import ascii_letters, digits from random import choice +from string import ascii_letters, digits + import numpy as np from grass.gunittest.case import TestCase from grass.gunittest.main import test - from grass.pygrass.vector.table import Table, get_path - # dictionary that generate random data RNG = np.random.default_rng() COL2VALS = { diff --git a/python/grass/pygrass/vector/testsuite/test_vector.py b/python/grass/pygrass/vector/testsuite/test_vector.py index 5d16333c31f..b4eebb2ab1e 100644 --- a/python/grass/pygrass/vector/testsuite/test_vector.py +++ b/python/grass/pygrass/vector/testsuite/test_vector.py @@ -6,8 +6,6 @@ from grass.gunittest.case import TestCase from grass.gunittest.main import test - -from grass.script.core import run_command from grass.pygrass.vector import VectorTopo diff --git a/python/grass/pygrass/vector/testsuite/test_vector3d.py b/python/grass/pygrass/vector/testsuite/test_vector3d.py index 796f85ca529..84e688d22e8 100644 --- a/python/grass/pygrass/vector/testsuite/test_vector3d.py +++ b/python/grass/pygrass/vector/testsuite/test_vector3d.py @@ -8,13 +8,9 @@ from grass.gunittest.case import TestCase from grass.gunittest.main import test - -from grass.script.core import run_command - +from grass.pygrass.gis.region import Region from grass.pygrass.vector import VectorTopo from grass.pygrass.vector.geometry import Point -from grass.pygrass.gis.region import Region -from grass.pygrass.utils import get_mapset_vector def generate_coordinates(number, bbox=None, with_z=False): diff --git a/python/grass/script/__init__.py b/python/grass/script/__init__.py index 8589e8a1521..ea82c2fe451 100644 --- a/python/grass/script/__init__.py +++ b/python/grass/script/__init__.py @@ -7,4 +7,4 @@ from .raster3d import * from .vector import * from .utils import * -from . import setup +from . import setup # noqa: F401 diff --git a/python/grass/semantic_label/__init__.py b/python/grass/semantic_label/__init__.py index 9f0b9b6caed..58bfe11a418 100644 --- a/python/grass/semantic_label/__init__.py +++ b/python/grass/semantic_label/__init__.py @@ -1 +1,3 @@ from .reader import SemanticLabelReader, SemanticLabelReaderError + +__all__ = ["SemanticLabelReader", "SemanticLabelReaderError"] diff --git a/python/grass/temporal/testsuite/unittests_temporal_algebra_grs.py b/python/grass/temporal/testsuite/unittests_temporal_algebra_grs.py index 7e0db57a8ce..d7a81bcb34d 100644 --- a/python/grass/temporal/testsuite/unittests_temporal_algebra_grs.py +++ b/python/grass/temporal/testsuite/unittests_temporal_algebra_grs.py @@ -8,7 +8,6 @@ """ import datetime -import os import grass.temporal as tgis from grass.gunittest.case import TestCase diff --git a/python/grass/temporal/testsuite/unittests_temporal_algebra_mixed_stds.py b/python/grass/temporal/testsuite/unittests_temporal_algebra_mixed_stds.py index cf0e9c82bb7..ce911839049 100644 --- a/python/grass/temporal/testsuite/unittests_temporal_algebra_mixed_stds.py +++ b/python/grass/temporal/testsuite/unittests_temporal_algebra_mixed_stds.py @@ -8,7 +8,6 @@ """ import datetime -import os import grass.temporal as tgis from grass.gunittest.case import TestCase diff --git a/python/grass/temporal/testsuite/unittests_temporal_conditionals.py b/python/grass/temporal/testsuite/unittests_temporal_conditionals.py index ff0703aec1d..800c7d30bf6 100644 --- a/python/grass/temporal/testsuite/unittests_temporal_conditionals.py +++ b/python/grass/temporal/testsuite/unittests_temporal_conditionals.py @@ -8,7 +8,6 @@ """ import datetime -import os import grass.temporal as tgis from grass.gunittest.case import TestCase diff --git a/raster/r.in.ascii/testsuite/test_r_in_ascii.py b/raster/r.in.ascii/testsuite/test_r_in_ascii.py index baa892d83a8..8db06f83517 100644 --- a/raster/r.in.ascii/testsuite/test_r_in_ascii.py +++ b/raster/r.in.ascii/testsuite/test_r_in_ascii.py @@ -11,7 +11,6 @@ from grass.gunittest.case import TestCase from grass.gunittest.main import test -from grass.script.core import read_command INPUT_NOQUOTES = """north: 4299000.00 south: 4247000.00 diff --git a/raster/r.object.geometry/testsuite/test_r_object_geometry.py b/raster/r.object.geometry/testsuite/test_r_object_geometry.py index 419df4d780b..279d46ae4f0 100644 --- a/raster/r.object.geometry/testsuite/test_r_object_geometry.py +++ b/raster/r.object.geometry/testsuite/test_r_object_geometry.py @@ -6,11 +6,9 @@ import json import os -from sys import stderr from grass.gunittest.case import TestCase from grass.gunittest.main import test -from grass.gunittest.gmodules import call_module from grass.gunittest.gmodules import SimpleModule diff --git a/raster/r.viewshed/testsuite/test_r_viewshed.py b/raster/r.viewshed/testsuite/test_r_viewshed.py index 2b136abd551..a341d8e97b5 100644 --- a/raster/r.viewshed/testsuite/test_r_viewshed.py +++ b/raster/r.viewshed/testsuite/test_r_viewshed.py @@ -1,6 +1,5 @@ from grass.gunittest.case import TestCase from grass.gunittest.main import test -from grass.gunittest.gmodules import call_module class TestViewshed(TestCase): diff --git a/scripts/db.in.ogr/testsuite/test_db_in_ogr.py b/scripts/db.in.ogr/testsuite/test_db_in_ogr.py index 423917e1b93..b79efa1f096 100644 --- a/scripts/db.in.ogr/testsuite/test_db_in_ogr.py +++ b/scripts/db.in.ogr/testsuite/test_db_in_ogr.py @@ -8,7 +8,6 @@ from grass.gunittest.main import test from grass.gunittest.gmodules import SimpleModule -from grass.script.core import run_command from grass.script.utils import decode import os diff --git a/scripts/r.fillnulls/testsuite/test_r_fillnulls.py b/scripts/r.fillnulls/testsuite/test_r_fillnulls.py index 975fd5330f2..dda6d99ed95 100644 --- a/scripts/r.fillnulls/testsuite/test_r_fillnulls.py +++ b/scripts/r.fillnulls/testsuite/test_r_fillnulls.py @@ -4,8 +4,6 @@ @author: Sanjeet Bhatti """ -import os - from grass.gunittest.case import TestCase from grass.gunittest.main import test from grass.gunittest.gmodules import SimpleModule diff --git a/scripts/r.grow/testsuite/test_r_grow.py b/scripts/r.grow/testsuite/test_r_grow.py index 3d54b9ab993..1c9488b4180 100644 --- a/scripts/r.grow/testsuite/test_r_grow.py +++ b/scripts/r.grow/testsuite/test_r_grow.py @@ -7,7 +7,6 @@ from grass.gunittest.case import TestCase from grass.gunittest.main import test from grass.gunittest.gmodules import SimpleModule -from grass.script.core import run_command class TestRGrow(TestCase): diff --git a/scripts/v.dissolve/v_dissolve.ipynb b/scripts/v.dissolve/v_dissolve.ipynb index f90907a704d..085b23828d8 100644 --- a/scripts/v.dissolve/v_dissolve.ipynb +++ b/scripts/v.dissolve/v_dissolve.ipynb @@ -19,7 +19,6 @@ "metadata": {}, "outputs": [], "source": [ - "import os\n", "import json\n", "import subprocess\n", "import sys\n", diff --git a/temporal/t.rast.accumulate/testsuite/test_accumulation.py b/temporal/t.rast.accumulate/testsuite/test_accumulation.py index 566c1971f99..6c277c320cf 100644 --- a/temporal/t.rast.accumulate/testsuite/test_accumulation.py +++ b/temporal/t.rast.accumulate/testsuite/test_accumulation.py @@ -12,7 +12,6 @@ import grass.temporal as tgis from grass.gunittest.case import TestCase -from grass.gunittest.gmodules import SimpleModule class TestAccumulate(TestCase): diff --git a/temporal/t.rast.aggregate/testsuite/test_aggregation_absolute.py b/temporal/t.rast.aggregate/testsuite/test_aggregation_absolute.py index 924e3950ca8..e7702ba23b5 100644 --- a/temporal/t.rast.aggregate/testsuite/test_aggregation_absolute.py +++ b/temporal/t.rast.aggregate/testsuite/test_aggregation_absolute.py @@ -10,7 +10,6 @@ import os -import grass.pygrass.modules as pymod import grass.temporal as tgis from grass.gunittest.case import TestCase from grass.gunittest.gmodules import SimpleModule diff --git a/temporal/t.rast.aggregate/testsuite/test_aggregation_absolute_parallel.py b/temporal/t.rast.aggregate/testsuite/test_aggregation_absolute_parallel.py index 2b56dd30615..5ad25e2e32c 100644 --- a/temporal/t.rast.aggregate/testsuite/test_aggregation_absolute_parallel.py +++ b/temporal/t.rast.aggregate/testsuite/test_aggregation_absolute_parallel.py @@ -11,7 +11,6 @@ import os from datetime import datetime -import grass.pygrass.modules as pymod import grass.temporal as tgis from grass.gunittest.case import TestCase from grass.gunittest.gmodules import SimpleModule diff --git a/temporal/t.rast.aggregate/testsuite/test_aggregation_relative.py b/temporal/t.rast.aggregate/testsuite/test_aggregation_relative.py index 244b0d519d5..dc1da4c1cc3 100644 --- a/temporal/t.rast.aggregate/testsuite/test_aggregation_relative.py +++ b/temporal/t.rast.aggregate/testsuite/test_aggregation_relative.py @@ -10,7 +10,6 @@ import os -import grass.pygrass.modules as pymod import grass.temporal as tgis from grass.gunittest.case import TestCase from grass.gunittest.gmodules import SimpleModule diff --git a/temporal/t.rast.algebra/testsuite/test_raster_algebra_operators.py b/temporal/t.rast.algebra/testsuite/test_raster_algebra_operators.py index cf3d12a1aae..f07fabc9106 100644 --- a/temporal/t.rast.algebra/testsuite/test_raster_algebra_operators.py +++ b/temporal/t.rast.algebra/testsuite/test_raster_algebra_operators.py @@ -12,7 +12,6 @@ import grass.temporal as tgis from grass.gunittest.case import TestCase -from grass.gunittest.gmodules import SimpleModule from grass.gunittest.main import test diff --git a/temporal/t.rast.extract/testsuite/test_t_rast_extract.py b/temporal/t.rast.extract/testsuite/test_t_rast_extract.py index ca3c1a4c1db..b9fff4753c2 100644 --- a/temporal/t.rast.extract/testsuite/test_t_rast_extract.py +++ b/temporal/t.rast.extract/testsuite/test_t_rast_extract.py @@ -8,9 +8,6 @@ @author Soeren Gebbert """ -import subprocess - -import grass.pygrass.modules as pymod from grass.gunittest.case import TestCase from grass.gunittest.gmodules import SimpleModule diff --git a/temporal/t.rast.gapfill/testsuite/test_gapfill.py b/temporal/t.rast.gapfill/testsuite/test_gapfill.py index 7b083617d08..bfc8f48f581 100644 --- a/temporal/t.rast.gapfill/testsuite/test_gapfill.py +++ b/temporal/t.rast.gapfill/testsuite/test_gapfill.py @@ -8,8 +8,6 @@ @author Soeren Gebbert """ -import subprocess - from grass.gunittest.case import TestCase from grass.gunittest.gmodules import SimpleModule from grass.gunittest.utils import xfail_windows diff --git a/temporal/t.rast.neighbors/testsuite/test_neighbors.py b/temporal/t.rast.neighbors/testsuite/test_neighbors.py index 414b009fb1d..b48df4df169 100644 --- a/temporal/t.rast.neighbors/testsuite/test_neighbors.py +++ b/temporal/t.rast.neighbors/testsuite/test_neighbors.py @@ -5,6 +5,7 @@ """ import os + import grass.temporal as tgis from grass.gunittest.case import TestCase from grass.gunittest.gmodules import SimpleModule diff --git a/temporal/t.rast.series/testsuite/test_series.py b/temporal/t.rast.series/testsuite/test_series.py index e936e10e0e7..97df7c048fc 100644 --- a/temporal/t.rast.series/testsuite/test_series.py +++ b/temporal/t.rast.series/testsuite/test_series.py @@ -10,7 +10,6 @@ import os -import grass.pygrass.modules as pymod import grass.temporal as tgis from grass.gunittest.case import TestCase from grass.gunittest.gmodules import SimpleModule diff --git a/temporal/t.rast.to.rast3/testsuite/test_strds_to_rast3.py b/temporal/t.rast.to.rast3/testsuite/test_strds_to_rast3.py index 3a6eb7d45cc..28853ecbff1 100644 --- a/temporal/t.rast.to.rast3/testsuite/test_strds_to_rast3.py +++ b/temporal/t.rast.to.rast3/testsuite/test_strds_to_rast3.py @@ -8,11 +8,7 @@ @author Soeren Gebbert """ -import subprocess - -import grass.pygrass.modules as pymod from grass.gunittest.case import TestCase -from grass.gunittest.gmodules import SimpleModule class TestSTRDSToRast3(TestCase): diff --git a/temporal/t.rast.to.vect/testsuite/test_to_vect.py b/temporal/t.rast.to.vect/testsuite/test_to_vect.py index 2217b46a909..b429d8bf0dc 100644 --- a/temporal/t.rast.to.vect/testsuite/test_to_vect.py +++ b/temporal/t.rast.to.vect/testsuite/test_to_vect.py @@ -8,8 +8,6 @@ @author Soeren Gebbert """ -import subprocess - from grass.gunittest.case import TestCase from grass.gunittest.gmodules import SimpleModule diff --git a/temporal/t.rast.univar/testsuite/test_t_rast_univar.py b/temporal/t.rast.univar/testsuite/test_t_rast_univar.py index 17831b9b5b9..83e5c2d6229 100644 --- a/temporal/t.rast.univar/testsuite/test_t_rast_univar.py +++ b/temporal/t.rast.univar/testsuite/test_t_rast_univar.py @@ -9,6 +9,7 @@ """ from pathlib import Path + from grass.gunittest.case import TestCase from grass.gunittest.gmodules import SimpleModule from grass.gunittest.utils import xfail_windows diff --git a/temporal/t.rast3d.extract/testsuite/test_t_rast3d_extract.py b/temporal/t.rast3d.extract/testsuite/test_t_rast3d_extract.py index 4f0d476e0e8..3ae010e8423 100644 --- a/temporal/t.rast3d.extract/testsuite/test_t_rast3d_extract.py +++ b/temporal/t.rast3d.extract/testsuite/test_t_rast3d_extract.py @@ -8,9 +8,6 @@ :authors: Soeren Gebbert """ -import subprocess - -import grass.pygrass.modules as pymod from grass.gunittest.case import TestCase from grass.gunittest.gmodules import SimpleModule diff --git a/temporal/t.rast3d.univar/testsuite/test_t_rast3d_univar.py b/temporal/t.rast3d.univar/testsuite/test_t_rast3d_univar.py index 4ca64e88442..21980921168 100644 --- a/temporal/t.rast3d.univar/testsuite/test_t_rast3d_univar.py +++ b/temporal/t.rast3d.univar/testsuite/test_t_rast3d_univar.py @@ -9,6 +9,7 @@ """ from pathlib import Path + from grass.gunittest.case import TestCase from grass.gunittest.gmodules import SimpleModule from grass.gunittest.utils import xfail_windows diff --git a/temporal/t.shift/testsuite/test_shift.py b/temporal/t.shift/testsuite/test_shift.py index ea28e165995..3d55b059b4c 100644 --- a/temporal/t.shift/testsuite/test_shift.py +++ b/temporal/t.shift/testsuite/test_shift.py @@ -10,10 +10,8 @@ import os -import grass.pygrass.modules as pymod import grass.temporal as tgis from grass.gunittest.case import TestCase -from grass.gunittest.gmodules import SimpleModule class TestShiftAbsoluteSTRDS(TestCase): diff --git a/temporal/t.snap/testsuite/test_snap.py b/temporal/t.snap/testsuite/test_snap.py index 36a71c420bf..e46e25c454d 100644 --- a/temporal/t.snap/testsuite/test_snap.py +++ b/temporal/t.snap/testsuite/test_snap.py @@ -10,10 +10,8 @@ import os -import grass.pygrass.modules as pymod import grass.temporal as tgis from grass.gunittest.case import TestCase -from grass.gunittest.gmodules import SimpleModule class TestSnapAbsoluteSTRDS(TestCase): diff --git a/temporal/t.support/testsuite/test_support_str3ds.py b/temporal/t.support/testsuite/test_support_str3ds.py index 928826dbd72..533a9649692 100644 --- a/temporal/t.support/testsuite/test_support_str3ds.py +++ b/temporal/t.support/testsuite/test_support_str3ds.py @@ -10,10 +10,8 @@ import os -import grass.pygrass.modules as pymod import grass.temporal as tgis from grass.gunittest.case import TestCase -from grass.gunittest.gmodules import SimpleModule class TestSupportAbsoluteSTR3DS(TestCase): diff --git a/temporal/t.support/testsuite/test_support_strds.py b/temporal/t.support/testsuite/test_support_strds.py index f5e9ac3aa29..397a4a5e88d 100644 --- a/temporal/t.support/testsuite/test_support_strds.py +++ b/temporal/t.support/testsuite/test_support_strds.py @@ -10,10 +10,8 @@ import os -import grass.pygrass.modules as pymod import grass.temporal as tgis from grass.gunittest.case import TestCase -from grass.gunittest.gmodules import SimpleModule class TestSupportAbsoluteSTRDS(TestCase): diff --git a/temporal/t.support/testsuite/test_support_stvds.py b/temporal/t.support/testsuite/test_support_stvds.py index 2b6e347dead..b7ecc1aee01 100644 --- a/temporal/t.support/testsuite/test_support_stvds.py +++ b/temporal/t.support/testsuite/test_support_stvds.py @@ -10,10 +10,8 @@ import os -import grass.pygrass.modules as pymod import grass.temporal as tgis from grass.gunittest.case import TestCase -from grass.gunittest.gmodules import SimpleModule class TestSupportAbsoluteSTVDS(TestCase): diff --git a/temporal/t.unregister/testsuite/test_unregister.py b/temporal/t.unregister/testsuite/test_unregister.py index a32d33c1f32..b0102095e3d 100644 --- a/temporal/t.unregister/testsuite/test_unregister.py +++ b/temporal/t.unregister/testsuite/test_unregister.py @@ -10,7 +10,6 @@ import os -import grass.pygrass.modules as pymod import grass.temporal as tgis from grass.gunittest.case import TestCase from grass.gunittest.gmodules import SimpleModule diff --git a/vector/v.extract/testsuite/test_v_extract.py b/vector/v.extract/testsuite/test_v_extract.py index 958f153d1a3..c46505a5527 100644 --- a/vector/v.extract/testsuite/test_v_extract.py +++ b/vector/v.extract/testsuite/test_v_extract.py @@ -11,7 +11,6 @@ import os from grass.gunittest.case import TestCase -from grass.gunittest.gmodules import SimpleModule from grass.script.core import read_command TABLE_1 = """cat|MAJORRDS_|ROAD_NAME|MULTILANE|PROPYEAR|OBJECTID|SHAPE_LEN diff --git a/vector/v.fill.holes/examples.ipynb b/vector/v.fill.holes/examples.ipynb index fcc006501e4..a6b3ea746ce 100644 --- a/vector/v.fill.holes/examples.ipynb +++ b/vector/v.fill.holes/examples.ipynb @@ -15,12 +15,10 @@ "metadata": {}, "outputs": [], "source": [ - "import os\n", "import sys\n", "\n", "from IPython.display import Image\n", "\n", - "import grass.script as gs\n", "import grass.jupyter as gj" ] }, diff --git a/vector/v.in.ogr/testsuite/test_v_in_ogr.py b/vector/v.in.ogr/testsuite/test_v_in_ogr.py index 167cd8747e8..31d2ef7ef3b 100644 --- a/vector/v.in.ogr/testsuite/test_v_in_ogr.py +++ b/vector/v.in.ogr/testsuite/test_v_in_ogr.py @@ -4,7 +4,6 @@ """ from grass.gunittest.case import TestCase -from grass.gunittest.gmodules import SimpleModule class TestOgrImport(TestCase): From 058270914eafaf09b4b7e8c49c0590936890d0a2 Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Wed, 23 Oct 2024 17:13:03 -0400 Subject: [PATCH 436/471] wxGUI: Fixed F841 in psmap/ (#4574) --- .flake8 | 6 +-- gui/wxpython/psmap/dialogs.py | 24 +--------- gui/wxpython/psmap/frame.py | 74 +++++++++++++++--------------- gui/wxpython/psmap/instructions.py | 2 +- 4 files changed, 41 insertions(+), 65 deletions(-) diff --git a/.flake8 b/.flake8 index 9985b8e5a42..5d951306ad1 100644 --- a/.flake8 +++ b/.flake8 @@ -24,9 +24,9 @@ per-file-ignores = gui/scripts/d.wms.py: E501 gui/wxpython/image2target/g.gui.image2target.py: E501 gui/wxpython/nviz/*: E722 - gui/wxpython/photo2image/*: E722 - gui/wxpython/photo2image/g.gui.photo2image.py: E501 - gui/wxpython/psmap/*: F841, E266, E722 + gui/wxpython/photo2image/*: F841, E722, E265 + gui/wxpython/photo2image/g.gui.photo2image.py: E501, F841 + gui/wxpython/psmap/*: E501, E722 gui/wxpython/vdigit/*: F841, E722, F405, F403 gui/wxpython/animation/g.gui.animation.py: E501 gui/wxpython/tplot/frame.py: F841, E722 diff --git a/gui/wxpython/psmap/dialogs.py b/gui/wxpython/psmap/dialogs.py index c4784525cc9..f086d65dd36 100644 --- a/gui/wxpython/psmap/dialogs.py +++ b/gui/wxpython/psmap/dialogs.py @@ -5731,8 +5731,6 @@ def updateDialog(self): y = self.unitConv.convert(value=y, fromUnit="inch", toUnit=currUnit) self.positionPanel.position["xCtrl"].SetValue("%5.3f" % x) self.positionPanel.position["yCtrl"].SetValue("%5.3f" % y) - # EN coordinates - e, n = self.textDict["east"], self.textDict["north"] self.positionPanel.position["eCtrl"].SetValue(str(self.textDict["east"])) self.positionPanel.position["nCtrl"].SetValue(str(self.textDict["north"])) @@ -6027,7 +6025,7 @@ def OnImageSelectionChanged(self, event): pImg = PILImage.open(file) img = PilImageToWxImage(pImg) except OSError as e: - GError(message=_("Unable to read file %s") % file) + GError(message=_("Unable to read file %s: %s") % (file, str(e))) self.ClearPreview() return self.SetSizeInfoLabel(img) @@ -6140,15 +6138,6 @@ def update(self): else: self.imageDict["XY"] = False - if self.positionPanel.position["eCtrl"].GetValue(): - e = self.positionPanel.position["eCtrl"].GetValue() - else: - self.imageDict["east"] = self.imageDict["east"] - - if self.positionPanel.position["nCtrl"].GetValue(): - n = self.positionPanel.position["nCtrl"].GetValue() - else: - self.imageDict["north"] = self.imageDict["north"] x, y = PaperMapCoordinates( mapInstr=self.instruction[self.mapId], @@ -6211,7 +6200,6 @@ def updateDialog(self): self.positionPanel.position["xCtrl"].SetValue("%5.3f" % x) self.positionPanel.position["yCtrl"].SetValue("%5.3f" % y) # EN coordinates - e, n = self.imageDict["east"], self.imageDict["north"] self.positionPanel.position["eCtrl"].SetValue(str(self.imageDict["east"])) self.positionPanel.position["nCtrl"].SetValue(str(self.imageDict["north"])) @@ -6526,15 +6514,6 @@ def update(self): else: self.pointDict["XY"] = False - if self.positionPanel.position["eCtrl"].GetValue(): - e = self.positionPanel.position["eCtrl"].GetValue() - else: - self.pointDict["east"] = self.pointDict["east"] - - if self.positionPanel.position["nCtrl"].GetValue(): - n = self.positionPanel.position["nCtrl"].GetValue() - else: - self.pointDict["north"] = self.pointDict["north"] x, y = PaperMapCoordinates( mapInstr=self.instruction[self.mapId], @@ -6590,7 +6569,6 @@ def updateDialog(self): self.positionPanel.position["xCtrl"].SetValue("%5.3f" % x) self.positionPanel.position["yCtrl"].SetValue("%5.3f" % y) # EN coordinates - e, n = self.pointDict["east"], self.pointDict["north"] self.positionPanel.position["eCtrl"].SetValue(str(self.pointDict["east"])) self.positionPanel.position["nCtrl"].SetValue(str(self.pointDict["north"])) diff --git a/gui/wxpython/psmap/frame.py b/gui/wxpython/psmap/frame.py index 1d937483123..e86b277252c 100644 --- a/gui/wxpython/psmap/frame.py +++ b/gui/wxpython/psmap/frame.py @@ -195,7 +195,6 @@ def __init__( self.getInitMap() # image path - env = gs.gisenv() self.imgName = gs.tempfile() # canvas for preview @@ -513,45 +512,44 @@ def OnCmdDone(self, event): env=self.env, ) # wx.BusyInfo does not display the message - busy = wx.BusyInfo(_("Generating preview, wait please"), parent=self) - wx.GetApp().Yield() - try: - im = PILImage.open(event.userData["filename"]) - if self.instruction[self.pageId]["Orientation"] == "Landscape": - import numpy as np - - im_array = np.array(im) - im = PILImage.fromarray(np.rot90(im_array, 3)) - im.save(self.imgName, format="PNG") - except OSError: - del busy - program = self._getGhostscriptProgramName() - dlg = HyperlinkDialog( - self, - title=_("Preview not available"), - message=_( - "Preview is not available probably because Ghostscript is not " - "installed or not on PATH." - ), - hyperlink="https://www.ghostscript.com/releases/gsdnld.html", - hyperlinkLabel=_( - "You can download {program} {arch} version here." - ).format( - program=program, - arch="64bit" if "64" in program else "32bit", - ), - ) - dlg.ShowModal() - dlg.Destroy() - return + with wx.BusyInfo(_("Generating preview, wait please"), parent=self): + wx.GetApp().Yield() + try: + im = PILImage.open(event.userData["filename"]) + if self.instruction[self.pageId]["Orientation"] == "Landscape": + import numpy as np + + im_array = np.array(im) + im = PILImage.fromarray(np.rot90(im_array, 3)) + im.save(self.imgName, format="PNG") + except OSError: + del busy + program = self._getGhostscriptProgramName() + dlg = HyperlinkDialog( + self, + title=_("Preview not available"), + message=_( + "Preview is not available probably because Ghostscript is not " + "installed or not on PATH." + ), + hyperlink="https://www.ghostscript.com/releases/gsdnld.html", + hyperlinkLabel=_( + "You can download {program} {arch} version here." + ).format( + program=program, + arch="64bit" if "64" in program else "32bit", + ), + ) + dlg.ShowModal() + dlg.Destroy() + return - self.book.SetSelection(1) - self.currentPage = 1 - rect = self.previewCanvas.ImageRect() - self.previewCanvas.image = wx.Image(self.imgName, wx.BITMAP_TYPE_PNG) - self.previewCanvas.DrawImage(rect=rect) + self.book.SetSelection(1) + self.currentPage = 1 + rect = self.previewCanvas.ImageRect() + self.previewCanvas.image = wx.Image(self.imgName, wx.BITMAP_TYPE_PNG) + self.previewCanvas.DrawImage(rect=rect) - del busy self.SetStatusText(_("Preview generated"), 0) gs.try_remove(event.userData["instrFile"]) diff --git a/gui/wxpython/psmap/instructions.py b/gui/wxpython/psmap/instructions.py index 845e99b2e6a..82560353806 100644 --- a/gui/wxpython/psmap/instructions.py +++ b/gui/wxpython/psmap/instructions.py @@ -1751,7 +1751,7 @@ def EstimateHeight(self, raster, discrete, fontsize, cols=None, height=None): rinfo = gs.raster_info(raster) if rinfo["datatype"] in {"DCELL", "FCELL"}: - minim, maxim = rinfo["min"], rinfo["max"] + maxim = rinfo["max"] rows = ceil(maxim / cols) else: cat = ( From 3d7b331ba8e059217e8f6c7d1a6b2d13e6e4781e Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Wed, 23 Oct 2024 17:17:00 -0400 Subject: [PATCH 437/471] r.in.srtm: Fix exception handling and add specific error types (#4571) --- .flake8 | 1 - scripts/r.in.srtm/r.in.srtm.py | 5 +++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.flake8 b/.flake8 index 5d951306ad1..7bc3070d15e 100644 --- a/.flake8 +++ b/.flake8 @@ -102,7 +102,6 @@ per-file-ignores = scripts/db.univar/db.univar.py: E501 scripts/d.frame/d.frame.py: E722 scripts/i.pansharpen/i.pansharpen.py: E722, E501 - scripts/r.in.srtm/r.in.srtm.py: E722 scripts/r.fillnulls/r.fillnulls.py: E722 scripts/v.what.strds/v.what.strds.py: E501 # Line too long (esp. module interface definitions) diff --git a/scripts/r.in.srtm/r.in.srtm.py b/scripts/r.in.srtm/r.in.srtm.py index b9f51415ca0..49c7cef9758 100755 --- a/scripts/r.in.srtm/r.in.srtm.py +++ b/scripts/r.in.srtm/r.in.srtm.py @@ -76,6 +76,7 @@ import atexit import grass.script as gs import zipfile as zfile +from grass.exceptions import CalledModuleError tmpl1sec = """BYTEORDER M @@ -227,7 +228,7 @@ def main(): try: zf = zfile.ZipFile(zipfile) zf.extractall() - except: + except (zfile.BadZipfile, zfile.LargeZipFile, PermissionError): gs.fatal(_("Unable to unzip file.")) gs.message(_("Converting input file to BIL...")) @@ -274,7 +275,7 @@ def main(): try: gs.run_command("r.in.gdal", input=bilfile, out=tileout) - except: + except CalledModuleError: gs.fatal(_("Unable to import data")) # nice color table From cc0fb732328f6f3a0137eddcd937c7c3e2badcab Mon Sep 17 00:00:00 2001 From: Moritz Lennert Date: Thu, 24 Oct 2024 17:39:24 +0200 Subject: [PATCH 438/471] i.segment.html: parameter names and links to other modules (#4483) --- imagery/i.segment/i.segment.html | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/imagery/i.segment/i.segment.html b/imagery/i.segment/i.segment.html index e30f21d3743..43d5e5fde68 100644 --- a/imagery/i.segment/i.segment.html +++ b/imagery/i.segment/i.segment.html @@ -100,11 +100,11 @@

    Goodness of Fit

    Mean shift

    Mean shift image segmentation consists of 2 steps: anisotrophic filtering and 2. clustering. For anisotrophic filtering new cell values -are calculated from all pixels not farther than hs pixels away +are calculated from all pixels not farther than radius pixels away from the current pixel and with a spectral difference not larger than hr. That means that pixels that are too different from the current pixel are not considered in the calculation of new pixel values. -hs and hr are the spatial and spectral (range) bandwidths +radius and hr are the spatial and spectral (range) bandwidths for anisotrophic filtering. Cell values are iteratively recalculated (shifted to the segment's mean) until the maximum number of iterations is reached or until the largest shift is smaller than threshold. @@ -251,8 +251,6 @@

    Functionality

    Use of Segmentation Results

      -
    • Improve the optional output from this module, or better yet, add a -module for i.segment.metrics.
    • Providing updates to i.maxlik to ensure the segmentation output can be used as input for the existing classification functionality.
    • @@ -277,6 +275,9 @@

      REFERENCES

      SEE ALSO

      +i.segment.stats (addon), +i.segment.uspo (addon), +i.segment.hierarchical (addon) g.gui.iclass, i.group, i.maxlik, From 0860b87ec7b6f69ae48319bbee151857947cc15e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 24 Oct 2024 23:53:58 -0400 Subject: [PATCH 439/471] CI(deps): Update ruff to v0.7.1 (#4587) --- .github/workflows/python-code-quality.yml | 2 +- .pre-commit-config.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index 94a8d0be26b..9aaa37a7db1 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -36,7 +36,7 @@ jobs: # renovate: datasource=pypi depName=bandit BANDIT_VERSION: "1.7.10" # renovate: datasource=pypi depName=ruff - RUFF_VERSION: "0.7.0" + RUFF_VERSION: "0.7.1" runs-on: ${{ matrix.os }} permissions: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4c82e138950..2f3216877de 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -37,7 +37,7 @@ repos: ) - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.7.0 + rev: v0.7.1 hooks: # Run the linter. - id: ruff From bbe66e460d19d1e6ca3d51694a1c1aaf350bd8ed Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 24 Oct 2024 23:54:27 -0400 Subject: [PATCH 440/471] CI(deps): Update actions/setup-python action to v5.3.0 (#4583) --- .github/workflows/additional_checks.yml | 2 +- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/create_release_draft.yml | 2 +- .github/workflows/pytest.yml | 2 +- .github/workflows/python-code-quality.yml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/additional_checks.yml b/.github/workflows/additional_checks.yml index a2fbfadc36f..76554f24db3 100644 --- a/.github/workflows/additional_checks.yml +++ b/.github/workflows/additional_checks.yml @@ -43,7 +43,7 @@ jobs: exclude: mswindows .*\.bat .*/testsuite/data/.* - name: Set up Python - uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 + uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0 with: python-version: '3.10' diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 9c161300170..d98950b0818 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -42,7 +42,7 @@ jobs: - name: Checkout repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Set up Python - uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 + uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0 with: python-version: '3.x' - name: Install non-Python dependencies diff --git a/.github/workflows/create_release_draft.yml b/.github/workflows/create_release_draft.yml index 6c3597599fc..efefb0fd0c7 100644 --- a/.github/workflows/create_release_draft.yml +++ b/.github/workflows/create_release_draft.yml @@ -35,7 +35,7 @@ jobs: ref: ${{ github.ref }} fetch-depth: 0 - name: Set up Python - uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 + uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0 with: python-version: '3.11' - name: Create output directory diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index b28fb40ad14..3fea65773ce 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -35,7 +35,7 @@ jobs: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Set up Python - uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 + uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0 with: python-version: ${{ matrix.python-version }} cache: pip diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index 9aaa37a7db1..aa6bd33724f 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -57,7 +57,7 @@ jobs: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Set up Python - uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 + uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0 with: python-version: ${{ env.PYTHON_VERSION }} cache: pip From b6514ca8afc5b82a080f3b694a49a8684ff4defc Mon Sep 17 00:00:00 2001 From: Makiko Shukunobe Date: Thu, 24 Oct 2024 23:56:18 -0400 Subject: [PATCH 441/471] tests: Add regression test for r.his (#4572) * Add tests to r.his * Add assertRasterFitsUnivar * Update assertRasterFitsUnivar * Add g.region to make r.univar values consistent * Update raster/r.his/testsuite/test_r_his.py Apply the suggestion. Co-authored-by: Anna Petrasova * Update raster/r.his/testsuite/test_r_his.py Apply the suggestion. Co-authored-by: Anna Petrasova * Update raster/r.his/testsuite/test_r_his.py Black styling --------- Co-authored-by: Anna Petrasova --- raster/r.his/testsuite/test_r_his.py | 91 ++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 raster/r.his/testsuite/test_r_his.py diff --git a/raster/r.his/testsuite/test_r_his.py b/raster/r.his/testsuite/test_r_his.py new file mode 100644 index 00000000000..da56d633b6d --- /dev/null +++ b/raster/r.his/testsuite/test_r_his.py @@ -0,0 +1,91 @@ +from grass.gunittest.case import TestCase +from grass.gunittest.main import test + + +class TestRHis(TestCase): + hue = "elevation" + intensity = "elevation_shaded_50" + saturation = "elevation" + red = "shadedmap_r" + green = "shadedmap_g" + blue = "shadedmap_b" + bgcolor = "none" + + @classmethod + def setUpClass(cls): + cls.use_temp_region() + cls.runModule("g.region", raster="elevation") + cls.elev_shade = "elevation_shaded_relief" + cls.runModule("r.relief", input="elevation", output=cls.elev_shade) + cls.runModule( + "r.mapcalc", expression=f"{cls.intensity} = {cls.elev_shade} * 1.5" + ) + cls.runModule("r.colors", map=cls.intensity, color="grey255") + + @classmethod + def tearDownClass(cls): + cls.runModule( + "g.remove", type="raster", flags="f", name=[cls.intensity, cls.elev_shade] + ) + cls.del_temp_region() + + def tearDown(self): + """Remove d.his generated rasters after each test method""" + self.runModule("g.remove", type="raster", flags="f", pattern="shadedmap_*") + + def test_bgcolor_none(self): + """Test r.his with bgcolor 'none'""" + self.runModule( + "r.his", + hue=self.hue, + intensity=self.intensity, + saturation=self.saturation, + red=self.red, + green=self.green, + blue=self.blue, + bgcolor=self.bgcolor, + ) + + red_value = "null_cells=5696\nmin=3\nmax=255\nmean=156.41168\nstddev=34.434612" + blue_value = "null_cells=5696\nmin=0\nmax=127\nmean=36.05560\nstddev=37.61216" + green_value = "null_cells=5696\nmin=1\nmax=255\nmean=129.62880\nstddev=34.48307" + + self.assertRasterFitsUnivar( + raster=self.red, reference=red_value, precision=1e-5 + ) + self.assertRasterFitsUnivar( + raster=self.blue, reference=blue_value, precision=1e-5 + ) + self.assertRasterFitsUnivar( + raster=self.green, reference=green_value, precision=1e-5 + ) + + def test_with_bgcolor_rgb(self): + """Test r.his with bgcolor '0:0:0'""" + self.runModule( + "r.his", + hue=self.hue, + intensity=self.intensity, + saturation=self.saturation, + red=self.red, + green=self.green, + blue=self.blue, + bgcolor="0:0:0", + ) + + red_value = "null_cells=0\nmin=0\nmax=255\nmean=155.97172\nstddev=35.36988" + blue_value = "null_cells=0\nmin=0\nmax=127\nmean=35.95417\nstddev=37.60774" + green_value = "null_cells=0\nmin=0\nmax=255\nmean=129.26418\nstddev=35.11225" + self.assertRasterFitsUnivar( + raster=self.red, reference=red_value, precision=1e-5 + ) + self.assertRasterFitsUnivar( + raster=self.blue, reference=blue_value, precision=1e-5 + ) + self.assertRasterFitsUnivar( + raster=self.green, reference=green_value, precision=1e-5 + ) + + +if __import__("__main__"): + test() From 5b94314a82de7e9c78fd6044659ea9d6c2323edb Mon Sep 17 00:00:00 2001 From: Markus Neteler Date: Fri, 25 Oct 2024 16:16:09 +0200 Subject: [PATCH 442/471] manual: mention raster semantic labels and multiple http/https fixes (#4591) * r.support/i.band.library/r.semantic.label: better mention raster semantic labels * v.db.reconnect.all: add missing keywords * URLs: selectively change http to https; fix a few broken URLs --- AUTHORS | 2 +- REQUIREMENTS.md | 4 ++-- TODO | 4 ++-- binaryInstall.src | 2 +- configure.ac | 2 +- db/drivers/mysql/grass-mesql.html | 2 +- db/drivers/mysql/grass-mysql.html | 2 +- db/drivers/odbc/INSTALL | 2 +- db/drivers/odbc/grass-odbc.html | 6 ++--- db/drivers/postgres/README | 2 +- db/drivers/postgres/grass-pg.html | 10 ++++---- db/drivers/sqlite/README | 2 +- db/drivers/sqlite/fetch.c | 2 +- db/drivers/sqlite/grass-sqlite.html | 12 +++++----- doc/debugging.txt | 2 +- doc/development/rfc/PSC_guidelines.md | 2 +- doc/development/rfc/PSC_voting_procedures.md | 6 ++--- .../legal_aspects_of_code_contributions.md | 2 +- doc/development/style_guide.md | 4 ++-- doc/gui/wxpython/example/g.gui.example.html | 2 +- doc/vector/TODO | 10 ++++---- general/g.list/g.list.html | 2 +- general/g.version/g.version.html | 4 ++-- gui/wxpython/animation/dialogs.py | 2 +- gui/wxpython/animation/g.gui.animation.html | 2 +- gui/wxpython/core/settings.py | 2 +- gui/wxpython/docs/wxGUI.html | 2 +- gui/wxpython/docs/wxgui_sphinx/src/index.rst | 2 +- gui/wxpython/iclass/dialogs.py | 2 +- gui/wxpython/iclass/g.gui.iclass.html | 6 ++--- gui/wxpython/iscatt/iscatt_core.py | 4 ++-- gui/wxpython/iscatt/plots.py | 2 +- gui/wxpython/mapswipe/g.gui.mapswipe.html | 2 +- gui/wxpython/timeline/frame.py | 2 +- gui/wxpython/timeline/g.gui.timeline.html | 4 ++-- gui/wxpython/tplot/frame.py | 2 +- gui/wxpython/tplot/g.gui.tplot.html | 2 +- imagery/i.atcorr/i.atcorr.html | 4 ++-- imagery/i.biomass/i.biomass.html | 2 +- imagery/i.cluster/i.cluster.html | 2 +- imagery/i.eb.eta/i.eb.eta.html | 6 ++--- imagery/i.eb.evapfr/i.eb.evapfr.html | 6 ++--- imagery/i.eb.hsebal01/i.eb.hsebal01.html | 6 ++--- imagery/i.eb.netrad/i.eb.netrad.html | 4 ++-- .../i.eb.soilheatflux/i.eb.soilheatflux.html | 6 ++--- imagery/i.emissivity/i.emissivity.html | 2 +- imagery/i.fft/i.fft.html | 4 ++-- imagery/i.ifft/i.ifft.html | 2 +- imagery/i.landsat.toar/i.landsat.toar.html | 2 +- imagery/i.ortho.photo/README | 2 +- imagery/i.ortho.photo/lib/TODO | 2 +- imagery/i.rectify/i.rectify.html | 4 ++-- imagery/i.vi/evi2.c | 2 +- imagery/i.vi/i.vi.html | 10 ++++---- imagery/imageryintro.html | 2 +- include/Make/Doxyfile_arch_html.in | 12 +++++----- include/Make/Doxyfile_arch_latex.in | 12 +++++----- include/grass/defs/gprojects.h | 2 +- include/grass/gprojects.h | 2 +- lib/cairodriver/cairodriver.dox | 2 +- lib/db/README | 2 +- lib/db/dbmi_base/default_name.c | 2 +- lib/db/dbmilib.dox | 6 ++--- lib/db/sqlp/sql.html | 8 +++---- lib/gmath/gmathlib.dox | 4 ++-- lib/init/variables.html | 2 +- lib/vector/Vlib/buffer2.c | 2 +- lib/vector/Vlib/legal_vname.c | 2 +- lib/vector/vectorlib_pg.dox | 4 ++-- locale/po/grassmods_ar.po | 2 +- locale/po/grassmods_bn.po | 2 +- locale/po/grassmods_cs.po | 2 +- locale/po/grassmods_de.po | 2 +- locale/po/grassmods_el.po | 2 +- locale/po/grassmods_es.po | 2 +- locale/po/grassmods_fi.po | 2 +- locale/po/grassmods_fr.po | 2 +- locale/po/grassmods_hu.po | 2 +- locale/po/grassmods_id_ID.po | 2 +- locale/po/grassmods_it.po | 2 +- locale/po/grassmods_ja.po | 2 +- locale/po/grassmods_ko.po | 2 +- locale/po/grassmods_lv.po | 2 +- locale/po/grassmods_ml.po | 2 +- locale/po/grassmods_pl.po | 2 +- locale/po/grassmods_pt.po | 2 +- locale/po/grassmods_pt_BR.po | 2 +- locale/po/grassmods_ro.po | 2 +- locale/po/grassmods_ru.po | 2 +- locale/po/grassmods_si.po | 2 +- locale/po/grassmods_sl.po | 2 +- locale/po/grassmods_ta.po | 2 +- locale/po/grassmods_th.po | 2 +- locale/po/grassmods_tr.po | 2 +- locale/po/grassmods_uk.po | 2 +- locale/po/grassmods_vi.po | 2 +- locale/po/grassmods_zh.po | 2 +- locale/po/grassmods_zh_CN.po | 2 +- locale/templates/grassmods.pot | 2 +- macosx/ReadMe.md | 2 +- mswindows/README.html | 2 +- mswindows/external/rbatch/R.bat | 4 ++-- mswindows/external/rbatch/README.grass | 2 +- mswindows/external/rbatch/Rpathset.bat | 2 +- mswindows/external/rbatch/batchfiles.md | 2 +- mswindows/external/rbatch/batchfiles.tex | 2 +- mswindows/external/rbatch/copydir.bat | 2 +- mswindows/external/rbatch/movedir.bat | 2 +- python/grass/docs/src/pygrass_index.rst | 4 ++-- python/grass/docs/src/pygrass_modules.rst | 2 +- python/grass/docs/src/temporal_framework.rst | 2 +- python/grass/gunittest/reporters.py | 2 +- python/grass/imaging/images2gif.py | 2 +- .../grass/pygrass/modules/interface/module.py | 2 +- python/grass/script/utils.py | 2 +- .../ctypesgen/parser/yacc.py | 2 +- .../ctypesgen/printer_json/printer.py | 2 +- raster/r.buildvrt/r.buildvrt.html | 4 ++-- raster/r.composite/r.composite.html | 2 +- raster/r.external.out/r.external.out.html | 2 +- raster/r.external/r.external.html | 2 +- raster/r.fill.stats/r.fill.stats.html | 2 +- raster/r.geomorphon/r.geomorphon.html | 2 +- raster/r.grow.distance/r.grow.distance.html | 4 ++-- raster/r.in.gdal/r.in.gdal.html | 6 ++--- raster/r.in.xyz/r.in.xyz.html | 2 +- raster/r.out.gdal/r.out.gdal.html | 10 ++++---- raster/r.out.mpeg/r.out.mpeg.html | 2 +- raster/r.resamp.filter/r.resamp.filter.html | 4 ++-- .../r.sim/r.sim.sediment/r.sim.sediment.html | 2 +- raster/r.sim/r.sim.water/r.sim.water.html | 2 +- raster/r.stream.extract/r.stream.extract.html | 2 +- raster/r.sun/TODO | 4 ++-- raster/r.sun/r.sun.html | 2 +- raster/r.support/r.support.html | 13 ++++++---- raster/r.surf.fractal/r.surf.fractal.html | 2 +- raster/r.watershed/front/r.watershed.html | 8 +++---- raster3d/r3.flow/r3.flow.html | 2 +- raster3d/r3.out.netcdf/main.c | 2 +- raster3d/r3.out.netcdf/r3.out.netcdf.html | 2 +- scripts/d.polar/d.polar.html | 2 +- scripts/db.in.ogr/db.in.ogr.html | 4 ++-- scripts/i.band.library/i.band.library.html | 10 ++++---- scripts/i.in.spotvgt/i.in.spotvgt.py | 6 ++--- scripts/i.pansharpen/i.pansharpen.html | 2 +- scripts/r.grow/r.grow.html | 4 ++-- scripts/r.in.wms/r.in.wms.html | 2 +- .../r.semantic.label/r.semantic.label.html | 8 +++---- scripts/v.db.dropcolumn/v.db.dropcolumn.py | 2 +- scripts/v.db.join/v.db.join.html | 6 ++--- .../v.db.reconnect.all/v.db.reconnect.all.py | 2 ++ scripts/v.import/v.import.html | 8 +++---- scripts/v.in.e00/v.in.e00.html | 2 +- scripts/v.in.geonames/v.in.geonames.html | 2 +- temporal/t.rast.algebra/t.rast.algebra.html | 4 ++-- temporal/temporalintro.html | 4 ++-- vector/v.buffer/v.buffer.html | 2 +- vector/v.cluster/v.cluster.html | 4 ++-- vector/v.external.out/v.external.out.html | 14 +++++------ vector/v.external/v.external.html | 4 ++-- vector/v.in.dxf/v.in.dxf.html | 2 +- vector/v.kernel/v.kernel.html | 6 ++--- vector/v.label.sa/v.label.sa.html | 2 +- vector/v.net.bridge/v.net.bridge.html | 4 ++-- vector/v.net.centrality/v.net.centrality.html | 2 +- vector/v.net.flow/v.net.flow.html | 2 +- vector/v.net/v.net.html | 2 +- vector/v.out.ascii/v.out.ascii.html | 2 +- vector/v.out.dxf/v.out.dxf.html | 2 +- vector/v.out.ogr/v.out.ogr.html | 24 +++++++++---------- vector/v.out.postgis/v.out.postgis.html | 4 ++-- vector/v.surf.rst/v.surf.rst.html | 2 +- vector/v.univar/main.c | 2 +- vector/v.vol.rst/v.vol.rst.html | 2 +- vector/v.voronoi/v.voronoi.html | 2 +- 175 files changed, 296 insertions(+), 293 deletions(-) diff --git a/AUTHORS b/AUTHORS index a81f5a75e39..952b369702f 100644 --- a/AUTHORS +++ b/AUTHORS @@ -206,7 +206,7 @@ MS-Windows/Cygwin: Huidae Cho Source code Quality assessment system - SOCCER Labs at Ecole Polytechnique de Montreal, Canada http://web.soccerlab.polymtl.ca/grass-evolution/grass-browsers/grass-index-en.html - http://lists.osgeo.org/mailman/listinfo/grass-qa + https://lists.osgeo.org/mailman/listinfo/grass-qa GRASS 5.7/6.0: Primary authors of new source code diff --git a/REQUIREMENTS.md b/REQUIREMENTS.md index 7b2757d38f5..5d173133716 100644 --- a/REQUIREMENTS.md +++ b/REQUIREMENTS.md @@ -49,7 +49,7 @@ Note: also the respective development packages (commonly named `xxx-dev` or [https://facebook.github.io/zstd](https://facebook.github.io/zstd) - **FFTW 2.x or 3.x** (library for computing the Discrete Fourier Transform), required for `i.fft` and `i.ifft` and other modules - [http://www.fftw.org](http://www.fftw.org) + [https://fftw.org](https://fftw.org) - **GEOS** (Geometry Engine library), needed for `v.buffer` and adds extended options to the `v.select` module [https://libgeos.org/](https://libgeos.org/) @@ -81,7 +81,7 @@ Note: also the respective development packages (commonly named `xxx-dev` or - **SQLite libraries** (for the SQLite database interface) [https://www.sqlite.org](https://www.sqlite.org) - **unixODBC** (for the ODBC database interface) - [http://www.unixodbc.org](http://www.unixodbc.org) + [https://www.unixodbc.org](https://www.unixodbc.org) - **R Statistics** (for the R statistical language interface) [https://cran.r-project.org](https://cran.r-project.org) - **FreeType2** (for TrueType font support and `d.text.freetype`) diff --git a/TODO b/TODO index 510d38dfa6b..5750175258e 100644 --- a/TODO +++ b/TODO @@ -39,6 +39,6 @@ Imagery ----------------- See also -http://trac.osgeo.org/grass/wiki/Grass7Planning +https://trac.osgeo.org/grass/wiki/Grass7Planning -http://trac.osgeo.org/grass/wiki/Grass8Planning +https://trac.osgeo.org/grass/wiki/Grass8Planning diff --git a/binaryInstall.src b/binaryInstall.src index 5ab008dd796..2bcb3165327 100755 --- a/binaryInstall.src +++ b/binaryInstall.src @@ -109,7 +109,7 @@ if [ $? -eq 0 ] ; then IFS="$IFSSAVE" if [ ! "$GUNZIP" ] ; then echo "No gunzip installed. Please get from:" - echo " http://www.gnu.org/software/gzip/gzip.html" + echo " https://www.gnu.org/software/gzip/gzip.html" exit fi else diff --git a/configure.ac b/configure.ac index 52a985960c3..fc3403163da 100644 --- a/configure.ac +++ b/configure.ac @@ -15,7 +15,7 @@ # Public License (>=v2). Read the file COPYING that # comes with GRASS for details. # -# MANUAL: http://www.gnu.org/software/autoconf/manual/autoconf.html +# MANUAL: https://www.gnu.org/software/autoconf/manual/autoconf.html # http://savannah.gnu.org/projects/autoconf/ # Website for config.guess, config.sub: # wget http://git.savannah.gnu.org/cgit/config.git/plain/config.guess diff --git a/db/drivers/mysql/grass-mesql.html b/db/drivers/mysql/grass-mesql.html index 8e2aca2155c..e04b30a2a8f 100644 --- a/db/drivers/mysql/grass-mesql.html +++ b/db/drivers/mysql/grass-mesql.html @@ -83,7 +83,7 @@

      AUTHOR

      Credits: Development of the driver was sponsored by -Faunalia (Italy) +Faunalia (Italy) as part of a project for ATAC. diff --git a/db/drivers/mysql/grass-mysql.html b/db/drivers/mysql/grass-mysql.html index 60c7fbe4f78..af8c0273d98 100644 --- a/db/drivers/mysql/grass-mysql.html +++ b/db/drivers/mysql/grass-mysql.html @@ -116,7 +116,7 @@

      SEE ALSO

      Credits

      Development of the driver was sponsored by -Faunalia (Italy) +Faunalia (Italy) as part of a project for ATAC.

      AUTHOR

      diff --git a/db/drivers/odbc/INSTALL b/db/drivers/odbc/INSTALL index 74928ff55a6..3f485ee3f20 100644 --- a/db/drivers/odbc/INSTALL +++ b/db/drivers/odbc/INSTALL @@ -1,4 +1,4 @@ -1. Download, compile, install and configure ODBC (http://www.unixodbc.org/) +1. Download, compile, install and configure ODBC (https://www.unixodbc.org/) 2. Copy ODBC include files to /usr/include/odbc 3. Compile src/libes/dbmi/drivers/odbc 4. Add row for ODBC driver to dbmscap file diff --git a/db/drivers/odbc/grass-odbc.html b/db/drivers/odbc/grass-odbc.html index 7692893ca92..75a5ca578d7 100644 --- a/db/drivers/odbc/grass-odbc.html +++ b/db/drivers/odbc/grass-odbc.html @@ -95,10 +95,10 @@

      Linux

       ConnSettings            = Configuration of an DSN without GUI is described on -http://www.unixodbc.org/odbcinst.html, +https://www.unixodbc.org/odbcinst.html, but odbc.ini and .odbc.ini may be created by the 'ODBCConfig' tool. You can easily view your DSN structure by 'DataManager'. Configuration with -GUI is described on http://www.unixodbc.org/doc/UserManual/ +GUI is described on https://www.unixodbc.org/doc/UserManual/

      To find out about your PostgreSQL protocol, run:

      @@ -159,6 +159,6 @@ 

      SEE ALSO

      db.connect, v.db.connect, -unixODBC web site, +unixODBC web site, SQL support in GRASS GIS diff --git a/db/drivers/postgres/README b/db/drivers/postgres/README index 254a0fd28c9..a2959739f6e 100644 --- a/db/drivers/postgres/README +++ b/db/drivers/postgres/README @@ -19,7 +19,7 @@ Check also for PostgreSQL data types for defining them in GRASS: Supported types in ./globals.h: -(See http://www.postgresql.org/docs/9.4/interactive/datatype.html) +(See https://www.postgresql.org/docs/9.4/interactive/datatype.html) DB_C_TYPE_INT: bit, int2, smallint, int4, int, integer, int8, bigint, serial, oid diff --git a/db/drivers/postgres/grass-pg.html b/db/drivers/postgres/grass-pg.html index 72c292aade4..cb707fa9314 100644 --- a/db/drivers/postgres/grass-pg.html +++ b/db/drivers/postgres/grass-pg.html @@ -6,7 +6,7 @@

      Creating a PostgreSQL database

      A new database is created with createdb, see -the PostgreSQL +the PostgreSQL manual for details.

      Connecting GRASS to PostgreSQL

      @@ -120,7 +120,7 @@

      Geometry Converters

    • e00pg: E00 to PostGIS filter, see also v.in.e00.
    • -
    • GDAL/OGR ogrinfo and ogr2ogr: +
    • GDAL/OGR ogrinfo and ogr2ogr: GIS vector format converter and library, e.g. ArcInfo or SHAPE to PostGIS.
      ogr2ogr -f "PostgreSQL" shapefile ??
    • @@ -141,8 +141,8 @@

      SEE ALSO

      REFERENCES

      diff --git a/db/drivers/sqlite/README b/db/drivers/sqlite/README index b0c59be7d64..955cbede58a 100644 --- a/db/drivers/sqlite/README +++ b/db/drivers/sqlite/README @@ -14,7 +14,7 @@ db.connect driver=sqlite \ The database is created automatically when used first time. That is SQLite feature followed also in the driver. -SQLite uses "type affinity", (http://www.sqlite.org/datatype3.html) +SQLite uses "type affinity", (https://www.sqlite.org/datatype3.html) that means column types are recommended, but not required. If the driver in GRASS has to determine column type, it first reads diff --git a/db/drivers/sqlite/fetch.c b/db/drivers/sqlite/fetch.c index 8e020753293..44a62b4bb4d 100644 --- a/db/drivers/sqlite/fetch.c +++ b/db/drivers/sqlite/fetch.c @@ -130,7 +130,7 @@ int db__driver_fetch(dbCursor *cn, int position, int *more) G_debug(3, "col %d, litetype %d, sqltype %d: val = '%s'", col, litetype, sqltype, text); - /* http://www.sqlite.org/capi3ref.html#sqlite3_column_type + /* https://www.sqlite.org/capi3ref.html#sqlite3_column_type SQLITE_INTEGER 1 SQLITE_FLOAT 2 SQLITE_TEXT 3 diff --git a/db/drivers/sqlite/grass-sqlite.html b/db/drivers/sqlite/grass-sqlite.html index e746f5820c0..ae59def1e52 100644 --- a/db/drivers/sqlite/grass-sqlite.html +++ b/db/drivers/sqlite/grass-sqlite.html @@ -25,8 +25,8 @@

      Supported SQL commands

      All SQL commands supported by SQLite (for limitations, see SQLite help page: -SQL As Understood By SQLite and -Unsupported SQL). +SQL As Understood By SQLite and +Unsupported SQL).

      Operators available in conditions

      @@ -46,7 +46,7 @@

      Browsing table data in DB

      The algorithm uses input parameters set by the user on the diff --git a/imagery/i.eb.eta/i.eb.eta.html b/imagery/i.eb.eta/i.eb.eta.html index ff947cb1b43..7915c7eba1a 100644 --- a/imagery/i.eb.eta/i.eb.eta.html +++ b/imagery/i.eb.eta/i.eb.eta.html @@ -24,7 +24,7 @@

      REFERENCES

      [1] Bastiaanssen, W.G.M., 1995. Estimation of Land surface parameters by remote sensing under clear-sky conditions. PhD thesis, Wageningen University, Wageningen, The Netherlands. -(PDF) +(PDF)

      [2] Chemin Y., Alexandridis T.A., 2001. Improving spatial resolution of ET seasonal for irrigated rice in Zhanghe, China. Asian Journal of Geoinformatics. @@ -33,12 +33,12 @@

      REFERENCES

      [3] Alexandridis T.K., Cherif I., Chemin Y., Silleos N.G., Stavrinos E., Zalidis G.C. Integrated methodology for estimating water use in Mediterranean agricultural areas. Remote Sensing. 2009, 1, 445-465. -(PDF) +(PDF)

      [4] Chemin, Y., 2012. A Distributed Benchmarking Framework for Actual ET Models, in: Irmak, A. (Ed.), Evapotranspiration - Remote Sensing and Modeling. InTech. -(PDF) +(PDF)

      SEE ALSO

      diff --git a/imagery/i.eb.evapfr/i.eb.evapfr.html b/imagery/i.eb.evapfr/i.eb.evapfr.html index 8d6354f36f9..9e745534728 100644 --- a/imagery/i.eb.evapfr/i.eb.evapfr.html +++ b/imagery/i.eb.evapfr/i.eb.evapfr.html @@ -18,7 +18,7 @@

      REFERENCES

      Bastiaanssen, W.G.M., 1995. Estimation of Land surface parameters by remote sensing under clear-sky conditions. PhD thesis, Wageningen University, Wageningen, The Netherlands. -(PDF) +(PDF)

      Bastiaanssen, W.G.M., Molden, D.J., Makin, I.W., 2000. Remote sensing for irrigated agriculture: examples from research and @@ -32,12 +32,12 @@

      REFERENCES

      Zalidis G.C., 2009. Integrated methodology for estimating water use in Mediterranean agricultural areas. Remote Sensing. 1, 445-465. -(PDF) +(PDF)

      Chemin, Y., 2012. A Distributed Benchmarking Framework for Actual ET Models, in: Irmak, A. (Ed.), Evapotranspiration - Remote Sensing and Modeling. InTech. -(PDF) +(PDF)

      SEE ALSO

      diff --git a/imagery/i.eb.hsebal01/i.eb.hsebal01.html b/imagery/i.eb.hsebal01/i.eb.hsebal01.html index 4b6d3953e53..4501af13c3c 100644 --- a/imagery/i.eb.hsebal01/i.eb.hsebal01.html +++ b/imagery/i.eb.hsebal01/i.eb.hsebal01.html @@ -37,7 +37,7 @@

      REFERENCES

      [1] Bastiaanssen, W.G.M., 1995. Estimation of Land surface parameters by remote sensing under clear-sky conditions. PhD thesis, Wageningen University, Wageningen, The Netherlands. -(PDF) +(PDF)

      [2] Chemin Y., Alexandridis T.A., 2001. Improving spatial resolution of ET seasonal for irrigated rice in Zhanghe, China. Asian Journal of @@ -46,12 +46,12 @@

      REFERENCES

      [3] Alexandridis T.K., Cherif I., Chemin Y., Silleos N.G., Stavrinos E., Zalidis G.C. Integrated methodology for estimating water use in Mediterranean agricultural areas. Remote Sensing. 2009, 1, 445-465. -(PDF) +(PDF)

      [4] Chemin, Y., 2012. A Distributed Benchmarking Framework for Actual ET Models, in: Irmak, A. (Ed.), Evapotranspiration - Remote Sensing and Modeling. InTech. -(PDF) +(PDF)

      SEE ALSO

      diff --git a/imagery/i.eb.netrad/i.eb.netrad.html b/imagery/i.eb.netrad/i.eb.netrad.html index 306bb39c90d..c6cba3ad484 100644 --- a/imagery/i.eb.netrad/i.eb.netrad.html +++ b/imagery/i.eb.netrad/i.eb.netrad.html @@ -28,12 +28,12 @@

      REFERENCES

      densities and moisture indicators in composite terrain; a remote sensing approach under clear skies in mediterranean climates. PhD thesis, Wageningen Agricultural Univ., The Netherland, 271 pp. -(PDF) +(PDF)
    • Chemin, Y., 2012. A Distributed Benchmarking Framework for Actual ET Models, in: Irmak, A. (Ed.), Evapotranspiration - Remote Sensing and Modeling. InTech. -(PDF)
    • +(PDF)

    SEE ALSO

    diff --git a/imagery/i.eb.soilheatflux/i.eb.soilheatflux.html b/imagery/i.eb.soilheatflux/i.eb.soilheatflux.html index b948d827da2..ecf62af78e9 100644 --- a/imagery/i.eb.soilheatflux/i.eb.soilheatflux.html +++ b/imagery/i.eb.soilheatflux/i.eb.soilheatflux.html @@ -41,7 +41,7 @@

    REFERENCES

    Bastiaanssen, W.G.M., 1995. Estimation of Land surface parameters by remote sensing under clear-sky conditions. PhD thesis, Wageningen University, Wageningen, The Netherlands. - (PDF) + (PDF)

    Chemin Y., Alexandridis T.A., 2001. Improving spatial resolution of ET seasonal for irrigated rice in Zhanghe, China. Asian Journal of Geoinformatics. 5(1):3-11,2004. @@ -49,12 +49,12 @@

    REFERENCES

    Alexandridis T.K., Cherif I., Chemin Y., Silleos N.G., Stavrinos E., Zalidis G.C. Integrated methodology for estimating water use in Mediterranean agricultural areas. Remote Sensing. 2009, 1, 445-465. -(PDF) +(PDF)

    Chemin, Y., 2012. A Distributed Benchmarking Framework for Actual ET Models, in: Irmak, A. (Ed.), Evapotranspiration - Remote Sensing and Modeling. InTech. -(PDF) +(PDF)

    SEE ALSO

    diff --git a/imagery/i.emissivity/i.emissivity.html b/imagery/i.emissivity/i.emissivity.html index 872ca4fddae..e6419e8f776 100644 --- a/imagery/i.emissivity/i.emissivity.html +++ b/imagery/i.emissivity/i.emissivity.html @@ -21,7 +21,7 @@

    REFERENCES

  • Bastiaanssen, W.G.M., 1995. Estimation of Land surface parameters by remote sensing under clear-sky conditions. PhD thesis, Wageningen University, Wageningen, The Netherlands. - (PDF)
  • + (PDF)
  • Caselles, V., C. Coll, and E. Valor, 1997. Land surface emissivity and temperature determination in the whole HAPEX-Sahel area from AVHRR data. International Journal of Remote diff --git a/imagery/i.fft/i.fft.html b/imagery/i.fft/i.fft.html index abb7453af82..8eb63a9c8ae 100644 --- a/imagery/i.fft/i.fft.html +++ b/imagery/i.fft/i.fft.html @@ -40,12 +40,12 @@

    REFERENCES

    • M. Frigo and S. G. Johnson (1998): "FFTW: An Adaptive Software Architecture -for the FFT". See www.FFTW.org: FFTW is a C subroutine library +for the FFT". See www.FFTW.org: FFTW is a C subroutine library for computing the Discrete Fourier Transform (DFT) in one or more dimensions, of both real and complex data, and of arbitrary input size.
    • John A. Richards, 1986. Remote Sensing Digital Image Analysis, Springer-Verlag.
    • Personal communication, between program author and Ali R. Vali, -Space Research Center, University of Texas, Austin, 1990. +Space Research Center, University of Texas, Austin, 1990.

    SEE ALSO

    diff --git a/imagery/i.ifft/i.ifft.html b/imagery/i.ifft/i.ifft.html index 028f81f5895..eee7bc54e06 100644 --- a/imagery/i.ifft/i.ifft.html +++ b/imagery/i.ifft/i.ifft.html @@ -23,7 +23,7 @@

    REFERENCES

    • M. Frigo and S. G. Johnson (1998): "FFTW: An Adaptive Software -Architecture for the FFT". See www.fftw.org: +Architecture for the FFT". See www.fftw.org: FFTW is a C subroutine library for computing the Discrete Fourier Transform (DFT) in one or more dimensions, of both real and complex data, and of arbitrary input size.
    • diff --git a/imagery/i.landsat.toar/i.landsat.toar.html b/imagery/i.landsat.toar/i.landsat.toar.html index 821f7cb4cfe..4a792c26274 100644 --- a/imagery/i.landsat.toar/i.landsat.toar.html +++ b/imagery/i.landsat.toar/i.landsat.toar.html @@ -235,7 +235,7 @@

      DOS1 example

      Calculation of reflectance values from DN using DOS1 (metadata obtained -from p016r035_7x20020524.met.gz): +from p016r035_7x20020524.met.gz):
       i.landsat.toar input=lsat7_2002. output=lsat7_2002_toar. sensor=tm7 \
      diff --git a/imagery/i.ortho.photo/README b/imagery/i.ortho.photo/README
      index bc8313ce304..51260299d5b 100644
      --- a/imagery/i.ortho.photo/README
      +++ b/imagery/i.ortho.photo/README
      @@ -49,7 +49,7 @@ Workflow description:
       Open Source GIS: A GRASS GIS Approach, Second Edition, 2004
       by Markus Neteler and Helena Mitasova,
       Chapter 10 – PROCESSING OF AERIAL PHOTOS
      -http://grassbook.org/extra/sample-chapter/
      +https://grassbook.org/extra/sample-chapter/
       --> PDF
       
       ######################################################################
      diff --git a/imagery/i.ortho.photo/lib/TODO b/imagery/i.ortho.photo/lib/TODO
      index 792cb581378..df02ac3cf5d 100644
      --- a/imagery/i.ortho.photo/lib/TODO
      +++ b/imagery/i.ortho.photo/lib/TODO
      @@ -30,4 +30,4 @@ Possibly a lot is already done in lib/image3/ ?
       -----------------
       See also
       
      -http://trac.osgeo.org/grass/wiki/Grass7Planning
      +https://trac.osgeo.org/grass/wiki/Grass7Planning
      diff --git a/imagery/i.rectify/i.rectify.html b/imagery/i.rectify/i.rectify.html
      index 6451c8c9be9..7ef374b948e 100644
      --- a/imagery/i.rectify/i.rectify.html
      +++ b/imagery/i.rectify/i.rectify.html
      @@ -11,8 +11,8 @@ 

      DESCRIPTION

      are first, second, and third order polynomial and thin plate spline. Thin plate spline is recommended for ungeoreferenced satellite imagery where ground control points (GCPs) are included. Examples are -NOAA/AVHRR -and ENVISAT +NOAA/AVHRR +and ENVISAT imagery which include throusands of GCPs.

      diff --git a/imagery/i.vi/evi2.c b/imagery/i.vi/evi2.c index c56ad2f62e0..502fba957ab 100644 --- a/imagery/i.vi/evi2.c +++ b/imagery/i.vi/evi2.c @@ -7,7 +7,7 @@ * 2-band enhanced vegetation index without a blue band and its application to * AVHRR data Proc. SPIE 6679, Remote Sensing and Modeling of Ecosystems for * Sustainability IV, 667905 (October 09, 2007) doi:10.1117/12.734933 - * http://dx.doi.org/10.1117/12.734933 + * https://doi.org/10.1117/12.734933 */ double e_vi2(double redchan, double nirchan) { diff --git a/imagery/i.vi/i.vi.html b/imagery/i.vi/i.vi.html index 7e600600678..3597dc5f500 100644 --- a/imagery/i.vi/i.vi.html +++ b/imagery/i.vi/i.vi.html @@ -134,7 +134,7 @@

      Vegetation Indices

      vegetation index without a blue band and its application to AVHRR data. Proc. SPIE 6679, Remote Sensing and Modeling of Ecosystems for Sustainability IV, 667905 (october 09, 2007) -doi:10.1117/12.734933). +doi:10.1117/12.734933).
       evi2( redchan, nirchan )
      @@ -150,7 +150,7 @@ 

      Vegetation Indices

      Gitelson, Anatoly A.; Kaufman, Yoram J.; Merzlyak, Mark N. (1996) Use of a green channel in remote sensing of global vegetation from EOS- MODIS, Remote Sensing of Environment 58 (3), 289-298. -doi:10.1016/s0034-4257(96)00072-7 +doi:10.1016/s0034-4257(96)00072-7
       gari( redchan, nirchan, bluechan, greenchan )
      @@ -508,7 +508,7 @@ 

      Preparation: DN to reflectance

      Calculation of reflectance values from DN using DOS1 (metadata obtained -from p016r035_7x20020524.met.gz): +from p016r035_7x20020524.met.gz):

      @@ -596,8 +596,8 @@ 

      REFERENCES

      densities and moisture indicators in composite terrain; a remote sensing approach under clear skies in mediterranean climates. PhD thesis, Wageningen Agricultural Univ., The Netherland, 271 pp. -(PDF) -
    • Index DataBase: List of available Indices
    • +(PDF) +
    • Index DataBase: List of available Indices

    SEE ALSO

    diff --git a/imagery/imageryintro.html b/imagery/imageryintro.html index 9018b4871ae..c0ef2a48616 100644 --- a/imagery/imageryintro.html +++ b/imagery/imageryintro.html @@ -59,7 +59,7 @@

    Image processing in general

    using the DOS correction method. The more accurate way is using i.atcorr (which supports many satellite sensors). The atmospherically corrected sensor data represent -surface reflectance, +surface reflectance, which ranges theoretically from 0% to 100%. Note that this level of data correction is the proper level of correction to calculate vegetation indices. diff --git a/include/Make/Doxyfile_arch_html.in b/include/Make/Doxyfile_arch_html.in index 3f1e828494c..4c60088412a 100644 --- a/include/Make/Doxyfile_arch_html.in +++ b/include/Make/Doxyfile_arch_html.in @@ -18,7 +18,7 @@ # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See -# http://www.gnu.org/software/libiconv for the list of possible encodings. +# https://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 @@ -573,7 +573,7 @@ LAYOUT_FILE = # containing the references data. This must be a list of .bib files. The # .bib extension is automatically appended if omitted. Using this command # requires the bibtex tool to be installed. See also -# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style +# https://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style # of the bibliography can be controlled using LATEX_BIB_STYLE. To use this # feature you need bibtex and perl available in the search path. @@ -644,7 +644,7 @@ INPUT = . # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built -# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# into libc) for the transcoding. See https://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 @@ -851,7 +851,7 @@ REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source -# tagging system (see http://www.gnu.org/software/global/global.html). You +# tagging system (see https://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO @@ -946,7 +946,7 @@ HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. # Doxygen will adjust the colors in the style sheet and background images # according to this color. Hue is specified as an angle on a colorwheel, -# see http://en.wikipedia.org/wiki/Hue for more information. +# see https://en.wikipedia.org/wiki/Hue for more information. # For instance the value 0 represents red, 60 is yellow, 120 is green, # 180 is cyan, 240 is blue, 300 purple, and 360 is red again. # The allowed range is 0 to 359. @@ -1337,7 +1337,7 @@ LATEX_SOURCE_CODE = NO # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See -# http://en.wikipedia.org/wiki/BibTeX for more info. +# https://en.wikipedia.org/wiki/BibTeX for more info. LATEX_BIB_STYLE = plain diff --git a/include/Make/Doxyfile_arch_latex.in b/include/Make/Doxyfile_arch_latex.in index 1962073feec..25f6ac1592f 100644 --- a/include/Make/Doxyfile_arch_latex.in +++ b/include/Make/Doxyfile_arch_latex.in @@ -18,7 +18,7 @@ # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See -# http://www.gnu.org/software/libiconv for the list of possible encodings. +# https://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 @@ -573,7 +573,7 @@ LAYOUT_FILE = # containing the references data. This must be a list of .bib files. The # .bib extension is automatically appended if omitted. Using this command # requires the bibtex tool to be installed. See also -# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style +# https://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style # of the bibliography can be controlled using LATEX_BIB_STYLE. To use this # feature you need bibtex and perl available in the search path. @@ -644,7 +644,7 @@ INPUT = . # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built -# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# into libc) for the transcoding. See https://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 @@ -851,7 +851,7 @@ REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source -# tagging system (see http://www.gnu.org/software/global/global.html). You +# tagging system (see https://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO @@ -946,7 +946,7 @@ HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. # Doxygen will adjust the colors in the style sheet and background images # according to this color. Hue is specified as an angle on a colorwheel, -# see http://en.wikipedia.org/wiki/Hue for more information. +# see https://en.wikipedia.org/wiki/Hue for more information. # For instance the value 0 represents red, 60 is yellow, 120 is green, # 180 is cyan, 240 is blue, 300 purple, and 360 is red again. # The allowed range is 0 to 359. @@ -1337,7 +1337,7 @@ LATEX_SOURCE_CODE = NO # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See -# http://en.wikipedia.org/wiki/BibTeX for more info. +# https://en.wikipedia.org/wiki/BibTeX for more info. LATEX_BIB_STYLE = plain diff --git a/include/grass/defs/gprojects.h b/include/grass/defs/gprojects.h index 7ab5e47ddd6..e96200c1480 100644 --- a/include/grass/defs/gprojects.h +++ b/include/grass/defs/gprojects.h @@ -65,7 +65,7 @@ void GPJ_free_ellps(struct gpj_ellps *); #ifndef HAVE_PROJ_H /* PROJ.4's private datastructures copied from projects.h as removed from upstream; pending better solution. see: - http://trac.osgeo.org/proj/ticket/98 */ + https://trac.osgeo.org/proj/ticket/98 */ int pj_factors(LP, void *, double, struct FACTORS *); diff --git a/include/grass/gprojects.h b/include/grass/gprojects.h index 7872d6cb4b6..35f795d87ad 100644 --- a/include/grass/gprojects.h +++ b/include/grass/gprojects.h @@ -112,7 +112,7 @@ struct gpj_ellps { #ifndef HAVE_PROJ_H /* PROJ.4's private datastructures copied from projects.h as removed from upstream; pending better solution. see: - http://trac.osgeo.org/proj/ticket/98 */ + https://trac.osgeo.org/proj/ticket/98 */ /* In PROJ 5, the 'struct FACTORS' is back in as 'struct P5_FACTORS', * and old 'struct LP' is now back in as 'PJ_UV' */ diff --git a/lib/cairodriver/cairodriver.dox b/lib/cairodriver/cairodriver.dox index d7ce1ab60e2..ca3ac067e06 100644 --- a/lib/cairodriver/cairodriver.dox +++ b/lib/cairodriver/cairodriver.dox @@ -11,7 +11,7 @@ output, see Cairo website for details. GRASS Cairo display %driver was originally written by Lars Ahlzen (announcement). +href="https://lists.osgeo.org/pipermail/grass-dev/2007-October/033524.html">announcement). \section cairofunctions List of functions diff --git a/lib/db/README b/lib/db/README index 7268b4e182f..9dc8faf9ab6 100644 --- a/lib/db/README +++ b/lib/db/README @@ -26,7 +26,7 @@ DBMI Library Original author: Joel Jones (jjones * zorro.cecer.army.mil | jjones * uiuc.edu ) - Ref: http://lists.osgeo.org/pipermail/grass-dev/1995-February/002015.html + Ref: https://lists.osgeo.org/pipermail/grass-dev/1995-February/002015.html Directory contents: diff --git a/lib/db/dbmi_base/default_name.c b/lib/db/dbmi_base/default_name.c index f9904651a06..839060b44d9 100644 --- a/lib/db/dbmi_base/default_name.c +++ b/lib/db/dbmi_base/default_name.c @@ -123,7 +123,7 @@ int db_set_default_connection(void) * that here?) or $MAPSET/sqlite/mapname.sql as with dbf? */ - /* http://www.sqlite.org/lockingv3.html + /* https://www.sqlite.org/lockingv3.html * When SQLite creates a journal file on Unix, it opens the * directory that contains that file and calls fsync() on the * directory, in an effort to push the directory information to disk. diff --git a/lib/db/dbmilib.dox b/lib/db/dbmilib.dox index fdbac5fab36..0c85715e7f0 100644 --- a/lib/db/dbmilib.dox +++ b/lib/db/dbmilib.dox @@ -15,10 +15,10 @@ Interface) with its integrated drivers. At time of this writing following DBMI drivers for attribute storage are available: - DBF: xBase files (default) - - ODBC: to interface from http://www.unixodbc.org - - PostgreSQL driver (note that PostgreSQL can also be accessed through ODBC): http://www.postgresql.org + - ODBC: to interface from https://www.unixodbc.org + - PostgreSQL driver (note that PostgreSQL can also be accessed through ODBC): https://www.postgresql.org - mySQL: http://mysql.com/ - - SQLite: http://www.sqlite.org + - SQLite: https://www.sqlite.org These drivers are compiled depending on present DB related libraries and 'configure' settings. Only the DBF driver is always compiled. The diff --git a/lib/db/sqlp/sql.html b/lib/db/sqlp/sql.html index 730d0462c2a..eaed6a74b70 100644 --- a/lib/db/sqlp/sql.html +++ b/lib/db/sqlp/sql.html @@ -9,9 +9,9 @@ attribute table.

    GRASS GIS supports various RDBMS -(Relational +(Relational database management system) and embedded databases. SQL -(Structured Query +(Structured Query Language) queries are directly passed to the underlying database system. The set of supported SQL commands depends on the RDMBS and database driver selected. @@ -44,10 +44,10 @@

    Database drivers

  • http://mysql.org/
    odbcData storage via UnixODBC (PostgreSQL, Oracle, etc.)http://www.unixodbc.org/
    https://www.unixodbc.org/
    ogrData storage in OGR fileshttp://gdal.org/
    https://gdal.org/

    NOTES

    diff --git a/lib/gmath/gmathlib.dox b/lib/gmath/gmathlib.dox index a404595de04..77b516b8ebd 100644 --- a/lib/gmath/gmathlib.dox +++ b/lib/gmath/gmathlib.dox @@ -464,8 +464,8 @@ implemented.

    Getting BLAS/LAPACK (one package) if not already provided by the system: -
    http://www.netlib.org/lapack/ -
    http://netlib.bell-labs.com/netlib/master/readme.html +
    https://www.netlib.org/lapack/ +
    https://netlib.bell-labs.com/netlib/master/readme.html

    Pre-compiled binaries of LAPACK/BLAS are provided on many Linux diff --git a/lib/init/variables.html b/lib/init/variables.html index 6fd10d7f173..5dfd113dd85 100644 --- a/lib/init/variables.html +++ b/lib/init/variables.html @@ -392,7 +392,7 @@

    List of selected (GRASS related) shell environment variables

    TMPDIR, TEMP, TMP
    [Various GRASS GIS commands and wxGUI]
    - + The default wxGUI temporary directory is chosen from a platform-dependent list, but the user can control the selection of this directory by setting one of the TMPDIR, TEMP or TMP diff --git a/lib/vector/Vlib/buffer2.c b/lib/vector/Vlib/buffer2.c index 102b1ca350b..ce628d761ef 100644 --- a/lib/vector/Vlib/buffer2.c +++ b/lib/vector/Vlib/buffer2.c @@ -118,7 +118,7 @@ static void elliptic_tangent(double x, double y, double da, double db, /* * !!! This is not line in GRASS' sense. See - * http://en.wikipedia.org/wiki/Line_%28mathematics%29 + * https://en.wikipedia.org/wiki/Line_%28mathematics%29 */ static void line_coefficients(double x1, double y1, double x2, double y2, double *a, double *b, double *c) diff --git a/lib/vector/Vlib/legal_vname.c b/lib/vector/Vlib/legal_vname.c index cd2aa0f14ba..f51df6ace66 100644 --- a/lib/vector/Vlib/legal_vname.c +++ b/lib/vector/Vlib/legal_vname.c @@ -32,7 +32,7 @@ int Vect_legal_filename(const char *s) { /* full list of SQL keywords available at - http://www.postgresql.org/docs/8.2/static/sql-keywords-appendix.html + https://www.postgresql.org/docs/8.2/static/sql-keywords-appendix.html */ static const char *keywords[] = {"and", "or", "not", NULL}; char buf[GNAME_MAX]; diff --git a/lib/vector/vectorlib_pg.dox b/lib/vector/vectorlib_pg.dox index c3a072c6a14..d95b3dec1c2 100644 --- a/lib/vector/vectorlib_pg.dox +++ b/lib/vector/vectorlib_pg.dox @@ -10,14 +10,14 @@ by GRASS Development Team (https://grass.osgeo.org) write PostGIS data directly without any external library (like in the case of \ref vlibOgr). GRASS-PostGIS data provider is implemented using libpq +href="https://www.postgresql.org/docs/9.2/static/libpq.html">libpq library. Note that GRASS-PostGIS data provider is compiled only when GRASS is configured with --with-postgres switch. See the trac +href="https://trac.osgeo.org/grass/wiki/Grass7/VectorLib/PostGISInterface">trac page for more info. \section vlibFn List of functions diff --git a/locale/po/grassmods_ar.po b/locale/po/grassmods_ar.po index b21aefa5d0b..cbb43ec6e9e 100644 --- a/locale/po/grassmods_ar.po +++ b/locale/po/grassmods_ar.po @@ -71377,7 +71377,7 @@ msgid "Either or must be given" msgstr "طبقتين يجب تحديدهم" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:129 -msgid "'gdalinfo' not found, install GDAL tools first (http://www.gdal.org)" +msgid "'gdalinfo' not found, install GDAL tools first (https://gdal.org)" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:146 diff --git a/locale/po/grassmods_bn.po b/locale/po/grassmods_bn.po index 17a11f251b8..f3526d45180 100644 --- a/locale/po/grassmods_bn.po +++ b/locale/po/grassmods_bn.po @@ -65839,7 +65839,7 @@ msgid "Either or must be given" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:129 -msgid "'gdalinfo' not found, install GDAL tools first (http://www.gdal.org)" +msgid "'gdalinfo' not found, install GDAL tools first (https://gdal.org)" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:146 diff --git a/locale/po/grassmods_cs.po b/locale/po/grassmods_cs.po index c6f5f75df4c..0d17c962990 100644 --- a/locale/po/grassmods_cs.po +++ b/locale/po/grassmods_cs.po @@ -68271,7 +68271,7 @@ msgid "Either or must be given" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:129 -msgid "'gdalinfo' not found, install GDAL tools first (http://www.gdal.org)" +msgid "'gdalinfo' not found, install GDAL tools first (https://gdal.org)" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:146 diff --git a/locale/po/grassmods_de.po b/locale/po/grassmods_de.po index 8603cfda94d..7e994f4b379 100644 --- a/locale/po/grassmods_de.po +++ b/locale/po/grassmods_de.po @@ -68907,7 +68907,7 @@ msgid "Either or must be given" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:129 -msgid "'gdalinfo' not found, install GDAL tools first (http://www.gdal.org)" +msgid "'gdalinfo' not found, install GDAL tools first (https://gdal.org)" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:146 diff --git a/locale/po/grassmods_el.po b/locale/po/grassmods_el.po index 5b07d819bd2..e183723b634 100644 --- a/locale/po/grassmods_el.po +++ b/locale/po/grassmods_el.po @@ -66429,7 +66429,7 @@ msgid "Either or must be given" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:129 -msgid "'gdalinfo' not found, install GDAL tools first (http://www.gdal.org)" +msgid "'gdalinfo' not found, install GDAL tools first (https://gdal.org)" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:146 diff --git a/locale/po/grassmods_es.po b/locale/po/grassmods_es.po index 676e7382c2d..ca8a6727270 100644 --- a/locale/po/grassmods_es.po +++ b/locale/po/grassmods_es.po @@ -70595,7 +70595,7 @@ msgid "Either or must be given" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:129 -msgid "'gdalinfo' not found, install GDAL tools first (http://www.gdal.org)" +msgid "'gdalinfo' not found, install GDAL tools first (https://gdal.org)" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:146 diff --git a/locale/po/grassmods_fi.po b/locale/po/grassmods_fi.po index 15504ea84ae..692d9c0d9f2 100644 --- a/locale/po/grassmods_fi.po +++ b/locale/po/grassmods_fi.po @@ -65889,7 +65889,7 @@ msgid "Either or must be given" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:129 -msgid "'gdalinfo' not found, install GDAL tools first (http://www.gdal.org)" +msgid "'gdalinfo' not found, install GDAL tools first (https://gdal.org)" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:146 diff --git a/locale/po/grassmods_fr.po b/locale/po/grassmods_fr.po index f13cc33269f..8f4602fc6ec 100644 --- a/locale/po/grassmods_fr.po +++ b/locale/po/grassmods_fr.po @@ -68934,7 +68934,7 @@ msgid "Either or must be given" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:129 -msgid "'gdalinfo' not found, install GDAL tools first (http://www.gdal.org)" +msgid "'gdalinfo' not found, install GDAL tools first (https://gdal.org)" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:146 diff --git a/locale/po/grassmods_hu.po b/locale/po/grassmods_hu.po index 193acc17813..ee0f0929c11 100644 --- a/locale/po/grassmods_hu.po +++ b/locale/po/grassmods_hu.po @@ -66112,7 +66112,7 @@ msgid "Either or must be given" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:129 -msgid "'gdalinfo' not found, install GDAL tools first (http://www.gdal.org)" +msgid "'gdalinfo' not found, install GDAL tools first (https://gdal.org)" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:146 diff --git a/locale/po/grassmods_id_ID.po b/locale/po/grassmods_id_ID.po index c7159086153..d9a39bafe24 100644 --- a/locale/po/grassmods_id_ID.po +++ b/locale/po/grassmods_id_ID.po @@ -65729,7 +65729,7 @@ msgid "Either or must be given" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:129 -msgid "'gdalinfo' not found, install GDAL tools first (http://www.gdal.org)" +msgid "'gdalinfo' not found, install GDAL tools first (https://gdal.org)" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:146 diff --git a/locale/po/grassmods_it.po b/locale/po/grassmods_it.po index 3120ef6c35b..94368222e52 100644 --- a/locale/po/grassmods_it.po +++ b/locale/po/grassmods_it.po @@ -68276,7 +68276,7 @@ msgid "Either or must be given" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:129 -msgid "'gdalinfo' not found, install GDAL tools first (http://www.gdal.org)" +msgid "'gdalinfo' not found, install GDAL tools first (https://gdal.org)" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:146 diff --git a/locale/po/grassmods_ja.po b/locale/po/grassmods_ja.po index 3444ccaf5db..0dd3735b064 100644 --- a/locale/po/grassmods_ja.po +++ b/locale/po/grassmods_ja.po @@ -67358,7 +67358,7 @@ msgid "Either or must be given" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:129 -msgid "'gdalinfo' not found, install GDAL tools first (http://www.gdal.org)" +msgid "'gdalinfo' not found, install GDAL tools first (https://gdal.org)" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:146 diff --git a/locale/po/grassmods_ko.po b/locale/po/grassmods_ko.po index 5e3acc44adc..8d255c6f5ad 100644 --- a/locale/po/grassmods_ko.po +++ b/locale/po/grassmods_ko.po @@ -67003,7 +67003,7 @@ msgid "Either or must be given" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:129 -msgid "'gdalinfo' not found, install GDAL tools first (http://www.gdal.org)" +msgid "'gdalinfo' not found, install GDAL tools first (https://gdal.org)" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:146 diff --git a/locale/po/grassmods_lv.po b/locale/po/grassmods_lv.po index 717f25bac27..43e408d8721 100644 --- a/locale/po/grassmods_lv.po +++ b/locale/po/grassmods_lv.po @@ -67248,7 +67248,7 @@ msgid "Either or must be given" msgstr "2 līmeņiem jābūt norādītiem" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:129 -msgid "'gdalinfo' not found, install GDAL tools first (http://www.gdal.org)" +msgid "'gdalinfo' not found, install GDAL tools first (https://gdal.org)" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:146 diff --git a/locale/po/grassmods_ml.po b/locale/po/grassmods_ml.po index 201460df7bb..a3693866979 100644 --- a/locale/po/grassmods_ml.po +++ b/locale/po/grassmods_ml.po @@ -65839,7 +65839,7 @@ msgid "Either or must be given" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:129 -msgid "'gdalinfo' not found, install GDAL tools first (http://www.gdal.org)" +msgid "'gdalinfo' not found, install GDAL tools first (https://gdal.org)" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:146 diff --git a/locale/po/grassmods_pl.po b/locale/po/grassmods_pl.po index 8edda3da8e1..dccca2f5d0f 100644 --- a/locale/po/grassmods_pl.po +++ b/locale/po/grassmods_pl.po @@ -67713,7 +67713,7 @@ msgid "Either or must be given" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:129 -msgid "'gdalinfo' not found, install GDAL tools first (http://www.gdal.org)" +msgid "'gdalinfo' not found, install GDAL tools first (https://gdal.org)" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:146 diff --git a/locale/po/grassmods_pt.po b/locale/po/grassmods_pt.po index 63404997a1a..4a28f18ff64 100644 --- a/locale/po/grassmods_pt.po +++ b/locale/po/grassmods_pt.po @@ -67030,7 +67030,7 @@ msgid "Either or must be given" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:129 -msgid "'gdalinfo' not found, install GDAL tools first (http://www.gdal.org)" +msgid "'gdalinfo' not found, install GDAL tools first (https://gdal.org)" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:146 diff --git a/locale/po/grassmods_pt_BR.po b/locale/po/grassmods_pt_BR.po index 673f09c2d8c..d7bcb3c7f41 100644 --- a/locale/po/grassmods_pt_BR.po +++ b/locale/po/grassmods_pt_BR.po @@ -68320,7 +68320,7 @@ msgid "Either or must be given" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:129 -msgid "'gdalinfo' not found, install GDAL tools first (http://www.gdal.org)" +msgid "'gdalinfo' not found, install GDAL tools first (https://gdal.org)" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:146 diff --git a/locale/po/grassmods_ro.po b/locale/po/grassmods_ro.po index 46ac81a4b36..3f4174329a4 100644 --- a/locale/po/grassmods_ro.po +++ b/locale/po/grassmods_ro.po @@ -67214,7 +67214,7 @@ msgid "Either or must be given" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:129 -msgid "'gdalinfo' not found, install GDAL tools first (http://www.gdal.org)" +msgid "'gdalinfo' not found, install GDAL tools first (https://gdal.org)" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:146 diff --git a/locale/po/grassmods_ru.po b/locale/po/grassmods_ru.po index cc9bdc51a78..2630e39adcf 100644 --- a/locale/po/grassmods_ru.po +++ b/locale/po/grassmods_ru.po @@ -66746,7 +66746,7 @@ msgid "Either or must be given" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:129 -msgid "'gdalinfo' not found, install GDAL tools first (http://www.gdal.org)" +msgid "'gdalinfo' not found, install GDAL tools first (https://gdal.org)" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:146 diff --git a/locale/po/grassmods_si.po b/locale/po/grassmods_si.po index 7f42447aee9..b2f15092dbc 100644 --- a/locale/po/grassmods_si.po +++ b/locale/po/grassmods_si.po @@ -65839,7 +65839,7 @@ msgid "Either or must be given" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:129 -msgid "'gdalinfo' not found, install GDAL tools first (http://www.gdal.org)" +msgid "'gdalinfo' not found, install GDAL tools first (https://gdal.org)" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:146 diff --git a/locale/po/grassmods_sl.po b/locale/po/grassmods_sl.po index e2b72f76d56..56c5e73fd60 100644 --- a/locale/po/grassmods_sl.po +++ b/locale/po/grassmods_sl.po @@ -71740,7 +71740,7 @@ msgid "Either or must be given" msgstr "Uporabiš lahko ali 'from_table' ali 'select'" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:129 -msgid "'gdalinfo' not found, install GDAL tools first (http://www.gdal.org)" +msgid "'gdalinfo' not found, install GDAL tools first (https://gdal.org)" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:146 diff --git a/locale/po/grassmods_ta.po b/locale/po/grassmods_ta.po index d76c586fc00..4bd45b751d5 100644 --- a/locale/po/grassmods_ta.po +++ b/locale/po/grassmods_ta.po @@ -65897,7 +65897,7 @@ msgid "Either or must be given" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:129 -msgid "'gdalinfo' not found, install GDAL tools first (http://www.gdal.org)" +msgid "'gdalinfo' not found, install GDAL tools first (https://gdal.org)" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:146 diff --git a/locale/po/grassmods_th.po b/locale/po/grassmods_th.po index bb0898b05f1..a38947544d5 100644 --- a/locale/po/grassmods_th.po +++ b/locale/po/grassmods_th.po @@ -66270,7 +66270,7 @@ msgid "Either or must be given" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:129 -msgid "'gdalinfo' not found, install GDAL tools first (http://www.gdal.org)" +msgid "'gdalinfo' not found, install GDAL tools first (https://gdal.org)" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:146 diff --git a/locale/po/grassmods_tr.po b/locale/po/grassmods_tr.po index 6e58f486013..ef10aa29895 100644 --- a/locale/po/grassmods_tr.po +++ b/locale/po/grassmods_tr.po @@ -67181,7 +67181,7 @@ msgid "Either or must be given" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:129 -msgid "'gdalinfo' not found, install GDAL tools first (http://www.gdal.org)" +msgid "'gdalinfo' not found, install GDAL tools first (https://gdal.org)" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:146 diff --git a/locale/po/grassmods_uk.po b/locale/po/grassmods_uk.po index 8bf34d792ef..ded3fc09c7a 100644 --- a/locale/po/grassmods_uk.po +++ b/locale/po/grassmods_uk.po @@ -66059,7 +66059,7 @@ msgid "Either or must be given" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:129 -msgid "'gdalinfo' not found, install GDAL tools first (http://www.gdal.org)" +msgid "'gdalinfo' not found, install GDAL tools first (https://gdal.org)" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:146 diff --git a/locale/po/grassmods_vi.po b/locale/po/grassmods_vi.po index bfe8eb81909..cb4f0aa95a9 100644 --- a/locale/po/grassmods_vi.po +++ b/locale/po/grassmods_vi.po @@ -66307,7 +66307,7 @@ msgid "Either or must be given" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:129 -msgid "'gdalinfo' not found, install GDAL tools first (http://www.gdal.org)" +msgid "'gdalinfo' not found, install GDAL tools first (https://gdal.org)" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:146 diff --git a/locale/po/grassmods_zh.po b/locale/po/grassmods_zh.po index f5926443d90..3ec8942025d 100644 --- a/locale/po/grassmods_zh.po +++ b/locale/po/grassmods_zh.po @@ -66768,7 +66768,7 @@ msgid "Either or must be given" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:129 -msgid "'gdalinfo' not found, install GDAL tools first (http://www.gdal.org)" +msgid "'gdalinfo' not found, install GDAL tools first (https://gdal.org)" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:146 diff --git a/locale/po/grassmods_zh_CN.po b/locale/po/grassmods_zh_CN.po index 9ba4d09e886..476cc510193 100644 --- a/locale/po/grassmods_zh_CN.po +++ b/locale/po/grassmods_zh_CN.po @@ -65730,7 +65730,7 @@ msgid "Either or must be given" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:129 -msgid "'gdalinfo' not found, install GDAL tools first (http://www.gdal.org)" +msgid "'gdalinfo' not found, install GDAL tools first (https://gdal.org)" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:146 diff --git a/locale/templates/grassmods.pot b/locale/templates/grassmods.pot index c339eb81069..3476f2faa63 100644 --- a/locale/templates/grassmods.pot +++ b/locale/templates/grassmods.pot @@ -65839,7 +65839,7 @@ msgid "Either or must be given" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:129 -msgid "'gdalinfo' not found, install GDAL tools first (http://www.gdal.org)" +msgid "'gdalinfo' not found, install GDAL tools first (https://gdal.org)" msgstr "" #: ../scripts/i.in.spotvgt/i.in.spotvgt.py:146 diff --git a/macosx/ReadMe.md b/macosx/ReadMe.md index ac19f4c57de..86fb1c0ba38 100644 --- a/macosx/ReadMe.md +++ b/macosx/ReadMe.md @@ -230,7 +230,7 @@ build)*: To install the new Python GUI (see [REQUIREMENTS.html](../REQUIREMENTS.html) and [gui/wxpython/README](../gui/wxpython/README), wxpython installer -available at [wxpython.org](http://wxpython.org/)), add this to configure (fill +available at [wxpython.org](https://wxpython.org/)), add this to configure (fill in the correct version at x.x.x.x for the wxpython you have installed): ```bash diff --git a/mswindows/README.html b/mswindows/README.html index 2b6f87db703..91be0f9afbb 100644 --- a/mswindows/README.html +++ b/mswindows/README.html @@ -7,7 +7,7 @@ Instructions how to prepare a WinGRASS package installer has been moved to -the wiki +the wiki page. diff --git a/mswindows/external/rbatch/R.bat b/mswindows/external/rbatch/R.bat index d55a6b395a8..fd28421d126 100644 --- a/mswindows/external/rbatch/R.bat +++ b/mswindows/external/rbatch/R.bat @@ -1,7 +1,7 @@ @Echo OFF :: Software and documentation is (c) 2013 GKX Associates Inc. and -:: licensed under [GPL 2.0](http://www.gnu.org/licenses/gpl-2.0.html). +:: licensed under [GPL 2.0](https://www.gnu.org/licenses/gpl-2.0.html). :: Help is at bottom of script or just run script with single argument: help @@ -520,7 +520,7 @@ goto:eof :Rhelp echo (c) 2013 G. Grothendieck -echo License: GPL 2.0 ( http://www.gnu.org/licenses/gpl-2.0.html ) +echo License: GPL 2.0 ( https://www.gnu.org/licenses/gpl-2.0.html ) echo Launch script for R and associated functions. echo Usage: R.bat [subcommand] [arguments] echo Subcommands where (0) means takes no arguments; (A) means may need Admin priv diff --git a/mswindows/external/rbatch/README.grass b/mswindows/external/rbatch/README.grass index 85f32b7a9be..40bc7a307a7 100644 --- a/mswindows/external/rbatch/README.grass +++ b/mswindows/external/rbatch/README.grass @@ -13,7 +13,7 @@ at svn-revision 104 (2012-08-31). -- -See http://trac.osgeo.org/grass/ticket/1149#comment:9 +See https://trac.osgeo.org/grass/ticket/1149#comment:9 -- diff --git a/mswindows/external/rbatch/Rpathset.bat b/mswindows/external/rbatch/Rpathset.bat index 7873fc01907..590e9a58980 100644 --- a/mswindows/external/rbatch/Rpathset.bat +++ b/mswindows/external/rbatch/Rpathset.bat @@ -1,5 +1,5 @@ :: Software and documentation is (c) 2013 GKX Associates Inc. and -:: licensed under [GPL 2.0](http://www.gnu.org/licenses/gpl-2.0.html). +:: licensed under [GPL 2.0](https://www.gnu.org/licenses/gpl-2.0.html). :: Purpose: setup path to use R, Rtools and other utilities from cmd line. :: diff --git a/mswindows/external/rbatch/batchfiles.md b/mswindows/external/rbatch/batchfiles.md index 2c10e2bcf75..de26ff2748f 100644 --- a/mswindows/external/rbatch/batchfiles.md +++ b/mswindows/external/rbatch/batchfiles.md @@ -3,7 +3,7 @@ G. Grothendieck Software and documentation is (c) 2013 GKX Associates Inc. and licensed -under [GPL 2.0](http://www.gnu.org/licenses/gpl-2.0.html). +under [GPL 2.0](https://www.gnu.org/licenses/gpl-2.0.html). ## Introduction ## diff --git a/mswindows/external/rbatch/batchfiles.tex b/mswindows/external/rbatch/batchfiles.tex index da8d64664b7..acfa8c6e634 100644 --- a/mswindows/external/rbatch/batchfiles.tex +++ b/mswindows/external/rbatch/batchfiles.tex @@ -3,7 +3,7 @@ \section{Windows Batch Files for R} G. Grothendieck Software and documentation is (c) 2013 GKX Associates Inc. and licensed -under \href{http://www.gnu.org/licenses/gpl-2.0.html}{GPL 2.0}. +under \href{https://www.gnu.org/licenses/gpl-2.0.html}{GPL 2.0}. \subsection{Introduction} diff --git a/mswindows/external/rbatch/copydir.bat b/mswindows/external/rbatch/copydir.bat index f346560b3f0..9ab646e6e0c 100644 --- a/mswindows/external/rbatch/copydir.bat +++ b/mswindows/external/rbatch/copydir.bat @@ -1,6 +1,6 @@ @echo off :: Software and documentation is (c) 2013 GKX Associates Inc. and -:: licensed under [GPL 2.0](http://www.gnu.org/licenses/gpl-2.0.html). +:: licensed under [GPL 2.0](https://www.gnu.org/licenses/gpl-2.0.html). setlocal if not "%2"=="" goto:run echo Usage: copydir fromdir todir diff --git a/mswindows/external/rbatch/movedir.bat b/mswindows/external/rbatch/movedir.bat index 104d6a0d5f0..57fb0a7a498 100644 --- a/mswindows/external/rbatch/movedir.bat +++ b/mswindows/external/rbatch/movedir.bat @@ -1,6 +1,6 @@ @echo off :: Software and documentation is (c) 2013 GKX Associates Inc. and -:: licensed under [GPL 2.0](http://www.gnu.org/licenses/gpl-2.0.html). +:: licensed under [GPL 2.0](https://www.gnu.org/licenses/gpl-2.0.html). setlocal if not "%2"=="" goto:run echo Usage: copydir fromdir todir diff --git a/python/grass/docs/src/pygrass_index.rst b/python/grass/docs/src/pygrass_index.rst index 9e782e4c452..844e5578ae8 100644 --- a/python/grass/docs/src/pygrass_index.rst +++ b/python/grass/docs/src/pygrass_index.rst @@ -46,7 +46,7 @@ References Resources Analysis Support System (GRASS) Geographic Information System (GIS)*. ISPRS International Journal of Geo-Information. 2(1):201-219. `doi:10.3390/ijgi2010201 - `_ + `_ * `Python related articles in the GRASS GIS Wiki `_ * `GRASS GIS 8 Programmer's Manual @@ -54,7 +54,7 @@ References This project has been funded with support from the `Google Summer of Code 2012 -`_ +`_ .. Indices and tables diff --git a/python/grass/docs/src/pygrass_modules.rst b/python/grass/docs/src/pygrass_modules.rst index a668739a34f..854119057d6 100644 --- a/python/grass/docs/src/pygrass_modules.rst +++ b/python/grass/docs/src/pygrass_modules.rst @@ -200,4 +200,4 @@ Multiple GRASS modules can be joined into one object by :class:`~pygrass.modules.interface.module.MultiModule`. -.. _Popen: http://docs.python.org/library/subprocess.html#Popen +.. _Popen: https://docs.python.org/library/subprocess.html#Popen diff --git a/python/grass/docs/src/temporal_framework.rst b/python/grass/docs/src/temporal_framework.rst index 7e68de132a9..2443098b26a 100644 --- a/python/grass/docs/src/temporal_framework.rst +++ b/python/grass/docs/src/temporal_framework.rst @@ -414,7 +414,7 @@ Temporal shifting References ---------- -* Gebbert, S., Pebesma, E., 2014. *TGRASS: A temporal GIS for field based environmental modeling*. Environmental Modelling & Software. 2(1):201-219. `doi:10.1016/j.envsoft.2013.11.001 `_ +* Gebbert, S., Pebesma, E., 2014. *TGRASS: A temporal GIS for field based environmental modeling*. Environmental Modelling & Software. 2(1):201-219. `doi:10.1016/j.envsoft.2013.11.001 `_ * `TGRASS related articles in the GRASS GIS Wiki `_ * Supplementary material of the publication *The GRASS GIS Temporal Framework* to be published in diff --git a/python/grass/gunittest/reporters.py b/python/grass/gunittest/reporters.py index 57b0a8fc643..7c93872a656 100644 --- a/python/grass/gunittest/reporters.py +++ b/python/grass/gunittest/reporters.py @@ -107,7 +107,7 @@ def get_source_url(path, revision, line=None): :param revision: SVN revision (should be a number) :param line: line in the file (should be None for directories) """ - tracurl = "http://trac.osgeo.org/grass/browser/" + tracurl = "https://trac.osgeo.org/grass/browser/" if line: return "{tracurl}{path}?rev={revision}#L{line}".format(**locals()) return "{tracurl}{path}?rev={revision}".format(**locals()) diff --git a/python/grass/imaging/images2gif.py b/python/grass/imaging/images2gif.py index 4084dade509..d37ec0c4e32 100644 --- a/python/grass/imaging/images2gif.py +++ b/python/grass/imaging/images2gif.py @@ -58,7 +58,7 @@ Useful links: * http://tronche.com/computer-graphics/gif/ - * http://en.wikipedia.org/wiki/Graphics_Interchange_Format + * https://en.wikipedia.org/wiki/Graphics_Interchange_Format * http://www.w3.org/Graphics/GIF/spec-gif89a.txt """ diff --git a/python/grass/pygrass/modules/interface/module.py b/python/grass/pygrass/modules/interface/module.py index 392d7571015..7f70d82bac1 100644 --- a/python/grass/pygrass/modules/interface/module.py +++ b/python/grass/pygrass/modules/interface/module.py @@ -559,7 +559,7 @@ def __init__(self, cmd, *args, **kargs): # get the xml of the module self.xml = get_cmd_xml.communicate()[0] # transform and parse the xml into an Element class: - # http://docs.python.org/library/xml.etree.elementtree.html + # https://docs.python.org/library/xml.etree.elementtree.html tree = fromstring(self.xml) for e in tree: diff --git a/python/grass/script/utils.py b/python/grass/script/utils.py index 25eb70190c5..0a32a9e7b4e 100644 --- a/python/grass/script/utils.py +++ b/python/grass/script/utils.py @@ -321,7 +321,7 @@ def split(s): # source: -# http://stackoverflow.com/questions/4836710/ +# https://stackoverflow.com/questions/4836710/ # does-python-have-a-built-in-function-for-string-natural-sort/4836734#4836734 def natural_sort(items): """Returns sorted list using natural sort diff --git a/python/libgrass_interface_generator/ctypesgen/parser/yacc.py b/python/libgrass_interface_generator/ctypesgen/parser/yacc.py index f30cadb7a1a..9b57d110231 100644 --- a/python/libgrass_interface_generator/ctypesgen/parser/yacc.py +++ b/python/libgrass_interface_generator/ctypesgen/parser/yacc.py @@ -311,7 +311,7 @@ def restart(self): # certain kinds of advanced parsing situations where the lexer and parser interact with # each other or change states (i.e., manipulation of scope, lexer states, etc.). # - # See: http://www.gnu.org/software/bison/manual/html_node/Default-Reductions.html#Default-Reductions + # See: https://www.gnu.org/software/bison/manual/html_node/Default-Reductions.html#Default-Reductions def set_defaulted_states(self): self.defaulted_states = {} for state, actions in self.action.items(): diff --git a/python/libgrass_interface_generator/ctypesgen/printer_json/printer.py b/python/libgrass_interface_generator/ctypesgen/printer_json/printer.py index ab88af4dc40..4a201aa5702 100755 --- a/python/libgrass_interface_generator/ctypesgen/printer_json/printer.py +++ b/python/libgrass_interface_generator/ctypesgen/printer_json/printer.py @@ -7,7 +7,7 @@ # From: -# http://stackoverflow.com/questions/1036409/recursively-convert-python-object-graph-to-dictionary +# https://stackoverflow.com/questions/1036409/recursively-convert-python-object-graph-to-dictionary def todict(obj, classkey="Klass"): if isinstance(obj, dict): for k in obj.keys(): diff --git a/raster/r.buildvrt/r.buildvrt.html b/raster/r.buildvrt/r.buildvrt.html index d3a6d507bc1..67368179efb 100644 --- a/raster/r.buildvrt/r.buildvrt.html +++ b/raster/r.buildvrt/r.buildvrt.html @@ -20,7 +20,7 @@

    NOTES

    A GRASS virtual raster can be regarded as a simplified version of GDAL's -virtual raster format. +virtual raster format. The GRASS equivalent is simpler because issues like nodata, projection, resolution, resampling, masking are already handled by native GRASS raster routines. @@ -59,7 +59,7 @@

    SEE ALSO

    The equivalent GDAL utility -gdalbuildvrt +gdalbuildvrt

    AUTHOR

    diff --git a/raster/r.composite/r.composite.html b/raster/r.composite/r.composite.html index 4bd8e49cbe4..0ddd72592ea 100644 --- a/raster/r.composite/r.composite.html +++ b/raster/r.composite/r.composite.html @@ -47,7 +47,7 @@

    SEE ALSO

    r.rgb

    -Wikipedia Entry: Floyd-Steinberg dithering +Wikipedia Entry: Floyd-Steinberg dithering

    AUTHOR

    diff --git a/raster/r.external.out/r.external.out.html b/raster/r.external.out/r.external.out.html index baf48466ed3..c4b3bc65af1 100644 --- a/raster/r.external.out/r.external.out.html +++ b/raster/r.external.out/r.external.out.html @@ -70,7 +70,7 @@

    Complete workflow using only external geodata while processing in GRASS GIS<

    REFERENCES

    -GDAL Pages: http://www.gdal.org/
    +GDAL Pages: https://gdal.org/

    SEE ALSO

    diff --git a/raster/r.external/r.external.html b/raster/r.external/r.external.html index f20c3e059d6..1c69b2d3c3f 100644 --- a/raster/r.external/r.external.html +++ b/raster/r.external/r.external.html @@ -62,7 +62,7 @@

    Processing workflow without data import and export

    REFERENCES

    -GDAL Pages: http://www.gdal.org/
    +GDAL Pages: https://gdal.org/

    SEE ALSO

    diff --git a/raster/r.fill.stats/r.fill.stats.html b/raster/r.fill.stats/r.fill.stats.html index 3151d2c245a..fdc60119243 100644 --- a/raster/r.fill.stats/r.fill.stats.html +++ b/raster/r.fill.stats/r.fill.stats.html @@ -509,7 +509,7 @@

    SEE ALSO

    -Inverse Distance Weighting in Wikipedia +Inverse Distance Weighting in Wikipedia

    AUTHOR

    diff --git a/raster/r.geomorphon/r.geomorphon.html b/raster/r.geomorphon/r.geomorphon.html index 0eb675451f3..54af307b007 100644 --- a/raster/r.geomorphon/r.geomorphon.html +++ b/raster/r.geomorphon/r.geomorphon.html @@ -187,7 +187,7 @@

    REFERENCES

    109-112 (PDF)
  • Jasiewicz, J., Stepinski, T., 2013, Geomorphons - a pattern recognition approach to classification and mapping of landforms, -Geomorphology, vol. 182, 147-156 (DOI: 10.1016/j.geomorph.2012.11.005)
  • +Geomorphology, vol. 182, 147-156 (DOI: 10.1016/j.geomorph.2012.11.005)

    SEE ALSO

    diff --git a/raster/r.grow.distance/r.grow.distance.html b/raster/r.grow.distance/r.grow.distance.html index bc2788aae44..ee5e276a550 100644 --- a/raster/r.grow.distance/r.grow.distance.html +++ b/raster/r.grow.distance/r.grow.distance.html @@ -127,9 +127,9 @@

    SEE ALSO

    -Wikipedia Entry: +Wikipedia Entry: Euclidean Metric
    -Wikipedia Entry: +Wikipedia Entry: Manhattan Metric
    diff --git a/raster/r.in.gdal/r.in.gdal.html b/raster/r.in.gdal/r.in.gdal.html index f21f5682226..5af167a78e0 100644 --- a/raster/r.in.gdal/r.in.gdal.html +++ b/raster/r.in.gdal/r.in.gdal.html @@ -20,7 +20,7 @@

    GDAL supported raster formats

    Full details on all GDAL supported formats are available at:

    -http://www.gdal.org/formats_list.html +https://gdal.org/formats_list.html

    Selected formats out of the more than 140 supported formats: @@ -363,7 +363,7 @@

    GLOBE DEM

    Raster file import over network

    Since GDAL 2.x it is possible to import raster data over the network -(see GDAL Virtual File Systems) +(see GDAL Virtual File Systems) including Cloud Optimized GeoTIFF, i.e. access uncompressed and compressed raster data via a http(s) or ftp connection. As an example the import of the global SRTMGL1 V003 tiles at 1 arc second (about 30 meters) @@ -398,7 +398,7 @@

    HDF

    REFERENCES

    -GDAL Pages: http://www.gdal.org/ +GDAL Pages: https://gdal.org/

    SEE ALSO

    diff --git a/raster/r.in.xyz/r.in.xyz.html b/raster/r.in.xyz/r.in.xyz.html index 15d9f3565aa..42e5e1ee7f8 100644 --- a/raster/r.in.xyz/r.in.xyz.html +++ b/raster/r.in.xyz/r.in.xyz.html @@ -245,7 +245,7 @@

    Import of x,y,z ASCII into DEM

    Import of LiDAR data and DEM creation

    -Import the Jockey's +Import the Jockey's Ridge, NC, LIDAR dataset (compressed file "lidaratm2.txt.gz"), and process it into a clean DEM: diff --git a/raster/r.out.gdal/r.out.gdal.html b/raster/r.out.gdal/r.out.gdal.html index 9cb5b9d8156..06d269944c5 100644 --- a/raster/r.out.gdal/r.out.gdal.html +++ b/raster/r.out.gdal/r.out.gdal.html @@ -9,7 +9,7 @@

    DESCRIPTION

    (createopt="TFW=YES,COMPRESS=DEFLATE").

    For possible createopt and metaopt parameters please consult the individual -supported formats +supported formats pages on the GDAL website. The createopt parameter may be used to create TFW or World files ("TFW=YES","WORLDFILE=ON"). @@ -25,7 +25,7 @@

    DESCRIPTION

    SUPPORTED RASTER FORMATS

    -The set of supported +The set of supported raster formats written by r.out.gdal depends on the local GDAL installation, printed with the -l flag. Available may be (incomplete list):

    @@ -66,9 +66,9 @@

    NOTES

    Moreover, some GDAL-supported formats do not support all the data types possible in GDAL and GRASS. Use r.info to check the data type and range for your GRASS raster, refer to specific -format documentation (on the GDAL website), +format documentation (on the GDAL website), format vendor's documentation, and e.g. the Wikipedia article - + Typical boundaries of primitive integral types for details. @@ -368,7 +368,7 @@

    GDAL RELATED ERROR MESSAGES

    REFERENCES

    -GDAL Pages: https://gdal.org +GDAL Pages: https://gdal.org

    SEE ALSO

    diff --git a/raster/r.out.mpeg/r.out.mpeg.html b/raster/r.out.mpeg/r.out.mpeg.html index e035fce04c5..eb282acbbd9 100644 --- a/raster/r.out.mpeg/r.out.mpeg.html +++ b/raster/r.out.mpeg/r.out.mpeg.html @@ -2,7 +2,7 @@

    DESCRIPTION

    r.out.mpeg is a tool for combining a series of GRASS raster maps into a single MPEG-1 -(Motion +(Motion Pictures Experts Group) format file. MPEG-1 is a "lossy" video compression format, so the quality of each resulting frame of the animation will be much diminished from the diff --git a/raster/r.resamp.filter/r.resamp.filter.html b/raster/r.resamp.filter/r.resamp.filter.html index 0690a090b74..2872efd8774 100644 --- a/raster/r.resamp.filter/r.resamp.filter.html +++ b/raster/r.resamp.filter/r.resamp.filter.html @@ -14,7 +14,7 @@

    DESCRIPTION

    r.resamp.filter implements FIR (finite impulse response) filtering. All of the functions are low-pass filters, as they are symmetric. See -Wikipedia: Window function +Wikipedia: Window function for examples of common window functions and their frequency responses.

    @@ -63,7 +63,7 @@

    NOTES

    cover 3 times as large a time interval) as lanczos1 in order to get a similar frequency response (higher-order filters will fall off faster, but the frequency at which the fall-off starts should be the same). See -Wikipedia: Lanczos-kernel.svg +Wikipedia: Lanczos-kernel.svg for an illustration. If both graphs were drawn on the same axes, they would have roughly the same shape, but the a=3 window would have a longer tail. By scaling the axes to the same width, the a=3 window has a narrower diff --git a/raster/r.sim/r.sim.sediment/r.sim.sediment.html b/raster/r.sim/r.sim.sediment/r.sim.sediment.html index 025fa3f72b1..caebb9c308c 100644 --- a/raster/r.sim/r.sim.sediment/r.sim.sediment.html +++ b/raster/r.sim/r.sim.sediment/r.sim.sediment.html @@ -71,7 +71,7 @@

    REFERENCES

    In: Landscape erosion and landscape evolution modeling, Harmon R. and Doe W. eds., Kluwer Academic/Plenum Publishers, pp. 321-347.

    - + Neteler, M. and Mitasova, H., 2008, Open Source GIS: A GRASS GIS Approach. Third Edition. The International Series in Engineering and Computer Science: Volume 773. Springer New York Inc, p. 406. diff --git a/raster/r.sim/r.sim.water/r.sim.water.html b/raster/r.sim/r.sim.water/r.sim.water.html index 3c920473ca9..79cef1bfadc 100644 --- a/raster/r.sim/r.sim.water/r.sim.water.html +++ b/raster/r.sim/r.sim.water/r.sim.water.html @@ -237,7 +237,7 @@

    REFERENCES

    April 2015
  • Neteler, M. and Mitasova, H., 2008, -Open Source GIS: A GRASS GIS Approach. Third Edition. +Open Source GIS: A GRASS GIS Approach. Third Edition. The International Series in Engineering and Computer Science: Volume 773. Springer New York Inc, p. 406. diff --git a/raster/r.stream.extract/r.stream.extract.html b/raster/r.stream.extract/r.stream.extract.html index 22877936c93..acc9030d0b0 100644 --- a/raster/r.stream.extract/r.stream.extract.html +++ b/raster/r.stream.extract/r.stream.extract.html @@ -258,7 +258,7 @@

    REFERENCES

  • Holmgren, P. (1994). Multiple flow direction algorithms for runoff modelling in grid based elevation models: An empirical evaluation. -Hydrological Processes Vol 8(4), pp 327-334. DOI: 10.1002/hyp.3360080405
  • +Hydrological Processes Vol 8(4), pp 327-334. DOI: 10.1002/hyp.3360080405
  • Montgomery, D.R., Foufoula-Georgiou, E. (1993). Channel network source representation using digital elevation models. Water Resources Research Vol 29(12), pp 3925-3934.
  • diff --git a/raster/r.sun/TODO b/raster/r.sun/TODO index 3e54873977e..4d31c0314df 100644 --- a/raster/r.sun/TODO +++ b/raster/r.sun/TODO @@ -16,10 +16,10 @@ Update https://grasswiki.osgeo.org/wiki/R.sun #### -Fix http://trac.osgeo.org/grass/ticket/498 +Fix https://trac.osgeo.org/grass/ticket/498 pseudo-data test-case -http://trac.osgeo.org/grass/ticket/498#comment:22 +https://trac.osgeo.org/grass/ticket/498#comment:22 #spearfish (further north than NC so more defined shadows) g.region -d r.mapcalc "undulates = (2 + sin( row() * 2 ) + cos( col() * 2 )) * 500" diff --git a/raster/r.sun/r.sun.html b/raster/r.sun/r.sun.html index 189633f9f48..ec43813d51c 100644 --- a/raster/r.sun/r.sun.html +++ b/raster/r.sun/r.sun.html @@ -345,7 +345,7 @@

    REFERENCES

  • Neteler, M., Mitasova, H. (2002): Open Source GIS: A GRASS GIS Approach, Kluwer Academic Publishers. (Appendix explains formula; -r.sun script download) +r.sun script download)
  • Page, J. ed. (1986). Prediction of solar radiation on inclined surfaces. Solar energy R&D in the European Community, series F - Solar radiation data, diff --git a/raster/r.support/r.support.html b/raster/r.support/r.support.html index 111e46f653d..13fdf2a26af 100644 --- a/raster/r.support/r.support.html +++ b/raster/r.support/r.support.html @@ -5,12 +5,13 @@

    DESCRIPTION

    history, semantic label elements and title is supported. Category labels can also be copied from another raster map. -

    Raster band management

    +

    Raster semantic labels and band management

    + Raster semantic label concept is similar to dimension name in other GIS and -remote sensing applications. Most common usage will be assigning a -remote sensing platform sensor band ID to the raster, although any -identifier is supported. Raster semantic label is suggested to work with -imagery classification tools.
    +remote sensing applications. Most common usage will be assigning a remote +sensing platform sensor band identifier to the raster map metadata, although +any identifier is supported (see i.band.library). +Raster semantic label is suggested to work with imagery classification tools.

    EXAMPLES

    @@ -57,12 +58,14 @@

    NOTES

    SEE ALSO

    +i.band.library, r.category, r.describe, r.info, r.null, r.region, r.report, +r.semantic.label, r.timestamp diff --git a/raster/r.surf.fractal/r.surf.fractal.html b/raster/r.surf.fractal/r.surf.fractal.html index d564cbec4e1..2a2a3e3a060 100644 --- a/raster/r.surf.fractal/r.surf.fractal.html +++ b/raster/r.surf.fractal/r.surf.fractal.html @@ -11,7 +11,7 @@

    DESCRIPTION

    NOTE

    -This module requires the FFTW library +This module requires the FFTW library for computing Discrete Fourier Transforms.

    EXAMPLE

    diff --git a/raster/r.watershed/front/r.watershed.html b/raster/r.watershed/front/r.watershed.html index abe352a6948..d9a226412af 100644 --- a/raster/r.watershed/front/r.watershed.html +++ b/raster/r.watershed/front/r.watershed.html @@ -520,7 +520,7 @@

    REFERENCES

  • Holmgren P. (1994). Multiple flow direction algorithms for runoff modelling in grid based elevation models: An empirical evaluation. Hydrological Processes Vol 8(4), 327-334.
    -DOI: 10.1002/hyp.3360080405 +DOI: 10.1002/hyp.3360080405
  • Kinner D., Mitasova H., Harmon R., Toma L., Stallard R. (2005). GIS-based Stream Network Analysis for The Chagres River Basin, @@ -535,19 +535,19 @@

    REFERENCES

  • Metz M., Mitasova H., Harmon R. (2011). Efficient extraction of drainage networks from massive, radar-based elevation models with least cost path search, Hydrol. Earth Syst. Sci. Vol 15, 667-678.
    -DOI: 10.5194/hess-15-667-2011 +DOI: 10.5194/hess-15-667-2011
  • Moore I.D., Grayson R.B., Ladson A.R. (1991). Digital terrain modelling: a review of hydrogical, geomorphological, and biological applications, Hydrological Processes, Vol 5(1), 3-30
    -DOI: 10.1002/hyp.3360050103 +DOI: 10.1002/hyp.3360050103
  • Quinn P., K. Beven K., Chevallier P., Planchon O. (1991). The prediction of hillslope flow paths for distributed hydrological modelling using Digital Elevation Models, Hydrological Processes Vol 5(1), p.59-79.
    -DOI: 10.1002/hyp.3360050106 +DOI: 10.1002/hyp.3360050106
  • Weltz M. A., Renard K.G., Simanton J. R. (1987). Revised Universal Soil Loss Equation for Western Rangelands, U.S.A./Mexico Symposium of diff --git a/raster3d/r3.flow/r3.flow.html b/raster3d/r3.flow/r3.flow.html index f4f9510e7df..7a7c60fd21d 100644 --- a/raster3d/r3.flow/r3.flow.html +++ b/raster3d/r3.flow/r3.flow.html @@ -41,7 +41,7 @@

    Attributes

    NOTES

    r3.flow uses Runge-Kutta with adaptive step size -(Cash-Karp method). +(Cash-Karp method).

    EXAMPLES

    diff --git a/raster3d/r3.out.netcdf/main.c b/raster3d/r3.out.netcdf/main.c index 6bcdcbaf9f5..144c9085eef 100644 --- a/raster3d/r3.out.netcdf/main.c +++ b/raster3d/r3.out.netcdf/main.c @@ -17,7 +17,7 @@ * here: * http://cf-pcmdi.llnl.gov/documents/cf-conventions/1.6/cf-conventions.html#coordinate-system * https://cf-pcmdi.llnl.gov/trac/wiki/Cf2CrsWkt - * http://trac.osgeo.org/gdal/wiki/NetCDF_ProjectionTestingStatus + * https://trac.osgeo.org/gdal/wiki/NetCDF_ProjectionTestingStatus * *****************************************************************************/ diff --git a/raster3d/r3.out.netcdf/r3.out.netcdf.html b/raster3d/r3.out.netcdf/r3.out.netcdf.html index 9fb256a06d3..2dc69701d4d 100644 --- a/raster3d/r3.out.netcdf/r3.out.netcdf.html +++ b/raster3d/r3.out.netcdf/r3.out.netcdf.html @@ -22,7 +22,7 @@

    NOTES

    Spatial coordinates are exported as cell centered coordinates. The projection can be optionally stored in the metadata as crs attributes . The netCDF projection metadata storage follows the spatial_ref GDAL/netCDF suggestion -here +here and the netCDF CF 1.6 convention here using WKT projection information. Additional a PROJ string is diff --git a/scripts/d.polar/d.polar.html b/scripts/d.polar/d.polar.html index fad50649371..d1bbc2e6586 100644 --- a/scripts/d.polar/d.polar.html +++ b/scripts/d.polar/d.polar.html @@ -62,7 +62,7 @@

    REFERENCES

    J. Hofierka, H. Mitasova, and M. Neteler (2009): Terrain parameterization in GRASS. In T. Hengl and H.I. Reuter, editors, Geomorphometry: concepts, software, applications. Elsevier -(DOI) +(DOI)

    AUTHORS

    diff --git a/scripts/db.in.ogr/db.in.ogr.html b/scripts/db.in.ogr/db.in.ogr.html index 322f22840f0..70e7dddc1b1 100644 --- a/scripts/db.in.ogr/db.in.ogr.html +++ b/scripts/db.in.ogr/db.in.ogr.html @@ -1,7 +1,7 @@

    DESCRIPTION

    db.in.ogr imports attribute tables in various formats as -supported by the OGR library +supported by the OGR library on the local system (DBF, CSV, PostgreSQL, SQLite, MySQL, ODBC, etc.). Optionally a unique key (ID) column can be added to the table. @@ -12,7 +12,7 @@

    Import CSV file

    Limited type recognition can be done for Integer, Real, String, Date, Time and DateTime columns through a descriptive file with same name as the CSV file, but .csvt extension -(see details here). +(see details here).
     # NOTE: create koeppen_gridcode.csvt first for automated type recognition
    diff --git a/scripts/i.band.library/i.band.library.html b/scripts/i.band.library/i.band.library.html
    index b5d98d2607b..196f1319e3a 100644
    --- a/scripts/i.band.library/i.band.library.html
    +++ b/scripts/i.band.library/i.band.library.html
    @@ -95,11 +95,11 @@ 

    Band reference and semantic label relation

    NOTES

    -Semantic label concept is supported by temporal GRASS modules, -see t.register, -t.rast.list, -t.info -and t.rast.mapcalc +Semantic label concept is supported by temporal GRASS modules, see +t.register, +t.rast.list, +t.info +and t.rast.mapcalc modules for examples.

    Image collections

    diff --git a/scripts/i.in.spotvgt/i.in.spotvgt.py b/scripts/i.in.spotvgt/i.in.spotvgt.py index 666f3721761..9885caea5ec 100755 --- a/scripts/i.in.spotvgt/i.in.spotvgt.py +++ b/scripts/i.in.spotvgt/i.in.spotvgt.py @@ -20,7 +20,7 @@ ############################################################################# # # REQUIREMENTS: -# - gdal: http://www.gdal.org +# - gdal: https://gdal.org # # Notes: # * According to the faq (http://www.vgt.vito.be/faq/faq.html), SPOT vegetation @@ -125,9 +125,7 @@ def main(): # check for gdalinfo (just to check if installation is complete) if not gs.find_program("gdalinfo", "--help"): - gs.fatal( - _("'gdalinfo' not found, install GDAL tools first (http://www.gdal.org)") - ) + gs.fatal(_("'gdalinfo' not found, install GDAL tools first (https://gdal.org)")) pid = str(os.getpid()) tmpfile = gs.tempfile() diff --git a/scripts/i.pansharpen/i.pansharpen.html b/scripts/i.pansharpen/i.pansharpen.html index 9a50b84bc4e..0db14283be9 100644 --- a/scripts/i.pansharpen/i.pansharpen.html +++ b/scripts/i.pansharpen/i.pansharpen.html @@ -235,7 +235,7 @@

    REFERENCES

  • Neteler, M, D. Grasso, I. Michelazzi, L. Miori, S. Merler, and C. Furlanello (2005). An integrated toolbox for image registration, fusion and classification. International Journal of Geoinformatics, 1(1):51-61 - (PDF) + (PDF)
  • Pohl, C, and J.L van Genderen (1998). Multisensor image fusion in remote sensing: concepts, methods and application. Int. J. of Rem. Sens., 19, 823-854. diff --git a/scripts/r.grow/r.grow.html b/scripts/r.grow/r.grow.html index cbf0088ca3c..b1b037fe5bd 100644 --- a/scripts/r.grow/r.grow.html +++ b/scripts/r.grow/r.grow.html @@ -80,8 +80,8 @@

    SEE ALSO

    r.patch
    -

    Wikipedia Entry: Euclidean Metric
    -Wikipedia Entry: Manhattan Metric +

    Wikipedia Entry: Euclidean Metric
    +Wikipedia Entry: Manhattan Metric

    AUTHORS

    diff --git a/scripts/r.in.wms/r.in.wms.html b/scripts/r.in.wms/r.in.wms.html index ff95f3851ea..0031627991c 100644 --- a/scripts/r.in.wms/r.in.wms.html +++ b/scripts/r.in.wms/r.in.wms.html @@ -22,7 +22,7 @@

    NOTES

    When using GDAL WMS driver (driver=WMS_GDAL), the GDAL library needs to be built with WMS support, -see GDAL WMS manual page +see GDAL WMS manual page for details.

    Tiled WMS

    diff --git a/scripts/r.semantic.label/r.semantic.label.html b/scripts/r.semantic.label/r.semantic.label.html index 9ebcfce10a6..45ac7b9373e 100644 --- a/scripts/r.semantic.label/r.semantic.label.html +++ b/scripts/r.semantic.label/r.semantic.label.html @@ -25,10 +25,10 @@

    NOTES

    Semantic labels are supported by temporal GRASS modules. Name of STRDS can be extended by band identifier in order to filter the result by a semantic label. See -t.register, -t.rast.list, -t.info -and t.rast.mapcalc +t.register, +t.rast.list, +t.info +and t.rast.mapcalc modules for examples.

    EXAMPLES

    diff --git a/scripts/v.db.dropcolumn/v.db.dropcolumn.py b/scripts/v.db.dropcolumn/v.db.dropcolumn.py index 298a29133b3..fd34f36893f 100755 --- a/scripts/v.db.dropcolumn/v.db.dropcolumn.py +++ b/scripts/v.db.dropcolumn/v.db.dropcolumn.py @@ -86,7 +86,7 @@ def main(): if driver == "sqlite": # echo "Using special trick for SQLite" - # http://www.sqlite.org/faq.html#q11 + # https://www.sqlite.org/faq.html#q11 colnames = [] coltypes = [] for f in gs.db_describe(table, database=database, driver=driver)["cols"]: diff --git a/scripts/v.db.join/v.db.join.html b/scripts/v.db.join/v.db.join.html index e44705c55b5..436c86a7d43 100644 --- a/scripts/v.db.join/v.db.join.html +++ b/scripts/v.db.join/v.db.join.html @@ -16,8 +16,8 @@

    EXAMPLES

    Exercise to join North Carolina geological classes from a CSV table to the "geology" map of the North Carolina sample dataset (requires download -of legend CSV file nc_geology.csv -from External data for NC sample dataset): +of legend CSV file nc_geology.csv +from External data for NC sample dataset):
     # check original map attributes
    @@ -49,7 +49,7 @@ 

    EXAMPLES

    Soil map table join

    Joining the soil type explanations from table soils_legend -into the Spearfish soils map (download legend): +into the Spearfish soils map (download legend):
     g.copy vect=soils,mysoils
    diff --git a/scripts/v.db.reconnect.all/v.db.reconnect.all.py b/scripts/v.db.reconnect.all/v.db.reconnect.all.py
    index 9a9d46356a9..7a422f47f7f 100755
    --- a/scripts/v.db.reconnect.all/v.db.reconnect.all.py
    +++ b/scripts/v.db.reconnect.all/v.db.reconnect.all.py
    @@ -19,6 +19,8 @@
     # % keyword: vector
     # % keyword: attribute table
     # % keyword: database
    +# % keyword: DBF
    +# % keyword: SQLite
     # %end
     # %flag
     # % key: c
    diff --git a/scripts/v.import/v.import.html b/scripts/v.import/v.import.html
    index a530f5f3662..281136939e2 100644
    --- a/scripts/v.import/v.import.html
    +++ b/scripts/v.import/v.import.html
    @@ -1,7 +1,7 @@
     

    DESCRIPTION

    v.import imports vector data from files and database connections -supported by the OGR library into the +supported by the OGR library into the current project (previously called location) and mapset. If the coordinate reference system (CRS) of the input does not match the CRS of the project, the input is reprojected @@ -11,13 +11,13 @@

    DESCRIPTION

    Supported Vector Formats

    v.import uses the OGR library which supports various vector data -formats including ESRI -Shapefile, Mapinfo +formats including ESRI +Shapefile, Mapinfo File, UK .NTF, SDTS, TIGER, IHO S-57 (ENC), DGN, GML, GPX, AVCBin, REC, Memory, OGDI, and PostgreSQL, depending on the local OGR installation. For details see the OGR web site. The OGR (Simple Features Library) is part of the -GDAL library, hence GDAL needs to be +GDAL library, hence GDAL needs to be installed to use v.import.

    diff --git a/scripts/v.in.e00/v.in.e00.html b/scripts/v.in.e00/v.in.e00.html index ef027c192e2..931bec50cf6 100644 --- a/scripts/v.in.e00/v.in.e00.html +++ b/scripts/v.in.e00/v.in.e00.html @@ -12,7 +12,7 @@

    NOTES

    REFERENCES

    AVCE00 library (providing 'avcimport' and 'e00conv')
    -OGR vector library +OGR vector library

    SEE ALSO

    diff --git a/scripts/v.in.geonames/v.in.geonames.html b/scripts/v.in.geonames/v.in.geonames.html index f2657c878c3..a38aa770cc5 100644 --- a/scripts/v.in.geonames/v.in.geonames.html +++ b/scripts/v.in.geonames/v.in.geonames.html @@ -78,4 +78,4 @@

    SEE ALSO

    AUTHOR

    -Markus Neteler +Markus Neteler diff --git a/temporal/t.rast.algebra/t.rast.algebra.html b/temporal/t.rast.algebra/t.rast.algebra.html index 126ed5df9c2..b7fda851224 100644 --- a/temporal/t.rast.algebra/t.rast.algebra.html +++ b/temporal/t.rast.algebra/t.rast.algebra.html @@ -608,10 +608,10 @@

    REFERENCES

    Related publications:
    • Gebbert, S., Pebesma, E. 2014. TGRASS: A temporal GIS for field based environmental modeling. - Environmental Modelling & Software 53, 1-12 (DOI) + Environmental Modelling & Software 53, 1-12 (DOI) - preprint PDF
    • Gebbert, S., Pebesma, E. 2017. The GRASS GIS temporal framework. International Journal of - Geographical Information Science 31, 1273-1292 (DOI)
    • + Geographical Information Science 31, 1273-1292 (DOI)
    • Gebbert, S., Leppelt, T., Pebesma, E., 2019. A topology based spatio-temporal map algebra for big data analysis. Data 4, 86. (DOI)
    diff --git a/temporal/temporalintro.html b/temporal/temporalintro.html index c5eca6ead2c..2780efd8c85 100644 --- a/temporal/temporalintro.html +++ b/temporal/temporalintro.html @@ -262,11 +262,11 @@

    See also

    • Gebbert, S., Pebesma, E. 2014. TGRASS: A temporal GIS for field based environmental modeling. - Environmental Modelling & Software 53, 1-12 (DOI) + Environmental Modelling & Software 53, 1-12 (DOI) - preprint PDF
    • Gebbert, S., Pebesma, E. 2017. The GRASS GIS temporal framework. International Journal of - Geographical Information Science 31, 1273-1292 (DOI)
    • + Geographical Information Science 31, 1273-1292 (DOI)
    • Gebbert, S., Leppelt, T., Pebesma, E., 2019. A topology based spatio-temporal map algebra for big data analysis. Data 4, 86. (DOI)
    • diff --git a/vector/v.buffer/v.buffer.html b/vector/v.buffer/v.buffer.html index 8c835331630..06b2828cdfb 100644 --- a/vector/v.buffer/v.buffer.html +++ b/vector/v.buffer/v.buffer.html @@ -154,7 +154,7 @@

      Buffer inside input areas

      REFERENCES

      SEE ALSO

      diff --git a/vector/v.cluster/v.cluster.html b/vector/v.cluster/v.cluster.html index 0e92f266eb9..9447538afab 100644 --- a/vector/v.cluster/v.cluster.html +++ b/vector/v.cluster/v.cluster.html @@ -21,7 +21,7 @@

      DESCRIPTION

      separately for each observed density (distance to the farthest neighbor).

      dbscan

      -The Density-Based Spatial +The Density-Based Spatial Clustering of Applications with Noise is a commonly used clustering algorithm. A new cluster is started for a point with at least min - 1 neighbors within the maximum distance. These neighbors @@ -46,7 +46,7 @@

      density

      optics

      This method is Ordering Points to +href="https://en.wikipedia.org/wiki/OPTICS_algorithm">Ordering Points to Identify the Clustering Structure. It is controlled by the number of neighbor points (option min - 1). The core distance of a point is the distance to the farthest neighbor. The reachability of a diff --git a/vector/v.external.out/v.external.out.html b/vector/v.external.out/v.external.out.html index 1d82620f17c..f9c3575aa88 100644 --- a/vector/v.external.out/v.external.out.html +++ b/vector/v.external.out/v.external.out.html @@ -2,9 +2,9 @@

      DESCRIPTION

      v.external.out instructs GRASS to write vector maps in external data format (e.g. ESRI Shapefile, Mapinfo, and others) -using OGR library. PostGIS data can +using OGR library. PostGIS data can be also written by -built-in GRASS-PostGIS +built-in GRASS-PostGIS data provider.

      NOTES

      @@ -26,9 +26,9 @@

      NOTES

      by format option. See the list of valid creation options at OGR formats specification page, example -for ESRI +for ESRI Shapefile -or PostgreSQL/PostGIS +or PostgreSQL/PostGIS format (section "Layer Creation Options"). Options are comma-separated pairs (key=value), the options are case-insensitive, @@ -180,10 +180,10 @@

      Restore settings

      REFERENCES

      SEE ALSO

      diff --git a/vector/v.external/v.external.html b/vector/v.external/v.external.html index fb1512f8942..200d3726363 100644 --- a/vector/v.external/v.external.html +++ b/vector/v.external/v.external.html @@ -3,7 +3,7 @@

      DESCRIPTION

      v.external creates new vector map as a link to external OGR layer or PostGIS feature table. OGR (Simple Features Library) is part of the -GDAL library, so you need to install +GDAL library, so you need to install GDAL to use v.external for external OGR layers. Note that a PostGIS feature table can be linked also using built-in GRASS-PostGIS data driver (requires GRASS to be built with PostgreSQL support). @@ -140,7 +140,7 @@

      SEE ALSO

      -GDAL Library +GDAL Library
      PostGIS diff --git a/vector/v.in.dxf/v.in.dxf.html b/vector/v.in.dxf/v.in.dxf.html index 85d05623344..a08d2fbb5ee 100644 --- a/vector/v.in.dxf/v.in.dxf.html +++ b/vector/v.in.dxf/v.in.dxf.html @@ -45,7 +45,7 @@

      DESCRIPTION

      REFERENCES

      -AutoCad DXF (from Wikipedia, the free encyclopedia)
      +AutoCad DXF (from Wikipedia, the free encyclopedia)
      DXF References (Autodesk-supplied documentation)

      SEE ALSO

      diff --git a/vector/v.kernel/v.kernel.html b/vector/v.kernel/v.kernel.html index c2bd14c87b5..3a5a0162129 100644 --- a/vector/v.kernel/v.kernel.html +++ b/vector/v.kernel/v.kernel.html @@ -2,7 +2,7 @@

      DESCRIPTION

      v.kernel generates a raster density map from vector points data using a moving -kernel. Available kernel +kernel. Available kernel density functions are uniform, triangular, epanechnikov, quartic, triweight, gaussian, cosine, default is gaussian. @@ -20,7 +20,7 @@

      NOTES

      (integer). The density result stored as category may be multiplied by this number.

      For the gaussian kernel, standard deviation for the -gaussian function +gaussian function is set to 1/4 of the radius.

      With the -o flag (experimental) the command tries to calculate an @@ -54,7 +54,7 @@

      REFERENCES

      method for networks, its computational method and a GIS-based tool. International Journal of Geographical Information Science, Vol 23(1), pp. 7-32.
      -DOI: 10.1080/13658810802475491 +DOI: 10.1080/13658810802475491

    SEE ALSO

    diff --git a/vector/v.label.sa/v.label.sa.html b/vector/v.label.sa/v.label.sa.html index 65864c78a2e..2376c70771c 100644 --- a/vector/v.label.sa/v.label.sa.html +++ b/vector/v.label.sa/v.label.sa.html @@ -41,7 +41,7 @@

    SEE ALSO

    d.label
    d.labels
    ps.map -Wikipedia article on simulated annealing +Wikipedia article on simulated annealing

    AUTHOR

    diff --git a/vector/v.net.bridge/v.net.bridge.html b/vector/v.net.bridge/v.net.bridge.html index fedb0559d6c..accd2484a33 100644 --- a/vector/v.net.bridge/v.net.bridge.html +++ b/vector/v.net.bridge/v.net.bridge.html @@ -8,8 +8,8 @@

    NOTES

    the (sub-)network. A node is an articulation point if its removal would disconnect the (sub-)network. For more information and formal definitions check the wikipedia entries: -bridge -and articulation +bridge +and articulation point.

    The output of the module contains the selected diff --git a/vector/v.net.centrality/v.net.centrality.html b/vector/v.net.centrality/v.net.centrality.html index bd122149d34..28871b660e3 100644 --- a/vector/v.net.centrality/v.net.centrality.html +++ b/vector/v.net.centrality/v.net.centrality.html @@ -9,7 +9,7 @@

    NOTES

    stores them in the given columns of an attribute table, which is created and linked to the output map. For the description of these, please check the following -wikipedia article. +wikipedia article. If the column name is not given for a measure then that measure is not computed. If -a flag is set then points are added on nodes without points. Also, the points for which the output is computed diff --git a/vector/v.net.flow/v.net.flow.html b/vector/v.net.flow/v.net.flow.html index 66a617cb0e6..9a15c95301e 100644 --- a/vector/v.net.flow/v.net.flow.html +++ b/vector/v.net.flow/v.net.flow.html @@ -23,7 +23,7 @@

    NOTES

    flowing in the backward direction. Cut map contains the edges in the minimum cut.
    -A famous result +A famous result says that the total amount of water flowing is equal to the minimum cut. diff --git a/vector/v.net/v.net.html b/vector/v.net/v.net.html index ab112155589..baef25e5484 100644 --- a/vector/v.net/v.net.html +++ b/vector/v.net/v.net.html @@ -154,7 +154,7 @@

    NOTES

    EXAMPLES

    -The examples are North Carolina dataset based. +The examples are North Carolina dataset based.

    Create nodes globally for all line ends and intersections

    diff --git a/vector/v.out.ascii/v.out.ascii.html b/vector/v.out.ascii/v.out.ascii.html index 2eff87a01fa..d8b6440b1c7 100644 --- a/vector/v.out.ascii/v.out.ascii.html +++ b/vector/v.out.ascii/v.out.ascii.html @@ -109,7 +109,7 @@

    Point mode

    WKT mode

    WKT is abbreviation -for Well-known +for Well-known text.
    diff --git a/vector/v.out.dxf/v.out.dxf.html b/vector/v.out.dxf/v.out.dxf.html
    index 853e19472bd..95057f05f22 100644
    --- a/vector/v.out.dxf/v.out.dxf.html
    +++ b/vector/v.out.dxf/v.out.dxf.html
    @@ -12,7 +12,7 @@ 

    NOTES

    REFERENCES

    -AutoCad DXF (from Wikipedia, the free encyclopedia) +AutoCad DXF (from Wikipedia, the free encyclopedia)

    SEE ALSO

    diff --git a/vector/v.out.ogr/v.out.ogr.html b/vector/v.out.ogr/v.out.ogr.html index d9a2c84049b..664a5c35a1a 100644 --- a/vector/v.out.ogr/v.out.ogr.html +++ b/vector/v.out.ogr/v.out.ogr.html @@ -1,27 +1,27 @@

    DESCRIPTION

    v.out.ogr converts GRASS vector map layer to any of the -supported OGR vector formats +supported OGR vector formats (including OGC GeoPackage, ESRI Shapefile, SpatiaLite or GML).

    OGR (Simple Features Library) is part of the -GDAL library, so you need to +GDAL library, so you need to install this library to use v.out.ogr.

    The OGR library supports many various formats including:

    @@ -193,7 +193,7 @@

    Export to KML (Google Earth)

    REFERENCES

    diff --git a/vector/v.out.postgis/v.out.postgis.html b/vector/v.out.postgis/v.out.postgis.html index 104a9ad2dae..aa9bd459778 100644 --- a/vector/v.out.postgis/v.out.postgis.html +++ b/vector/v.out.postgis/v.out.postgis.html @@ -65,8 +65,8 @@

    NOTES

    "geom". Name of the geometry column can be changed by options=GEOMETRY_NAME=<column>. Note that for exporting vector features as simple features can be alternatively -used PostgreSQL driver -from OGR library +used PostgreSQL driver +from OGR library through v.out.ogr module.

    diff --git a/vector/v.surf.rst/v.surf.rst.html b/vector/v.surf.rst/v.surf.rst.html index 66a39ebc6f8..199ca4fc3ca 100644 --- a/vector/v.surf.rst/v.surf.rst.html +++ b/vector/v.surf.rst/v.surf.rst.html @@ -389,7 +389,7 @@

    REFERENCES

  • Mitas, L., and Mitasova H., 1988, General variational approach to the approximation problem, Computers and Mathematics with Applications, v.16, p. 983-992.
  • -
  • +
  • Neteler, M. and Mitasova, H., 2008, Open Source GIS: A GRASS GIS Approach, 3rd Edition, Springer, New York, 406 pages.
  • Talmi, A. and Gilat, G., 1977 : Method for Smooth Approximation of Data, diff --git a/vector/v.univar/main.c b/vector/v.univar/main.c index f84bf118fab..85217498035 100644 --- a/vector/v.univar/main.c +++ b/vector/v.univar/main.c @@ -19,7 +19,7 @@ /* TODO * - add flag to weigh by line/boundary length and area size * Roger Bivand on GRASS devel ml on July 2 2004 - * http://lists.osgeo.org/pipermail/grass-dev/2004-July/014976.html + * https://lists.osgeo.org/pipermail/grass-dev/2004-July/014976.html * "[...] calculating weighted means, weighting by line length * or area surface size [does not make sense]. I think it would be * better to treat each line or area as a discrete, unweighted, unit diff --git a/vector/v.vol.rst/v.vol.rst.html b/vector/v.vol.rst/v.vol.rst.html index 06fc55e5118..571a162162c 100644 --- a/vector/v.vol.rst/v.vol.rst.html +++ b/vector/v.vol.rst/v.vol.rst.html @@ -85,7 +85,7 @@

    Cross validation procedure

    representing the whole dataset.

    Example - (based on Slovakia3d dataset): + (based on Slovakia3d dataset):

     v.info -c precip3d
     g.region n=5530000 s=5275000 w=4186000 e=4631000 res=500 -p
    diff --git a/vector/v.voronoi/v.voronoi.html b/vector/v.voronoi/v.voronoi.html
    index 0b2c9e4cd40..e719c2c0038 100644
    --- a/vector/v.voronoi/v.voronoi.html
    +++ b/vector/v.voronoi/v.voronoi.html
    @@ -82,7 +82,7 @@ 

    REFERENCES

    Steve J. Fortune, (1987). A Sweepline Algorithm for Voronoi Diagrams, Algorithmica 2, 153-174 - (DOI). + (DOI).

    SEE ALSO

    From 86693f6ce763abc2fd1fd563b4b442bba2da8d3c Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Fri, 25 Oct 2024 13:32:09 -0400 Subject: [PATCH 443/471] wxGUI: FIxed bare except in photo2image/ (#4582) --- .flake8 | 3 +-- gui/wxpython/photo2image/ip2i_manager.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.flake8 b/.flake8 index 7bc3070d15e..e73cc6d94c5 100644 --- a/.flake8 +++ b/.flake8 @@ -24,8 +24,7 @@ per-file-ignores = gui/scripts/d.wms.py: E501 gui/wxpython/image2target/g.gui.image2target.py: E501 gui/wxpython/nviz/*: E722 - gui/wxpython/photo2image/*: F841, E722, E265 - gui/wxpython/photo2image/g.gui.photo2image.py: E501, F841 + gui/wxpython/photo2image/g.gui.photo2image.py: E501 gui/wxpython/psmap/*: E501, E722 gui/wxpython/vdigit/*: F841, E722, F405, F403 gui/wxpython/animation/g.gui.animation.py: E501 diff --git a/gui/wxpython/photo2image/ip2i_manager.py b/gui/wxpython/photo2image/ip2i_manager.py index 711ec4191af..d0726a3bae2 100644 --- a/gui/wxpython/photo2image/ip2i_manager.py +++ b/gui/wxpython/photo2image/ip2i_manager.py @@ -163,7 +163,7 @@ def __init__( if p.returncode == 0: print("returncode = ", str(p.returncode)) self.Map.region = self.Map.GetRegion() - except: + except Exception: pass self.SwitchEnv("source") From 1a04b6401f7a3e1723e01fd213df6939ec7cf638 Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Fri, 25 Oct 2024 13:33:31 -0400 Subject: [PATCH 444/471] r.fillnulls: Removed bare except from r.fillnulls (#4581) --- .flake8 | 1 - scripts/r.fillnulls/r.fillnulls.py | 8 ++++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.flake8 b/.flake8 index e73cc6d94c5..c29f447c597 100644 --- a/.flake8 +++ b/.flake8 @@ -101,7 +101,6 @@ per-file-ignores = scripts/db.univar/db.univar.py: E501 scripts/d.frame/d.frame.py: E722 scripts/i.pansharpen/i.pansharpen.py: E722, E501 - scripts/r.fillnulls/r.fillnulls.py: E722 scripts/v.what.strds/v.what.strds.py: E501 # Line too long (esp. module interface definitions) scripts/*/*.py: E501 diff --git a/scripts/r.fillnulls/r.fillnulls.py b/scripts/r.fillnulls/r.fillnulls.py index a53d19a8a72..b22550db497 100755 --- a/scripts/r.fillnulls/r.fillnulls.py +++ b/scripts/r.fillnulls/r.fillnulls.py @@ -273,7 +273,7 @@ def main(): type="area", quiet=quiet, ) - except: + except CalledModuleError: gs.fatal( _( "abandoned. Removing temporary maps, restoring " @@ -481,7 +481,7 @@ def main(): tmp_rmaps.remove(holename + "_edges") tmp_rmaps.remove(holename + "_dem") tmp_vmaps.remove(holename) - except: + except ValueError: pass gs.warning( _( @@ -545,7 +545,7 @@ def main(): tmp_rmaps.remove(holename + "_grown") tmp_rmaps.remove(holename + "_edges") tmp_rmaps.remove(holename + "_dem") - except: + except ValueError: pass try: gs.run_command( @@ -569,7 +569,7 @@ def main(): ) try: tmp_vmaps.remove(holename) - except: + except ValueError: pass try: gs.run_command( From 9e1e85d6079f36962f19b32793b31f43c23b5a4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Fri, 25 Oct 2024 13:34:52 -0400 Subject: [PATCH 445/471] style: Fix if-else-block-instead-of-if-exp (SIM108) (part 3) (#4570) * style: Fix if-else-block-instead-of-if-exp (SIM108) in scripts/ Ruff rule: https://docs.astral.sh/ruff/rules/if-else-block-instead-of-if-exp * style: Fix if-else-block-instead-of-if-exp (SIM108) in python/ Ruff rule: https://docs.astral.sh/ruff/rules/if-else-block-instead-of-if-exp * checks: Rename inner variable shadowing type to _type in python/grass/temporal/gui_support.py * style: Manual fixes for if-else-block-instead-of-if-exp (SIM108) in python/ Ruff rule: https://docs.astral.sh/ruff/rules/if-else-block-instead-of-if-exp * style: Fix if-else-block-instead-of-if-exp (SIM108) in gui/ Ruff rule: https://docs.astral.sh/ruff/rules/if-else-block-instead-of-if-exp * checks: Rename inner variable shadowing list to _list in gui/wxpython/core/render.py * style: Manual fixes for if-else-block-instead-of-if-exp (SIM108) in gui/ Ruff rule: https://docs.astral.sh/ruff/rules/if-else-block-instead-of-if-exp * python: Add type annotations for grass.script.core.parser() * python: Add type annotations for is_time_absolute() and is_time_relative() in grass.temporal.AbstractDataset * style: Enable Checking for SIM108 * Update pyproject.toml to remove fixed issues * Update base.py Co-authored-by: Anna Petrasova --------- Co-authored-by: Anna Petrasova --- gui/wxpython/animation/dialogs.py | 5 +- gui/wxpython/animation/frame.py | 17 ++---- gui/wxpython/animation/temporal_manager.py | 6 +-- gui/wxpython/core/gcmd.py | 5 +- gui/wxpython/core/gconsole.py | 10 +--- gui/wxpython/core/menutree.py | 32 +++-------- gui/wxpython/core/render.py | 45 ++++------------ gui/wxpython/core/settings.py | 5 +- gui/wxpython/core/treemodel.py | 7 +-- gui/wxpython/core/utils.py | 5 +- gui/wxpython/core/workspace.py | 22 ++------ gui/wxpython/datacatalog/tree.py | 5 +- gui/wxpython/dbmgr/base.py | 37 +++---------- gui/wxpython/dbmgr/dialogs.py | 10 +--- gui/wxpython/dbmgr/sqlbuilder.py | 10 +--- gui/wxpython/gcp/manager.py | 21 ++------ gui/wxpython/gmodeler/dialogs.py | 16 ++---- gui/wxpython/gmodeler/model.py | 62 +++++----------------- gui/wxpython/gmodeler/panels.py | 14 ++--- gui/wxpython/gui_core/dialogs.py | 15 ++---- gui/wxpython/gui_core/forms.py | 41 +++----------- gui/wxpython/gui_core/gselect.py | 27 ++-------- gui/wxpython/gui_core/mapdisp.py | 5 +- gui/wxpython/gui_core/menu.py | 5 +- gui/wxpython/gui_core/preferences.py | 5 +- gui/wxpython/gui_core/prompt.py | 5 +- gui/wxpython/gui_core/treeview.py | 5 +- gui/wxpython/iclass/dialogs.py | 6 +-- gui/wxpython/iclass/digit.py | 12 ++--- gui/wxpython/image2target/ii2t_gis_set.py | 5 +- gui/wxpython/image2target/ii2t_manager.py | 22 ++------ gui/wxpython/iscatt/controllers.py | 10 +--- gui/wxpython/iscatt/core_c.py | 6 +-- gui/wxpython/iscatt/frame.py | 6 +-- gui/wxpython/iscatt/iscatt_core.py | 6 +-- gui/wxpython/lmgr/frame.py | 10 +--- gui/wxpython/lmgr/layertree.py | 24 ++------- gui/wxpython/lmgr/menudata.py | 10 +--- gui/wxpython/location_wizard/wizard.py | 5 +- gui/wxpython/main_window/frame.py | 10 +--- gui/wxpython/mapdisp/frame.py | 11 ++-- gui/wxpython/mapdisp/toolbars.py | 5 +- gui/wxpython/mapwin/buffered.py | 25 ++------- gui/wxpython/mapwin/decorations.py | 10 +--- gui/wxpython/modules/colorrules.py | 25 ++------- gui/wxpython/modules/extensions.py | 6 +-- gui/wxpython/modules/import_export.py | 10 +--- gui/wxpython/modules/mcalc_builder.py | 5 +- gui/wxpython/nviz/mapwindow.py | 5 +- gui/wxpython/nviz/tools.py | 42 ++++----------- gui/wxpython/nviz/workspace.py | 5 +- gui/wxpython/nviz/wxnviz.py | 17 ++---- gui/wxpython/photo2image/ip2i_manager.py | 15 ++---- gui/wxpython/psmap/dialogs.py | 57 ++++---------------- gui/wxpython/psmap/frame.py | 35 +++--------- gui/wxpython/psmap/utils.py | 10 +--- gui/wxpython/rdigit/controller.py | 5 +- gui/wxpython/timeline/frame.py | 14 +++-- gui/wxpython/tools/update_menudata.py | 5 +- gui/wxpython/tplot/frame.py | 5 +- gui/wxpython/vdigit/dialogs.py | 5 +- gui/wxpython/vdigit/mapwindow.py | 10 +--- gui/wxpython/vdigit/preferences.py | 22 ++------ gui/wxpython/vdigit/toolbars.py | 5 +- gui/wxpython/vdigit/wxdigit.py | 5 +- gui/wxpython/vdigit/wxdisplay.py | 12 ++--- gui/wxpython/vnet/dialogs.py | 7 +-- gui/wxpython/vnet/vnet_data.py | 32 +++-------- gui/wxpython/vnet/vnet_utils.py | 5 +- gui/wxpython/web_services/widgets.py | 10 +--- gui/wxpython/wxgui.py | 5 +- gui/wxpython/wxplot/scatter.py | 6 +-- pyproject.toml | 3 +- python/grass/script/core.py | 10 ++-- python/grass/temporal/abstract_dataset.py | 6 ++- 75 files changed, 225 insertions(+), 804 deletions(-) diff --git a/gui/wxpython/animation/dialogs.py b/gui/wxpython/animation/dialogs.py index 644553ec328..5a634b3bb16 100644 --- a/gui/wxpython/animation/dialogs.py +++ b/gui/wxpython/animation/dialogs.py @@ -1619,10 +1619,7 @@ def SetStdsProperties(self, layer): dlg.CenterOnParent() if dlg.ShowModal() == wx.ID_OK: layer = dlg.GetLayer() - if hidden: - signal = self.layerAdded - else: - signal = self.cmdChanged + signal = self.layerAdded if hidden else self.cmdChanged signal.emit(index=self._layerList.GetLayerIndex(layer), layer=layer) elif hidden: self._layerList.RemoveLayer(layer) diff --git a/gui/wxpython/animation/frame.py b/gui/wxpython/animation/frame.py index 6496920a5cd..42953803778 100644 --- a/gui/wxpython/animation/frame.py +++ b/gui/wxpython/animation/frame.py @@ -274,17 +274,11 @@ def OnStop(self, event): self.controller.EndAnimation() def OnOneDirectionReplay(self, event): - if event.IsChecked(): - mode = ReplayMode.REPEAT - else: - mode = ReplayMode.ONESHOT + mode = ReplayMode.REPEAT if event.IsChecked() else ReplayMode.ONESHOT self.controller.SetReplayMode(mode) def OnBothDirectionReplay(self, event): - if event.IsChecked(): - mode = ReplayMode.REVERSE - else: - mode = ReplayMode.ONESHOT + mode = ReplayMode.REVERSE if event.IsChecked() else ReplayMode.ONESHOT self.controller.SetReplayMode(mode) def OnAdjustSpeed(self, event): @@ -642,11 +636,8 @@ def _updateFrameIndex(self, index): } else: label = _("to %(to)s") % {"to": self.timeLabels[index][1]} - else: # noqa: PLR5501 - if self.temporalType == TemporalType.ABSOLUTE: - label = start - else: - label = "" + else: + label = start if self.temporalType == TemporalType.ABSOLUTE else "" self.label2.SetLabel(label) if self.temporalType == TemporalType.RELATIVE: self.indexField.SetValue(start) diff --git a/gui/wxpython/animation/temporal_manager.py b/gui/wxpython/animation/temporal_manager.py index ef782543089..1b5037639cf 100644 --- a/gui/wxpython/animation/temporal_manager.py +++ b/gui/wxpython/animation/temporal_manager.py @@ -259,10 +259,8 @@ def _getLabelsAndMaps(self, timeseries): elif self.temporalType == TemporalType.RELATIVE: unit = self.timeseriesInfo[timeseries]["unit"] - if self.granularityMode == GranularityMode.ONE_UNIT: - gran = 1 - else: - gran = granNum + gran = 1 if self.granularityMode == GranularityMode.ONE_UNIT else granNum + # start sampling - now it can be used for both interval and point data # after instance, there can be a gap or an interval # if it is a gap we remove it and put there the previous instance instead diff --git a/gui/wxpython/core/gcmd.py b/gui/wxpython/core/gcmd.py index a173e28c7f8..134ed756d23 100644 --- a/gui/wxpython/core/gcmd.py +++ b/gui/wxpython/core/gcmd.py @@ -709,10 +709,7 @@ def RunCommand( kwargs["stdin"] = subprocess.PIPE # Do not change the environment, only a local copy. - if env: - env = env.copy() - else: - env = os.environ.copy() + env = env.copy() if env else os.environ.copy() if parent: env["GRASS_MESSAGE_FORMAT"] = "standard" diff --git a/gui/wxpython/core/gconsole.py b/gui/wxpython/core/gconsole.py index a4b58435cf4..5b58c08cc55 100644 --- a/gui/wxpython/core/gconsole.py +++ b/gui/wxpython/core/gconsole.py @@ -310,10 +310,7 @@ def write(self, s): if "GRASS_INFO_PERCENT" in line: value = int(line.rsplit(":", 1)[1].strip()) - if value >= 0 and value < 100: - progressValue = value - else: - progressValue = 0 + progressValue = value if value >= 0 and value < 100 else 0 elif "GRASS_INFO_MESSAGE" in line: self.type = "message" self.message += line.split(":", 1)[1].strip() + "\n" @@ -632,10 +629,7 @@ def load_source(modname, filename): return - if env: - env = env.copy() - else: - env = os.environ.copy() + env = env.copy() if env else os.environ.copy() # activate computational region (set with g.region) # for all non-display commands. if compReg and "GRASS_REGION" in env: diff --git a/gui/wxpython/core/menutree.py b/gui/wxpython/core/menutree.py index fba33daf705..b8f60e9610a 100644 --- a/gui/wxpython/core/menutree.py +++ b/gui/wxpython/core/menutree.py @@ -108,30 +108,14 @@ def _createItem(self, item, node): shortcut = item.find("shortcut") # optional wxId = item.find("id") # optional icon = item.find("icon") # optional - if gcmd is not None: - gcmd = gcmd.text - else: - gcmd = "" - if desc.text: - desc = _(desc.text) - else: - desc = "" - if keywords is None or keywords.text is None: - keywords = "" - else: - keywords = keywords.text - if shortcut is not None: - shortcut = shortcut.text - else: - shortcut = "" - if wxId is not None: - wxId = eval("wx." + wxId.text) - else: - wxId = wx.ID_ANY - if icon is not None: - icon = icon.text - else: - icon = "" + gcmd = gcmd.text if gcmd is not None else "" + desc = _(desc.text) if desc.text else "" + keywords = ( + "" if keywords is None or keywords.text is None else keywords.text + ) + shortcut = shortcut.text if shortcut is not None else "" + wxId = eval("wx." + wxId.text) if wxId is not None else wx.ID_ANY + icon = icon.text if icon is not None else "" label = origLabel if gcmd: if self.menustyle == 1: diff --git a/gui/wxpython/core/render.py b/gui/wxpython/core/render.py index 2db3287bf78..989287cd4a5 100644 --- a/gui/wxpython/core/render.py +++ b/gui/wxpython/core/render.py @@ -100,10 +100,7 @@ def __init__( if mapfile: self.mapfile = mapfile else: - if ltype == "overlay": - tempfile_sfx = ".png" - else: - tempfile_sfx = ".ppm" + tempfile_sfx = ".png" if ltype == "overlay" else ".ppm" self.mapfile = get_tempfile_name(suffix=tempfile_sfx) @@ -790,10 +787,7 @@ def ReportProgress(self, env, layer=None): stText += "..." if self.progressInfo["range"] != len(self.progressInfo["rendered"]): - if stText: - stText = _("Rendering & ") + stText - else: - stText = _("Rendering...") + stText = _("Rendering & ") + stText if stText else _("Rendering...") self.updateProgress.emit( range=self.progressInfo["range"], @@ -1260,16 +1254,8 @@ def GetListOfLayers( :return: list of selected layers """ selected = [] - - if isinstance(ltype, str): - one_type = True - else: - one_type = False - - if one_type and ltype == "overlay": - llist = self.overlays - else: - llist = self.layers + one_type = bool(isinstance(ltype, str)) + llist = self.overlays if one_type and ltype == "overlay" else self.layers # ["raster", "vector", "wms", ... ] for layer in llist: @@ -1332,10 +1318,7 @@ def Render(self, force=False, windres=False): self.renderMgr.Render(force, windres) def _addLayer(self, layer, pos=-1): - if layer.type == "overlay": - llist = self.overlays - else: - llist = self.layers + llist = self.overlays if layer.type == "overlay" else self.layers # add maplayer to the list of layers if pos > -1: @@ -1426,12 +1409,9 @@ def DeleteLayer(self, layer, overlay=False): """ Debug.msg(3, "Map.DeleteLayer(): name=%s" % layer.name) - if overlay: - list = self.overlays - else: - list = self.layers + _list = self.overlays if overlay else self.layers - if layer in list: + if layer in _list: if layer.mapfile: base, mapfile = os.path.split(layer.mapfile) tempbase = mapfile.split(".")[0] @@ -1448,7 +1428,7 @@ def DeleteLayer(self, layer, overlay=False): if os.path.isfile(layer._legrow): os.remove(layer._legrow) - list.remove(layer) + _list.remove(layer) self.layerRemoved.emit(layer=layer) return layer @@ -1583,13 +1563,10 @@ def GetLayerIndex(self, layer, overlay=False): :return: layer index :return: -1 if layer not found """ - if overlay: - list = self.overlays - else: - list = self.layers + _list = self.overlays if overlay else self.layers - if layer in list: - return list.index(layer) + if layer in _list: + return _list.index(layer) return -1 diff --git a/gui/wxpython/core/settings.py b/gui/wxpython/core/settings.py index b155d9607af..05c6579e636 100644 --- a/gui/wxpython/core/settings.py +++ b/gui/wxpython/core/settings.py @@ -960,10 +960,7 @@ def _readLegacyFile(self, settings=None): del kv[0] idx = 0 while idx < len(kv): - if subkeyMaster: - subkey = [subkeyMaster, kv[idx]] - else: - subkey = kv[idx] + subkey = [subkeyMaster, kv[idx]] if subkeyMaster else kv[idx] value = kv[idx + 1] value = self._parseValue(value, read=True) self.Append(settings, group, key, subkey, value) diff --git a/gui/wxpython/core/treemodel.py b/gui/wxpython/core/treemodel.py index fb18384ba36..0ce69133e1b 100644 --- a/gui/wxpython/core/treemodel.py +++ b/gui/wxpython/core/treemodel.py @@ -294,15 +294,12 @@ def __init__(self, label=None, data=None): def label(self): return self._label - def match(self, key, value, case_sensitive=False): + def match(self, key, value, case_sensitive=False) -> bool: """Method used for searching according to command, keywords or description.""" if not self.data: return False - if isinstance(key, str): - keys = [key] - else: - keys = key + keys = [key] if isinstance(key, str) else key for key in keys: if key not in {"command", "keywords", "description"}: diff --git a/gui/wxpython/core/utils.py b/gui/wxpython/core/utils.py index 2b1f3f4997a..6a39a158555 100644 --- a/gui/wxpython/core/utils.py +++ b/gui/wxpython/core/utils.py @@ -864,10 +864,7 @@ def StoreEnvVariable(key, value=None, envFile=None): ) ) return - if windows: - expCmd = "set" - else: - expCmd = "export" + expCmd = "set" if windows else "export" for key, value in environ.items(): fd.write("%s %s=%s\n" % (expCmd, key, value)) diff --git a/gui/wxpython/core/workspace.py b/gui/wxpython/core/workspace.py index 22aa56bfe62..dd460a68195 100644 --- a/gui/wxpython/core/workspace.py +++ b/gui/wxpython/core/workspace.py @@ -169,11 +169,8 @@ def __processFile(self): size = None extentAttr = display.get("extent", "") - if extentAttr: - # w, s, e, n - extent = map(float, extentAttr.split(",")) - else: - extent = None + # w, s, e, n + extent = map(float, extentAttr.split(",")) if extentAttr else None # projection node_projection = display.find("projection") @@ -312,10 +309,7 @@ def __processLayer(self, layer): ) ) - if layer.find("selected") is not None: - selected = True - else: - selected = False + selected = layer.find("selected") is not None # # Vector digitizer settings @@ -330,10 +324,7 @@ def __processLayer(self, layer): # Nviz (3D settings) # node_nviz = layer.find("nviz") - if node_nviz is not None: - nviz = self.__processLayerNviz(node_nviz) - else: - nviz = None + nviz = self.__processLayerNviz(node_nviz) if node_nviz is not None else None return (cmd, selected, vdigit, nviz) @@ -729,10 +720,7 @@ def __processLayerNvizNode(self, node, tag, cast, dc=None): try: value = cast(node_tag.text) except ValueError: - if cast == str: - value = "" - else: - value = None + value = "" if cast == str else None if dc: dc[tag] = {} dc[tag]["value"] = value diff --git a/gui/wxpython/datacatalog/tree.py b/gui/wxpython/datacatalog/tree.py index 176f5231712..bf06fd11ab8 100644 --- a/gui/wxpython/datacatalog/tree.py +++ b/gui/wxpython/datacatalog/tree.py @@ -2016,10 +2016,7 @@ def _getNewMapName(self, message, title, value, element, mapset, env): mapset=mapset, ) dlg.SetValue(value) - if dlg.ShowModal() == wx.ID_OK: - name = dlg.GetValue() - else: - name = None + name = dlg.GetValue() if dlg.ShowModal() == wx.ID_OK else None dlg.Destroy() return name diff --git a/gui/wxpython/dbmgr/base.py b/gui/wxpython/dbmgr/base.py index 1af8d6fccb1..c5eb8877b0a 100644 --- a/gui/wxpython/dbmgr/base.py +++ b/gui/wxpython/dbmgr/base.py @@ -1542,10 +1542,7 @@ def OnDataItemEdit(self, event): column = tlist.columns[columnName[i]] if len(values[i]) > 0: try: - if missingKey is True: - idx = i - 1 - else: - idx = i + idx = i - 1 if missingKey else i if column["ctype"] != str: tlist.itemDataMap[item][idx] = column["ctype"]( @@ -1707,10 +1704,7 @@ def OnDataItemAdd(self, event): del values[0] # add new item to the tlist - if len(tlist.itemIndexMap) > 0: - index = max(tlist.itemIndexMap) + 1 - else: - index = 0 + index = max(tlist.itemIndexMap) + 1 if len(tlist.itemIndexMap) > 0 else 0 tlist.itemIndexMap.append(index) tlist.itemDataMap[index] = values @@ -1828,11 +1822,7 @@ def _drawSelected(self, zoom, selectedOnly=True): return tlist = self.FindWindowById(self.layerPage[self.selLayer]["data"]) - if selectedOnly: - fn = tlist.GetSelectedItems - else: - fn = tlist.GetItems - + fn = tlist.GetSelectedItems if selectedOnly else tlist.GetItems cats = list(map(int, fn())) digitToolbar = None @@ -1929,11 +1919,7 @@ def AddQueryMapLayer(self, selectedOnly=True): :return: True if map has been redrawn, False if no map is given """ tlist = self.FindWindowById(self.layerPage[self.selLayer]["data"]) - if selectedOnly: - fn = tlist.GetSelectedItems - else: - fn = tlist.GetItems - + fn = tlist.GetSelectedItems if selectedOnly else tlist.GetItems cats = {self.selLayer: fn()} if self.mapdisplay.Map.GetLayerIndex(self.qlayer) < 0: @@ -2237,10 +2223,7 @@ def ValidateSelectStatement(self, statement): break cols += c index += 1 - if cols == "*": - cols = None - else: - cols = cols.split(",") + cols = None if cols == "*" else cols.split(",") tablelen = len(self.dbMgrData["mapDBInfo"].layers[self.selLayer]["table"]) @@ -2251,10 +2234,7 @@ def ValidateSelectStatement(self, statement): if len(statement[index + 7 + tablelen :]) > 0: index = statement.lower().find("where ") - if index > -1: - where = statement[index + 6 :] - else: - where = None + where = statement[index + 6 :] if index > -1 else None else: where = None @@ -3324,10 +3304,7 @@ def _createAddPage(self): row = 0 for key in ("layer", "driver", "database", "table", "key", "addCat"): label, value = self.addLayerWidgets[key] - if not value: - span = (1, 2) - else: - span = (1, 1) + span = (1, 2) if not value else (1, 1) dataSizer.Add(label, flag=wx.ALIGN_CENTER_VERTICAL, pos=(row, 0), span=span) if not value: diff --git a/gui/wxpython/dbmgr/dialogs.py b/gui/wxpython/dbmgr/dialogs.py index 0d7c6e9e523..8ec2b12f36b 100644 --- a/gui/wxpython/dbmgr/dialogs.py +++ b/gui/wxpython/dbmgr/dialogs.py @@ -393,10 +393,7 @@ def UpdateDialog(self, map=None, query=None, cats=None, fid=-1, action=None): """ if action: self.action = action - if action == "display": - enabled = False - else: - enabled = True + enabled = action != "display" self.closeDialog.Enable(enabled) self.FindWindowById(wx.ID_OK).Enable(enabled) @@ -420,10 +417,7 @@ def UpdateDialog(self, map=None, query=None, cats=None, fid=-1, action=None): idx = 0 for layer in data["Layer"]: layer = int(layer) - if data["Id"][idx] is not None: - tfid = int(data["Id"][idx]) - else: - tfid = 0 # Area / Volume + tfid = int(data["Id"][idx]) if data["Id"][idx] is not None else 0 if tfid not in self.cats: self.cats[tfid] = {} if layer not in self.cats[tfid]: diff --git a/gui/wxpython/dbmgr/sqlbuilder.py b/gui/wxpython/dbmgr/sqlbuilder.py index 5e6fe0e7706..51010e49677 100644 --- a/gui/wxpython/dbmgr/sqlbuilder.py +++ b/gui/wxpython/dbmgr/sqlbuilder.py @@ -574,10 +574,7 @@ def _add(self, element, value): idx1 = len("select") idx2 = sqlstr.lower().find("from") colstr = sqlstr[idx1:idx2].strip() - if colstr == "*": - cols = [] - else: - cols = colstr.split(",") + cols = [] if colstr == "*" else colstr.split(",") if value in cols: cols.remove(value) else: @@ -922,10 +919,7 @@ def _add(self, element, value): print(__doc__, file=sys.stderr) sys.exit() - if len(sys.argv) == 3: - layer = 1 - else: - layer = int(sys.argv[3]) + layer = 1 if len(sys.argv) == 3 else int(sys.argv[3]) if sys.argv[1] == "select": sqlBuilder = SQLBuilderSelect diff --git a/gui/wxpython/gcp/manager.py b/gui/wxpython/gcp/manager.py index 3b6dd398b51..74ede7189e9 100644 --- a/gui/wxpython/gcp/manager.py +++ b/gui/wxpython/gcp/manager.py @@ -469,11 +469,7 @@ def __init__(self, wizard, parent): def OnMaptype(self, event): """Change map type""" global maptype - - if event.GetInt() == 0: - maptype = "raster" - else: - maptype = "vector" + maptype = "raster" if event.GetInt() == 0 else "vector" def OnLocation(self, event): """Sets source location for map(s) to georectify""" @@ -1433,10 +1429,7 @@ def SetGCPSatus(self, item, itemIndex): else: item.SetPropertyVal("hide", False) if self.highest_only: - if itemIndex == self.highest_key: - wxPen = "highest" - else: - wxPen = "default" + wxPen = "highest" if itemIndex == self.highest_key else "default" elif self.mapcoordlist[key][5] > self.rmsthresh: wxPen = "highest" else: @@ -1676,10 +1669,7 @@ def OnFocus(self, event): pass def _onMouseLeftUpPointer(self, mapWindow, x, y): - if mapWindow == self.SrcMapWindow: - coordtype = "source" - else: - coordtype = "target" + coordtype = "source" if mapWindow == self.SrcMapWindow else "target" coord = (x, y) self.SetGCPData(coordtype, coord, self, confirm=True) @@ -1806,10 +1796,7 @@ def OnGeorect(self, event): self.grwiz.SwitchEnv("source") - if self.clip_to_region: - flags = "ac" - else: - flags = "a" + flags = "ac" if self.clip_to_region else "a" with wx.BusyInfo(_("Rectifying images, please wait..."), parent=self): wx.GetApp().Yield() diff --git a/gui/wxpython/gmodeler/dialogs.py b/gui/wxpython/gmodeler/dialogs.py index 7cd7cbd1147..c8dd7509e74 100644 --- a/gui/wxpython/gmodeler/dialogs.py +++ b/gui/wxpython/gmodeler/dialogs.py @@ -291,11 +291,7 @@ def GetPanel(self): def _getCmd(self): line = self.cmd_prompt.GetCurLine()[0].strip() - if len(line) == 0: - cmd = [] - else: - cmd = utils.split(str(line)) - return cmd + return [] if len(line) == 0 else utils.split(str(line)) def GetCmd(self): """Get command""" @@ -980,10 +976,7 @@ def Populate(self, data): checked.append(None) else: bId = action.GetBlockId() - if not bId: - bId = _("No") - else: - bId = _("Yes") + bId = _("No") if not bId else _("Yes") options = action.GetParameterizedParams() params = [] for f in options["flags"]: @@ -1110,10 +1103,7 @@ def MoveItems(self, items, up): idxList = {} itemsToSelect = [] for i in items: - if up: - idx = i - 1 - else: - idx = i + 1 + idx = i - 1 if up else i + 1 itemsToSelect.append(idx) idxList[model.GetItemIndex(modelActions[i])] = model.GetItemIndex( modelActions[idx] diff --git a/gui/wxpython/gmodeler/model.py b/gui/wxpython/gmodeler/model.py index fdf6ac63b36..15c4eaa8cd4 100644 --- a/gui/wxpython/gmodeler/model.py +++ b/gui/wxpython/gmodeler/model.py @@ -833,10 +833,7 @@ def Parameterize(self): if gtype in {"raster", "vector", "mapset", "file", "region", "dir"}: gisprompt = True prompt = gtype - if gtype == "raster": - element = "cell" - else: - element = gtype + element = "cell" if gtype == "raster" else gtype ptype = "string" else: gisprompt = False @@ -1102,10 +1099,7 @@ def _setPen(self): group="modeler", key="action", subkey=("width", "default") ) ) - if self.isEnabled: - style = wx.SOLID - else: - style = wx.DOT + style = wx.SOLID if self.isEnabled else wx.DOT pen = wx.Pen(wx.BLACK, width, style) self.SetPen(pen) @@ -1453,10 +1447,7 @@ def SetValue(self, value): self.SetLabel() for direction in ("from", "to"): for rel in self.GetRelations(direction): - if direction == "from": - action = rel.GetTo() - else: - action = rel.GetFrom() + action = rel.GetTo() if direction == "from" else rel.GetFrom() task = GUI(show=None).ParseCommand(cmd=action.GetLog(string=False)) task.set_param(rel.GetLabel(), self.value) @@ -1525,10 +1516,7 @@ def _getPen(self): group="modeler", key="action", subkey=("width", "default") ) ) - if self.intermediate: - style = wx.DOT - else: - style = wx.SOLID + style = wx.DOT if self.intermediate else wx.SOLID return wx.Pen(wx.BLACK, width, style) @@ -1718,10 +1706,7 @@ def __init__( def _setPen(self): """Set pen""" - if self.isEnabled: - style = wx.SOLID - else: - style = wx.DOT + style = wx.SOLID if self.isEnabled else wx.DOT pen = wx.Pen(wx.BLACK, 1, style) self.SetPen(pen) @@ -1986,10 +1971,7 @@ def __init__(self, tree): self.root = self.tree.getroot() # check if input is a valid GXM file if self.root is None or self.root.tag != "gxm": - if self.root is not None: - tagName = self.root.tag - else: - tagName = _("empty") + tagName = self.root.tag if self.root is not None else _("empty") raise GException(_("Details: unsupported tag name '{0}'.").format(tagName)) # list of actions, data @@ -2099,10 +2081,7 @@ def _processActions(self): aId = int(action.get("id", -1)) label = action.get("name") comment = action.find("comment") - if comment is not None: - commentString = comment.text - else: - commentString = "" + commentString = comment.text if comment is not None else "" self.actions.append( { @@ -2515,10 +2494,7 @@ def _data(self, dataList): # relations for ft in ("from", "to"): for rel in data.GetRelations(ft): - if ft == "from": - aid = rel.GetTo().GetId() - else: - aid = rel.GetFrom().GetId() + aid = rel.GetTo().GetId() if ft == "from" else rel.GetFrom().GetId() self.fd.write( '%s\n' % (" " * self.indent, ft, aid, rel.GetLabel()) @@ -2987,10 +2963,7 @@ def _write_input_outputs(self, item, intermediates): parameterized_params = item.GetParameterizedParams() for flag in parameterized_params["flags"]: - if flag["label"]: - desc = flag["label"] - else: - desc = flag["description"] + desc = flag["label"] or flag["description"] if flag["value"]: value = '\n{}default="{}"'.format( @@ -3256,12 +3229,7 @@ def _getPythonActionCmd(self, item, task, cmdIndent, variables={}): return ret def _getParamDesc(self, param): - if param["label"]: - desc = param["label"] - else: - desc = param["description"] - - return desc + return param["label"] or param["description"] def _getParamValue(self, param): if param["value"] and "output" not in param["name"]: @@ -3374,10 +3342,7 @@ def _writePython(self): for item in modelItems: parametrizedParams = item.GetParameterizedParams() for flag in parametrizedParams["flags"]: - if flag["label"]: - desc = flag["label"] - else: - desc = flag["description"] + desc = flag["label"] or flag["description"] self.fd.write( r"""# %option # % key: {flag_name} @@ -3398,10 +3363,7 @@ def _writePython(self): self.fd.write("# %end\n") for param in parametrizedParams["params"]: - if param["label"]: - desc = param["label"] - else: - desc = param["description"] + desc = param["label"] or param["description"] self.fd.write( r"""# %option # % key: {param_name} diff --git a/gui/wxpython/gmodeler/panels.py b/gui/wxpython/gmodeler/panels.py index 416a9b85592..fbdc2048620 100644 --- a/gui/wxpython/gmodeler/panels.py +++ b/gui/wxpython/gmodeler/panels.py @@ -622,10 +622,7 @@ def LoadModelFile(self, filename): item.Show(True) # relations/data for rel in item.GetRelations(): - if rel.GetFrom() == item: - dataItem = rel.GetTo() - else: - dataItem = rel.GetFrom() + dataItem = rel.GetTo() if rel.GetFrom() == item else rel.GetFrom() self._addEvent(dataItem) self.canvas.diagram.AddShape(dataItem) self.AddLine(rel) @@ -1663,13 +1660,8 @@ def GetScriptExt(self): """Get extension for script exporting. :return: script extension """ - if self.write_object == WriteActiniaFile: - ext = "json" - else: - # Python, PyWPS - ext = "py" - - return ext + # return "py" for Python, PyWPS + return "json" if self.write_object == WriteActiniaFile else "py" def SetWriteObject(self, script_type): """Set correct self.write_object depending on the script type. diff --git a/gui/wxpython/gui_core/dialogs.py b/gui/wxpython/gui_core/dialogs.py index a38836ac23e..27ce4523a76 100644 --- a/gui/wxpython/gui_core/dialogs.py +++ b/gui/wxpython/gui_core/dialogs.py @@ -466,10 +466,7 @@ def CreateNewVector( """ vExternalOut = grass.parse_command("v.external.out", flags="g") isNative = vExternalOut["format"] == "native" - if cmd[0] == "v.edit" and not isNative: - showType = True - else: - showType = False + showType = bool(cmd[0] == "v.edit" and not isNative) dlg = NewVectorDialog( parent, title=title, @@ -1340,10 +1337,7 @@ def ShowResult(self, group, returnCode, create): def GetSelectedGroup(self): """Return currently selected group (without mapset)""" g = self.groupSelect.GetValue().split("@")[0] - if self.edit_subg: - s = self.subGroupSelect.GetValue() - else: - s = None + s = self.subGroupSelect.GetValue() if self.edit_subg else None return g, s def GetGroupLayers(self, group, subgroup=None): @@ -1374,10 +1368,7 @@ def ApplyChanges(self): GMessage(parent=self, message=_("No subgroup selected.")) return 0 - if self.edit_subg: - subgroup = self.currentSubgroup - else: - subgroup = None + subgroup = self.currentSubgroup if self.edit_subg else None groups = self.GetExistGroups() if group in groups: diff --git a/gui/wxpython/gui_core/forms.py b/gui/wxpython/gui_core/forms.py index 2636b213df3..7172835f001 100644 --- a/gui/wxpython/gui_core/forms.py +++ b/gui/wxpython/gui_core/forms.py @@ -193,10 +193,7 @@ def run(self): if not pMap: pMap = self.task.get_param("input", raiseError=False) - if pMap: - map = pMap.get("value", "") - else: - map = None + map = pMap.get("value", "") if pMap else None # avoid running db.describe several times cparams = {} @@ -278,10 +275,7 @@ def run(self): elif p.get("element", "") in {"layer", "layer_all"}: # -> layer # get layer layer = p.get("value", "") - if layer != "": - layer = p.get("value", "") - else: - layer = p.get("default", "") + layer = p.get("value", "") if layer != "" else p.get("default", "") # get map name pMapL = self.task.get_param( @@ -722,10 +716,7 @@ def __init__( sizeFrame = self.GetBestSize() self.SetMinSize(sizeFrame) - if hasattr(self, "closebox"): - scale = 0.33 - else: - scale = 0.50 + scale = 0.33 if hasattr(self, "closebox") else 0.5 self.SetSize( wx.Size( round(sizeFrame[0]), @@ -813,10 +804,7 @@ def OnMapCreated(self, name, ltype, add: bool | None = None): :param ltype: layer type (prompt value) :param add: whether to display layer or not """ - if hasattr(self, "addbox") and self.addbox.IsChecked(): - add = True - else: - add = False + add = bool(hasattr(self, "addbox") and self.addbox.IsChecked()) if self._giface: self._giface.mapCreated.emit(name=name, ltype=ltype, add=add) @@ -1156,10 +1144,7 @@ def __init__(self, parent, giface, task, id=wx.ID_ANY, frame=None, *args, **kwar else: title_sizer = wx.BoxSizer(wx.HORIZONTAL) title_txt = StaticText(parent=which_panel) - if p["key_desc"]: - ltype = ",".join(p["key_desc"]) - else: - ltype = p["type"] + ltype = ",".join(p["key_desc"]) if p["key_desc"] else p["type"] # red star for required options if p.get("required", False): required_txt = StaticText(parent=which_panel, label="*") @@ -1900,10 +1885,7 @@ def __init__(self, parent, giface, task, id=wx.ID_ANY, frame=None, *args, **kwar win.Bind(wx.EVT_COMBOBOX, self.OnSetValue) elif prompt == "mapset": - if p.get("age", "old") == "old": - new = False - else: - new = True + new = p.get("age", "old") != "old" win = gselect.MapsetSelect( parent=which_panel, @@ -2008,10 +1990,7 @@ def __init__(self, parent, giface, task, id=wx.ID_ANY, frame=None, *args, **kwar # file selector elif p.get("prompt", "") != "color" and p.get("prompt", "") == "file": - if p.get("age", "new") == "new": - fmode = wx.FD_SAVE - else: - fmode = wx.FD_OPEN + fmode = wx.FD_SAVE if p.get("age", "new") == "new" else wx.FD_OPEN # check wildcard try: fExt = os.path.splitext(p.get("key_desc", ["*.*"])[0])[1] @@ -2770,11 +2749,7 @@ def OnVerbosity(self, event): event.Skip() def OnPageChange(self, event): - if not event: - sel = self.notebook.GetSelection() - else: - sel = event.GetSelection() - + sel = self.notebook.GetSelection() if not event else event.GetSelection() idx = self.notebook.GetPageIndexByName("manual") if idx > -1 and sel == idx: # calling LoadPage() is strangely time-consuming (only first call) diff --git a/gui/wxpython/gui_core/gselect.py b/gui/wxpython/gui_core/gselect.py index ff256803a97..67b8a1280e2 100644 --- a/gui/wxpython/gui_core/gselect.py +++ b/gui/wxpython/gui_core/gselect.py @@ -1394,10 +1394,7 @@ def __init__( super().__init__(parent, id=wx.ID_ANY, size=size, **kwargs) self.SetName("FormatSelect") - if ogr: - ftype = "ogr" - else: - ftype = "gdal" + ftype = "ogr" if ogr else "gdal" formats = [] for f in GetFormats()[ftype][srcType].items(): @@ -1504,10 +1501,7 @@ def __init__( self.protocolWidgets = {} self.pgWidgets = {} - if ogr: - fType = "ogr" - else: - fType = "gdal" + fType = "ogr" if ogr else "gdal" # file fileMask = "%(all)s (*)|*|" % {"all": _("All files")} @@ -2290,12 +2284,7 @@ def hasRastSameProjAsLocation(dsn, table=None): return projectionMatch def getProjMatchCaption(projectionMatch): - if projectionMatch == "0": - projectionMatchCaption = _("No") - else: - projectionMatchCaption = _("Yes") - - return projectionMatchCaption + return _("No") if projectionMatch == "0" else _("Yes") dsn = self.GetDsn() if not dsn: @@ -2440,15 +2429,9 @@ def OnHelp(self, event): """Show related manual page""" cmd = "" if self.dest: - if self.ogr: - cmd = "v.external.out" - else: - cmd = "r.external.out" + cmd = "v.external.out" if self.ogr else "r.external.out" elif self.link: - if self.ogr: - cmd = "v.external" - else: - cmd = "r.external" + cmd = "v.external" if self.ogr else "r.external" elif self.ogr: cmd = "v.in.ogr" else: diff --git a/gui/wxpython/gui_core/mapdisp.py b/gui/wxpython/gui_core/mapdisp.py index a2810c9892b..ea770defe8e 100644 --- a/gui/wxpython/gui_core/mapdisp.py +++ b/gui/wxpython/gui_core/mapdisp.py @@ -380,10 +380,7 @@ def StatusbarEnableLongHelp(self, enable=True): toolbar.EnableLongHelp(enable) def ShowAllToolbars(self, show=True): - if not show: # hide - action = self.RemoveToolbar - else: - action = self.AddToolbar + action = self.RemoveToolbar if not show else self.AddToolbar for toolbar in self.GetToolbarNames(): action(toolbar) diff --git a/gui/wxpython/gui_core/menu.py b/gui/wxpython/gui_core/menu.py index b284461af0a..34d6eeffd31 100644 --- a/gui/wxpython/gui_core/menu.py +++ b/gui/wxpython/gui_core/menu.py @@ -91,10 +91,7 @@ def _createMenuItem( menu.AppendSeparator() return - if command: - helpString = command + " -- " + description - else: - helpString = description + helpString = command + " -- " + description if command else description if shortcut: label += "\t" + shortcut diff --git a/gui/wxpython/gui_core/preferences.py b/gui/wxpython/gui_core/preferences.py index 724e4a5aec0..85e5313da0e 100644 --- a/gui/wxpython/gui_core/preferences.py +++ b/gui/wxpython/gui_core/preferences.py @@ -2268,10 +2268,7 @@ def OnEnableWheelZoom(self, event): """Enable/disable wheel zoom mode control""" choiceId = self.winId["display:mouseWheelZoom:selection"] choice = self.FindWindowById(choiceId) - if choice.GetSelection() == 2: - enable = False - else: - enable = True + enable = choice.GetSelection() != 2 scrollId = self.winId["display:scrollDirection:selection"] self.FindWindowById(scrollId).Enable(enable) diff --git a/gui/wxpython/gui_core/prompt.py b/gui/wxpython/gui_core/prompt.py index 6734de169a9..3857f04fb32 100644 --- a/gui/wxpython/gui_core/prompt.py +++ b/gui/wxpython/gui_core/prompt.py @@ -428,10 +428,7 @@ def GetWordLeft(self, withDelimiter=False, ignoredDelimiter=None): ignoredDelimiter = "" for char in set(" .,-=") - set(ignoredDelimiter): - if not withDelimiter: - delimiter = "" - else: - delimiter = char + delimiter = "" if not withDelimiter else char parts.append(delimiter + textLeft.rpartition(char)[2]) return min(parts, key=lambda x: len(x)) diff --git a/gui/wxpython/gui_core/treeview.py b/gui/wxpython/gui_core/treeview.py index a7660308843..2fde45feec8 100644 --- a/gui/wxpython/gui_core/treeview.py +++ b/gui/wxpython/gui_core/treeview.py @@ -209,10 +209,7 @@ class CTreeView(AbstractTreeViewMixin, CustomTreeCtrl): """Tree view class inheriting from wx.TreeCtrl""" def __init__(self, model, parent, **kw): - if hasAgw: - style = "agwStyle" - else: - style = "style" + style = "agwStyle" if hasAgw else "style" if style not in kw: kw[style] = ( diff --git a/gui/wxpython/iclass/dialogs.py b/gui/wxpython/iclass/dialogs.py index 9aaa7e31604..615e614689f 100644 --- a/gui/wxpython/iclass/dialogs.py +++ b/gui/wxpython/iclass/dialogs.py @@ -588,11 +588,7 @@ def ContrastColor(color): # gacek, # https://stackoverflow.com/questions/1855884/determine-font-color-based-on-background-color a = 1 - (0.299 * color[0] + 0.587 * color[1] + 0.114 * color[2]) / 255 - - if a < 0.5: - d = 0 - else: - d = 255 + d = 0 if a < 0.5 else 255 # maybe return just bool if text should be dark or bright return (d, d, d) diff --git a/gui/wxpython/iclass/digit.py b/gui/wxpython/iclass/digit.py index 85334993db1..7442c24ab5d 100644 --- a/gui/wxpython/iclass/digit.py +++ b/gui/wxpython/iclass/digit.py @@ -156,15 +156,9 @@ def CopyMap(self, name, tmp=False, update=False): poMapInfoNew = pointer(Map_info()) if not tmp: - if update: - open_fn = Vect_open_update - else: - open_fn = Vect_open_new - else: # noqa: PLR5501 - if update: - open_fn = Vect_open_tmp_update - else: - open_fn = Vect_open_tmp_new + open_fn = Vect_open_update if update else Vect_open_new + else: + open_fn = Vect_open_tmp_update if update else Vect_open_tmp_new if update: if open_fn(poMapInfoNew, name, "") == -1: diff --git a/gui/wxpython/image2target/ii2t_gis_set.py b/gui/wxpython/image2target/ii2t_gis_set.py index 6ed97bc46c1..0329516e04a 100644 --- a/gui/wxpython/image2target/ii2t_gis_set.py +++ b/gui/wxpython/image2target/ii2t_gis_set.py @@ -1014,10 +1014,7 @@ def OnSetDatabase(self, event): def OnBrowse(self, event): """'Browse' button clicked""" - if not event: - defaultPath = os.getenv("HOME") - else: - defaultPath = "" + defaultPath = os.getenv("HOME") if not event else "" dlg = wx.DirDialog( parent=self, diff --git a/gui/wxpython/image2target/ii2t_manager.py b/gui/wxpython/image2target/ii2t_manager.py index 3adcf5ef9bd..939c2fb0fe8 100644 --- a/gui/wxpython/image2target/ii2t_manager.py +++ b/gui/wxpython/image2target/ii2t_manager.py @@ -488,11 +488,7 @@ def __init__(self, wizard, parent): def OnMaptype(self, event): """Change map type""" global maptype - - if event.GetInt() == 0: - maptype = "raster" - else: - maptype = "vector" + maptype = "raster" if event.GetInt() == 0 else "vector" def OnLocation(self, event): """Sets source location for map(s) to georectify""" @@ -1416,10 +1412,7 @@ def SetGCPSatus(self, item, itemIndex): else: item.SetPropertyVal("hide", False) if self.highest_only: - if itemIndex == self.highest_key: - wxPen = "highest" - else: - wxPen = "default" + wxPen = "highest" if itemIndex == self.highest_key else "default" elif self.mapcoordlist[key][7] > self.rmsthresh: wxPen = "highest" else: @@ -1702,10 +1695,7 @@ def OnFocus(self, event): pass def _onMouseLeftUpPointer(self, mapWindow, x, y): - if mapWindow == self.SrcMapWindow: - coordtype = "source" - else: - coordtype = "target" + coordtype = "source" if mapWindow == self.SrcMapWindow else "target" coord = (x, y) self.SetGCPData(coordtype, coord, self, confirm=True) @@ -1761,11 +1751,7 @@ def OnGeorect(self, event): if maptype == "raster": self.grwiz.SwitchEnv("source") - - if self.clip_to_region: - flags = "ac" - else: - flags = "a" + flags = "ac" if self.clip_to_region else "a" with wx.BusyInfo(_("Rectifying images, please wait..."), parent=self): wx.GetApp().Yield() diff --git a/gui/wxpython/iscatt/controllers.py b/gui/wxpython/iscatt/controllers.py index fef4e95505e..06ca5d0b243 100644 --- a/gui/wxpython/iscatt/controllers.py +++ b/gui/wxpython/iscatt/controllers.py @@ -149,10 +149,7 @@ def SetBands(self, bands): callable=self.core.CleanUp, ondone=lambda event: self.CleanUpDone() ) - if self.show_add_scatt_plot: - show_add = True - else: - show_add = False + show_add = bool(self.show_add_scatt_plot) self.all_bands_to_bands = dict(zip(bands, [-1] * len(bands))) self.all_bands = bands @@ -697,10 +694,7 @@ def SetData(self): def AddCategory(self, cat_id=None, name=None, color=None, nstd=None): if cat_id is None: - if self.cats_ids: - cat_id = max(self.cats_ids) + 1 - else: - cat_id = 1 + cat_id = max(self.cats_ids) + 1 if self.cats_ids else 1 if self.scatt_mgr.data_set: self.scatt_mgr.thread.Run(callable=self.core.AddCategory, cat_id=cat_id) diff --git a/gui/wxpython/iscatt/core_c.py b/gui/wxpython/iscatt/core_c.py index 8be04964b90..a6e6ac5c8a0 100644 --- a/gui/wxpython/iscatt/core_c.py +++ b/gui/wxpython/iscatt/core_c.py @@ -214,11 +214,7 @@ def _regionToCellHead(region): } for k, v in region.items(): - if k in {"rows", "cols", "cells", "zone"}: # zone added in r65224 - v = int(v) - else: - v = float(v) - + v = int(v) if k in {"rows", "cols", "cells", "zone"} else float(v) if k in convert_dict: k = convert_dict[k] diff --git a/gui/wxpython/iscatt/frame.py b/gui/wxpython/iscatt/frame.py index 122aa053b9b..5350ecbd8a0 100644 --- a/gui/wxpython/iscatt/frame.py +++ b/gui/wxpython/iscatt/frame.py @@ -568,11 +568,7 @@ def OnCategoryRightUp(self, event): item = menu.Append(wx.ID_ANY, _("Change opacity level")) self.Bind(wx.EVT_MENU, self.OnPopupOpacityLevel, item) - if showed: - text = _("Hide") - else: - text = _("Show") - + text = _("Hide") if showed else _("Show") item = menu.Append(wx.ID_ANY, text) self.Bind( wx.EVT_MENU, diff --git a/gui/wxpython/iscatt/iscatt_core.py b/gui/wxpython/iscatt/iscatt_core.py index 09214d08e42..52cecbfb381 100644 --- a/gui/wxpython/iscatt/iscatt_core.py +++ b/gui/wxpython/iscatt/iscatt_core.py @@ -805,11 +805,7 @@ def _parseRegion(region_str): for param in region_str: k, v = param.split("=") - if k in {"rows", "cols", "cells"}: - v = int(v) - else: - v = float(v) - region[k] = v + region[k] = int(v) if k in {"rows", "cols", "cells"} else float(v) return region diff --git a/gui/wxpython/lmgr/frame.py b/gui/wxpython/lmgr/frame.py index 963a36e3a58..9879ce1d937 100644 --- a/gui/wxpython/lmgr/frame.py +++ b/gui/wxpython/lmgr/frame.py @@ -747,10 +747,7 @@ def OnLocationWizard(self, event): self._giface.grassdbChanged.emit( grassdb=grassdb, location=location, action="new", element="location" ) - if grassdb == gisenv["GISDBASE"]: - switch_grassdb = None - else: - switch_grassdb = grassdb + switch_grassdb = grassdb if grassdb != gisenv["GISDBASE"] else None if can_switch_mapset_interactive(self, grassdb, location, mapset): switch_mapset_interactively( self, @@ -1116,10 +1113,7 @@ def GetMenuCmd(self, event): :return: command as a list""" layer = None - if event: - cmd = self.menucmd[event.GetId()] - else: - cmd = "" + cmd = self.menucmd[event.GetId()] if event else "" try: cmdlist = cmd.split(" ") diff --git a/gui/wxpython/lmgr/layertree.py b/gui/wxpython/lmgr/layertree.py index 855896e35ab..350320d97a7 100644 --- a/gui/wxpython/lmgr/layertree.py +++ b/gui/wxpython/lmgr/layertree.py @@ -688,10 +688,7 @@ def OnLayerContextMenu(self, event): ) digitToolbar = self.mapdisplay.GetToolbar("vdigit") - if digitToolbar: - vdigitLayer = digitToolbar.GetLayer() - else: - vdigitLayer = None + vdigitLayer = digitToolbar.GetLayer() if digitToolbar else None layer = self.GetLayerInfo(self.layer_selected, key="maplayer") if vdigitLayer is not layer: item = wx.MenuItem( @@ -1569,10 +1566,7 @@ def AddLayer( name = None - if ctrl: - ctrlId = ctrl.GetId() - else: - ctrlId = None + ctrlId = ctrl.GetId() if ctrl else None # add a data object to hold the layer's command (does not # apply to generic command layers) @@ -1606,11 +1600,7 @@ def AddLayer( prevMapLayer = self.GetLayerInfo(prevItem, key="maplayer") prevItem = self.GetNextItem(prevItem) - - if prevMapLayer: - pos = self.Map.GetLayerIndex(prevMapLayer) - else: - pos = -1 + pos = self.Map.GetLayerIndex(prevMapLayer) if prevMapLayer else -1 maplayer = self.Map.AddLayer( pos=pos, @@ -2063,12 +2053,8 @@ def RecreateItem(self, dragItem, dropTarget, parent=None): # decide where to put recreated item if dropTarget is not None and dropTarget != self.GetRootItem(): - if parent: - # new item is a group - afteritem = parent - else: - # new item is a single layer - afteritem = dropTarget + # new item is a group (parent is truthy) or else new item is a single layer + afteritem = parent or dropTarget # dragItem dropped on group if self.GetLayerInfo(afteritem, key="type") == "group": diff --git a/gui/wxpython/lmgr/menudata.py b/gui/wxpython/lmgr/menudata.py index 59eb72ecb67..88f769af403 100644 --- a/gui/wxpython/lmgr/menudata.py +++ b/gui/wxpython/lmgr/menudata.py @@ -25,10 +25,7 @@ class LayerManagerMenuData(MenuTreeModelBuilder): def __init__(self, filename=None, message_handler=GError): - if filename: - expandAddons = False - else: - expandAddons = True + expandAddons = not filename fallback = os.path.join(WXGUIDIR, "xml", "menudata.xml") if not filename: @@ -57,10 +54,7 @@ def __init__(self, filename=None, message_handler=GError): class LayerManagerModuleTree(MenuTreeModelBuilder): def __init__(self, filename=None, message_handler=GError): - if filename: - expandAddons = False - else: - expandAddons = True + expandAddons = not filename fallback = os.path.join(WXGUIDIR, "xml", "module_tree_menudata.xml") if not filename: diff --git a/gui/wxpython/location_wizard/wizard.py b/gui/wxpython/location_wizard/wizard.py index 1f8de66dda6..d553b4e11d9 100644 --- a/gui/wxpython/location_wizard/wizard.py +++ b/gui/wxpython/location_wizard/wizard.py @@ -2760,10 +2760,7 @@ def CreateProj4String(self): # set ellipsoid parameters for item in ellipseparams: - if item[:4] == "f=1/": - item = " +rf=" + item[4:] - else: - item = " +" + item + item = " +rf=" + item[4:] if item[:4] == "f=1/" else " +" + item proj4string = "%s %s" % (proj4string, item) # set datum transform parameters if relevant diff --git a/gui/wxpython/main_window/frame.py b/gui/wxpython/main_window/frame.py index cef4aee7121..3574e7c8951 100644 --- a/gui/wxpython/main_window/frame.py +++ b/gui/wxpython/main_window/frame.py @@ -861,10 +861,7 @@ def OnLocationWizard(self, event): self._giface.grassdbChanged.emit( grassdb=grassdb, location=location, action="new", element="location" ) - if grassdb == gisenv["GISDBASE"]: - switch_grassdb = None - else: - switch_grassdb = grassdb + switch_grassdb = grassdb if grassdb != gisenv["GISDBASE"] else None if can_switch_mapset_interactive(self, grassdb, location, mapset): switch_mapset_interactively( self, @@ -1268,10 +1265,7 @@ def GetMenuCmd(self, event): :return: command as a list""" layer = None - if event: - cmd = self.menucmd[event.GetId()] - else: - cmd = "" + cmd = self.menucmd[event.GetId()] if event else "" try: cmdlist = cmd.split(" ") diff --git a/gui/wxpython/mapdisp/frame.py b/gui/wxpython/mapdisp/frame.py index 0a1660ff06a..3133341a334 100644 --- a/gui/wxpython/mapdisp/frame.py +++ b/gui/wxpython/mapdisp/frame.py @@ -783,10 +783,7 @@ def DOutFile(self, command, callback=None): # --overwrite continue if p == "format": # must be there - if self.IsPaneShown("3d"): - extType = "ppm" - else: - extType = val + extType = "ppm" if self.IsPaneShown("3d") else val if p == "output": # must be there name = val elif p == "size": @@ -798,10 +795,8 @@ def DOutFile(self, command, callback=None): elif ext[1:] != extType: extType = ext[1:] - if self.IsPaneShown("3d"): - bitmapType = "ppm" - else: - bitmapType = wx.BITMAP_TYPE_PNG # default type + # default type is PNG + bitmapType = "ppm" if self.IsPaneShown("3d") else wx.BITMAP_TYPE_PNG for each in ltype: if each["ext"] == extType: bitmapType = each["type"] diff --git a/gui/wxpython/mapdisp/toolbars.py b/gui/wxpython/mapdisp/toolbars.py index 4eb49846d4f..18459b26e17 100644 --- a/gui/wxpython/mapdisp/toolbars.py +++ b/gui/wxpython/mapdisp/toolbars.py @@ -285,10 +285,7 @@ def RemoveTool(self, tool): def ChangeToolsDesc(self, mode2d): """Change description of zoom tools for 2D/3D view""" - if mode2d: - icons = BaseIcons - else: - icons = NvizIcons + icons = BaseIcons if mode2d else NvizIcons for i, data in enumerate(self.controller.data): for tool in ("zoomIn", "zoomOut"): if data[0] == tool: diff --git a/gui/wxpython/mapwin/buffered.py b/gui/wxpython/mapwin/buffered.py index c4b07a94fd2..16b6b329ef6 100644 --- a/gui/wxpython/mapwin/buffered.py +++ b/gui/wxpython/mapwin/buffered.py @@ -344,10 +344,7 @@ def Draw( # TODO: find better solution if not pen: - if pdctype == "polyline": - pen = self.polypen - else: - pen = self.pen + pen = self.polypen if pdctype == "polyline" else self.pen if img and pdctype == "image": # self.imagedict[img]['coords'] = coords @@ -501,10 +498,7 @@ def Draw( elif pdctype == "text": # draw text on top of map if not img["active"]: return # only draw active text - if "rotation" in img: - rotation = float(img["rotation"]) - else: - rotation = 0.0 + rotation = float(img["rotation"]) if "rotation" in img else 0.0 w, h = self.GetFullTextExtent(img["text"])[0:2] pdc.SetFont(img["font"]) pdc.SetTextForeground(img["color"]) @@ -536,10 +530,7 @@ def TextBounds(self, textinfo, relcoords=False): :return: bbox of rotated text bbox (wx.Rect) :return: relCoords are text coord inside bbox """ - if "rotation" in textinfo: - rotation = float(textinfo["rotation"]) - else: - rotation = 0.0 + rotation = float(textinfo["rotation"]) if "rotation" in textinfo else 0.0 coords = textinfo["coords"] bbox = Rect(coords[0], coords[1], 0, 0) @@ -1469,10 +1460,7 @@ def OnMouseWheel(self, event): wheel = event.GetWheelRotation() Debug.msg(5, "BufferedWindow.MouseAction(): wheel=%d" % wheel) - if wheel > 0: - zoomtype = 1 - else: - zoomtype = -1 + zoomtype = 1 if wheel > 0 else -1 if UserSettings.Get(group="display", key="scrollDirection", subkey="selection"): zoomtype *= -1 # zoom 1/2 of the screen (TODO: settings) @@ -1504,10 +1492,7 @@ def OnDragging(self, event): previous = self.mouse["begin"] move = (current[0] - previous[0], current[1] - previous[1]) - if self.digit: - digitToolbar = self.toolbar - else: - digitToolbar = None + digitToolbar = self.toolbar if self.digit else None # dragging or drawing box with left button if self.mouse["use"] == "pan" or event.MiddleIsDown(): diff --git a/gui/wxpython/mapwin/decorations.py b/gui/wxpython/mapwin/decorations.py index 24db63d8492..9c643d17752 100644 --- a/gui/wxpython/mapwin/decorations.py +++ b/gui/wxpython/mapwin/decorations.py @@ -323,14 +323,8 @@ def ResizeLegend(self, begin, end, screenSize): """Resize legend according to given bbox coordinates.""" w = abs(begin[0] - end[0]) h = abs(begin[1] - end[1]) - if begin[0] < end[0]: - x = begin[0] - else: - x = end[0] - if begin[1] < end[1]: - y = begin[1] - else: - y = end[1] + x = min(end[0], begin[0]) + y = min(end[1], begin[1]) at = [ (screenSize[1] - (y + h)) / float(screenSize[1]) * 100, diff --git a/gui/wxpython/modules/colorrules.py b/gui/wxpython/modules/colorrules.py index 6f48006db44..dcb34619b57 100644 --- a/gui/wxpython/modules/colorrules.py +++ b/gui/wxpython/modules/colorrules.py @@ -977,10 +977,7 @@ def OnSelectionInput(self, event): self.cr_label.SetLabel(_("Enter raster category values or percents")) return - if info["datatype"] == "CELL": - mapRange = _("range") - else: - mapRange = _("fp range") + mapRange = _("range") if info["datatype"] == "CELL" else _("fp range") self.cr_label.SetLabel( _("Enter raster category values or percents (%(range)s = %(min)d-%(max)d)") % { @@ -1409,10 +1406,7 @@ def AddTemporaryColumn(self, type): idx += 1 self.properties["tmpColumn"] = name + "_" + str(idx) - if self.version7: - modul = "v.db.addcolumn" - else: - modul = "v.db.addcol" + modul = "v.db.addcolumn" if self.version7 else "v.db.addcol" RunCommand( modul, parent=self, @@ -1427,10 +1421,7 @@ def DeleteTemporaryColumn(self): return if self.inmap: - if self.version7: - modul = "v.db.dropcolumn" - else: - modul = "v.db.dropcol" + modul = "v.db.dropcolumn" if self.version7 else "v.db.dropcol" RunCommand( modul, map=self.inmap, @@ -1452,10 +1443,7 @@ def OnLayerSelection(self, event): self.sourceColumn.SetValue("cat") self.properties["sourceColumn"] = self.sourceColumn.GetValue() - if self.attributeType == "color": - type = ["character"] - else: - type = ["integer"] + type = ["character"] if self.attributeType == "color" else ["integer"] self.fromColumn.InsertColumns( vector=self.inmap, layer=vlayer, @@ -1502,10 +1490,7 @@ def OnAddColumn(self, event): self.columnsProp[self.attributeType]["name"] not in self.fromColumn.GetColumns() ): - if self.version7: - modul = "v.db.addcolumn" - else: - modul = "v.db.addcol" + modul = "v.db.addcolumn" if self.version7 else "v.db.addcol" RunCommand( modul, map=self.inmap, diff --git a/gui/wxpython/modules/extensions.py b/gui/wxpython/modules/extensions.py index f7822e0cb0e..7837a92589d 100644 --- a/gui/wxpython/modules/extensions.py +++ b/gui/wxpython/modules/extensions.py @@ -356,11 +356,7 @@ def _expandPrefix(self, c): def Load(self, url, full=True): """Load list of extensions""" self._emptyTree() - - if full: - flags = "g" - else: - flags = "l" + flags = "g" if full else "l" retcode, ret, msg = RunCommand( "g.extension", read=True, getErrorMsg=True, url=url, flags=flags, quiet=True ) diff --git a/gui/wxpython/modules/import_export.py b/gui/wxpython/modules/import_export.py index 6810484bfdd..db4e3b517a2 100644 --- a/gui/wxpython/modules/import_export.py +++ b/gui/wxpython/modules/import_export.py @@ -328,20 +328,14 @@ def AddLayers(self, returncode, cmd=None, userData=None): self.commandId += 1 layer, output = self.list.GetLayers()[self.commandId][:2] - if "@" not in output: - name = output + "@" + grass.gisenv()["MAPSET"] - else: - name = output + name = output + "@" + grass.gisenv()["MAPSET"] if "@" not in output else output # add imported layers into layer tree # an alternative would be emit signal (mapCreated) and (optionally) # connect to this signal llist = self._giface.GetLayerList() if self.importType == "gdal": - if userData: - nBands = int(userData.get("nbands", 1)) - else: - nBands = 1 + nBands = int(userData.get("nbands", 1)) if userData else 1 if UserSettings.Get(group="rasterLayer", key="opaque", subkey="enabled"): nFlag = True diff --git a/gui/wxpython/modules/mcalc_builder.py b/gui/wxpython/modules/mcalc_builder.py index 52630e55820..4294a9730de 100644 --- a/gui/wxpython/modules/mcalc_builder.py +++ b/gui/wxpython/modules/mcalc_builder.py @@ -681,10 +681,7 @@ def OnMCalcRun(self, event): self.log.RunCmd(cmd, onDone=self.OnDone) self.parent.Raise() else: - if self.overwrite.IsChecked(): - overwrite = True - else: - overwrite = False + overwrite = bool(self.overwrite.IsChecked()) params = {"expression": "%s=%s" % (name, expr), "overwrite": overwrite} if seed_flag: params["flags"] = "s" diff --git a/gui/wxpython/nviz/mapwindow.py b/gui/wxpython/nviz/mapwindow.py index 1eea5b278a9..d3f93f94938 100644 --- a/gui/wxpython/nviz/mapwindow.py +++ b/gui/wxpython/nviz/mapwindow.py @@ -382,10 +382,7 @@ def OnEraseBackground(self, event): def OnSize(self, event): size = self.GetClientSize() - if CheckWxVersion(version=[2, 9]): - context = self.context - else: - context = self.GetContext() + context = self.context if CheckWxVersion(version=[2, 9]) else self.GetContext() if self.size != size and context: Debug.msg( 3, "GLCanvas.OnSize(): w = %d, h = %d" % (size.width, size.height) diff --git a/gui/wxpython/nviz/tools.py b/gui/wxpython/nviz/tools.py index 7ced7d157e1..150fe519635 100644 --- a/gui/wxpython/nviz/tools.py +++ b/gui/wxpython/nviz/tools.py @@ -3210,10 +3210,7 @@ def _createControl( "style": style, "size": sizeW, } - if floatSlider: - slider = FloatSlider(**kwargs) - else: - slider = Slider(**kwargs) + slider = FloatSlider(**kwargs) if floatSlider else Slider(**kwargs) slider.SetName("slider") if bind[0]: @@ -3422,22 +3419,17 @@ def OnViewChange(self, event): self.AdjustSliderRange(slider=slider, value=value) - if winName == "height": - view = self.mapWindow.iview # internal - else: - view = self.mapWindow.view + # iview is internal + view = self.mapWindow.iview if winName == "height" else self.mapWindow.view if winName == "z-exag" and value >= 0: self.PostViewEvent(zExag=True) else: self.PostViewEvent(zExag=False) - if winName in {"persp", "twist"}: - convert = int - else: - convert = float - - view[winName]["value"] = convert(value) + view[winName]["value"] = ( + int(value) if winName in {"persp", "twist"} else float(value) + ) for win in self.win["view"][winName].values(): self.FindWindowById(win).SetValue(value) @@ -3650,10 +3642,8 @@ def EnablePage(self, name, enabled=True): def SetMapObjUseMap(self, nvizType, attrb, map=None): """Update dialog widgets when attribute type changed""" - if attrb in {"topo", "color", "shine"}: - incSel = -1 # decrement selection (no 'unset') - else: - incSel = 0 + # decrement selection (no 'unset') + incSel = -1 if attrb in {"topo", "color", "shine"} else 0 if nvizType == "volume" and attrb == "topo": return if map is True: # map @@ -3881,11 +3871,7 @@ def _get3dRange(self, name): def _getPercent(self, value, toPercent=True): """Convert values 0 - 255 to percents and vice versa""" value = int(value) - if toPercent: - value = int(value / 255.0 * 100) - else: - value = int(value / 100.0 * 255) - return value + return int(value / 255.0 * 100) if toPercent else int(value / 100.0 * 255) def OnSurfaceWireColor(self, event): """Set wire color""" @@ -4199,10 +4185,7 @@ def OnVectorHeightText(self, event): def OnVectorSurface(self, event): """Reference surface for vector map (lines/points)""" id = event.GetId() - if id == self.win["vector"]["lines"]["surface"]: - vtype = "lines" - else: - vtype = "points" + vtype = "lines" if id == self.win["vector"]["lines"]["surface"] else "points" checkList = self.FindWindowById(self.win["vector"][vtype]["surface"]) checked = [] surfaces = [] @@ -4278,10 +4261,7 @@ def OnCheckThematic(self, event): check = self.win["vector"][vtype]["thematic"]["check" + attrType] button = self.win["vector"][vtype]["thematic"]["button" + attrType] - if self.FindWindowById(check).GetValue(): - checked = True - else: - checked = False + checked = bool(self.FindWindowById(check).GetValue()) self.FindWindowById(button).Enable(checked) data = self.GetLayerData("vector") diff --git a/gui/wxpython/nviz/workspace.py b/gui/wxpython/nviz/workspace.py index 1e38aee239a..ac9e0582b2d 100644 --- a/gui/wxpython/nviz/workspace.py +++ b/gui/wxpython/nviz/workspace.py @@ -136,10 +136,7 @@ def SetVolumeDefaultProp(self): sel = UserSettings.Get( group="nviz", key="volume", subkey=["draw", "mode"] ) - if sel == 0: - desc = "isosurface" - else: - desc = "slice" + desc = "isosurface" if sel == 0 else "slice" data["draw"]["mode"] = { "value": sel, "desc": desc, diff --git a/gui/wxpython/nviz/wxnviz.py b/gui/wxpython/nviz/wxnviz.py index 5a223747277..35995d30e6b 100644 --- a/gui/wxpython/nviz/wxnviz.py +++ b/gui/wxpython/nviz/wxnviz.py @@ -885,11 +885,7 @@ def SetSurfaceAttr(self, id, attr, map, value): if map: ret = Nviz_set_attr(id, MAP_OBJ_SURF, attr, MAP_ATT, value, -1.0, self.data) else: - if attr == ATT_COLOR: - val = Nviz_color_from_str(value) - else: - val = float(value) - + val = Nviz_color_from_str(value) if attr == ATT_COLOR else float(value) ret = Nviz_set_attr(id, MAP_OBJ_SURF, attr, CONST_ATT, None, val, self.data) Debug.msg( @@ -1682,11 +1678,7 @@ def SetIsosurfaceAttr(self, id, isosurf_id, attr, map, value): if map: ret = GVL_isosurf_set_att_map(id, isosurf_id, attr, value) else: - if attr == ATT_COLOR: - val = Nviz_color_from_str(value) - else: - val = float(value) - + val = Nviz_color_from_str(value) if attr == ATT_COLOR else float(value) ret = GVL_isosurf_set_att_const(id, isosurf_id, attr, val) Debug.msg( @@ -2352,10 +2344,7 @@ def Resize(self): def Load(self): """Load image to texture""" - if self.image.HasAlpha(): - bytesPerPixel = 4 - else: - bytesPerPixel = 3 + bytesPerPixel = 4 if self.image.HasAlpha() else 3 bytes = bytesPerPixel * self.width * self.height rev_val = self.height - 1 im = (c_ubyte * bytes)() diff --git a/gui/wxpython/photo2image/ip2i_manager.py b/gui/wxpython/photo2image/ip2i_manager.py index d0726a3bae2..0e6168a8edd 100644 --- a/gui/wxpython/photo2image/ip2i_manager.py +++ b/gui/wxpython/photo2image/ip2i_manager.py @@ -797,10 +797,7 @@ def SetGCPSatus(self, item, itemIndex): else: item.SetPropertyVal("hide", False) if self.highest_only: - if itemIndex == self.highest_key: - wxPen = "highest" - else: - wxPen = "default" + wxPen = "highest" if itemIndex == self.highest_key else "default" else: # noqa: PLR5501 if self.mapcoordlist[key][5] > self.rmsthresh: wxPen = "highest" @@ -1042,10 +1039,7 @@ def OnFocus(self, event): pass def _onMouseLeftUpPointer(self, mapWindow, x, y): - if mapWindow == self.SrcMapWindow: - coordtype = "source" - else: - coordtype = "target" + coordtype = "source" if mapWindow == self.SrcMapWindow else "target" coord = (x, y) self.SetGCPData(coordtype, coord, self, confirm=True) @@ -1102,10 +1096,7 @@ def OnGeorect(self, event): if maptype == "raster": self.grwiz.SwitchEnv("source") - if self.clip_to_region: - flags = "ac" - else: - flags = "a" + flags = "ac" if self.clip_to_region else "a" with wx.BusyInfo(_("Rectifying images, please wait..."), parent=self): wx.GetApp().Yield() diff --git a/gui/wxpython/psmap/dialogs.py b/gui/wxpython/psmap/dialogs.py index f086d65dd36..428ae248c87 100644 --- a/gui/wxpython/psmap/dialogs.py +++ b/gui/wxpython/psmap/dialogs.py @@ -1256,10 +1256,7 @@ def OnMap(self, event): if self.scaleChoice.GetSelection() == 0: self.selectedMap = self.selected - if self.rasterTypeRadio.GetValue(): - mapType = "raster" - else: - mapType = "vector" + mapType = "raster" if self.rasterTypeRadio.GetValue() else "vector" self.scale[0], self.center[0], foo = AutoAdjust( self, @@ -1308,10 +1305,7 @@ def OnScaleChoice(self, event): self.vectorTypeRadio.Show() self.drawMap.Show() self.staticBox.SetLabel(" %s " % _("Map selection")) - if self.rasterTypeRadio.GetValue(): - stype = "raster" - else: - stype = "vector" + stype = "raster" if self.rasterTypeRadio.GetValue() else "vector" self.select.SetElementList(type=stype) self.mapText.SetLabel(self.mapOrRegionText[0]) @@ -1368,10 +1362,7 @@ def OnScaleChoice(self, event): def OnElementType(self, event): """Changes data in map selection tree ctrl popup""" - if self.rasterTypeRadio.GetValue(): - mapType = "raster" - else: - mapType = "vector" + mapType = "raster" if self.rasterTypeRadio.GetValue() else "vector" self.select.SetElementList(type=mapType) if self.mapType != mapType and event is not None: self.mapType = mapType @@ -1488,10 +1479,7 @@ def update(self): ) if self.mapType == "vector": raster = self.instruction.FindInstructionByType("raster") - if raster: - rasterId = raster.id - else: - rasterId = None + rasterId = raster.id if raster else None if rasterId: self.env["GRASS_REGION"] = gs.region_env( @@ -1562,10 +1550,7 @@ def update(self): region = gs.region(env=None) raster = self.instruction.FindInstructionByType("raster") - if raster: - rasterId = raster.id - else: - rasterId = None + rasterId = raster.id if raster else None if rasterId: # because of resolution self.env["GRASS_REGION"] = gs.region_env( @@ -3748,10 +3733,7 @@ def _vectorLegend(self, notebook): def sizePositionFont(self, legendType, parent, mainSizer): """Insert widgets for size, position and font control""" - if legendType == "raster": - legendDict = self.rLegendDict - else: - legendDict = self.vLegendDict + legendDict = self.rLegendDict if legendType == "raster" else self.vLegendDict panel = parent border = mainSizer @@ -4125,10 +4107,7 @@ def OnUp(self, event): self.vectorListCtrl.SetItemData(pos, idx1) self.vectorListCtrl.SetItemData(pos - 1, idx2) self.vectorListCtrl.SortItems(cmp) - if pos > 0: - selected = pos - 1 - else: - selected = 0 + selected = pos - 1 if pos > 0 else 0 self.vectorListCtrl.Select(selected) @@ -4463,11 +4442,7 @@ def updateDialog(self): else: self.rasterId = None - if raster: - currRaster = raster["raster"] - else: - currRaster = None - + currRaster = raster["raster"] if raster else None rasterType = getRasterType(map=currRaster) self.rasterCurrent.SetLabel( _("%(rast)s: type %(type)s") % {"rast": currRaster, "type": str(rasterType)} @@ -4995,10 +4970,7 @@ def _scalebarPanel(self): globalvar.IMGDIR, "scalebar-simple.png" ) for item, path in zip(["fancy", "simple"], imagePath): - if not os.path.exists(path): - bitmap = EmptyBitmap(0, 0) - else: - bitmap = wx.Bitmap(path) + bitmap = EmptyBitmap(0, 0) if not os.path.exists(path) else wx.Bitmap(path) self.sbCombo.Append(item="", bitmap=bitmap, clientData=item[0]) # self.sbCombo.Append( # item="simple", @@ -5882,11 +5854,7 @@ def _imagePanel(self, notebook): panel.image["scale"].SetFormat("%f") panel.image["scale"].SetDigits(1) - if self.imageDict["scale"]: - value = float(self.imageDict["scale"]) - else: - value = 0 - + value = float(self.imageDict["scale"]) if self.imageDict["scale"] else 0 panel.image["scale"].SetValue(value) gridSizer.Add(scaleLabel, pos=(0, 0), flag=wx.ALIGN_CENTER_VERTICAL) @@ -6579,10 +6547,7 @@ def __init__(self, parent, id, settings, env, type="rectangle", coordinates=None :param coordinates: begin and end point coordinate (wx.Point, wx.Point) """ - if type == "rectangle": - title = _("Rectangle settings") - else: - title = _("Line settings") + title = _("Rectangle settings") if type == "rectangle" else _("Line settings") PsmapDialog.__init__( self, parent=parent, id=id, title=title, settings=settings, env=env ) diff --git a/gui/wxpython/psmap/frame.py b/gui/wxpython/psmap/frame.py index e86b277252c..8926b8d1e75 100644 --- a/gui/wxpython/psmap/frame.py +++ b/gui/wxpython/psmap/frame.py @@ -369,10 +369,7 @@ def PSFile(self, filename=None, pdf=False): temp = False regOld = gs.region(env=self.env) - if pdf: - pdfname = filename - else: - pdfname = None + pdfname = filename if pdf else None # preview or pdf if not filename or (filename and pdf): temp = True @@ -569,10 +566,7 @@ def getFile(self, wildcard): s = "." + s suffix.append(s) raster = self.instruction.FindInstructionByType("raster") - if raster: - rasterId = raster.id - else: - rasterId = None + rasterId = raster.id if raster else None if rasterId and self.instruction[rasterId]["raster"]: mapName = self.instruction[rasterId]["raster"].split("@")[0] + suffix[0] @@ -1106,10 +1100,7 @@ def getInitMap(self): scale = mapInitRect.Get()[2] / realWidth initMap = self.instruction.FindInstructionByType("initMap") - if initMap: - id = initMap.id - else: - id = None + id = initMap.id if initMap else None if not id: id = NewId() @@ -2100,10 +2091,7 @@ def OnDragging(self, event): instr = self.instruction[self.dragId] points = instr["where"] # moving point - if self.currentLinePoint == 0: - pPaper = points[1] - else: - pPaper = points[0] + pPaper = points[1] if self.currentLinePoint == 0 else points[0] pCanvas = self.CanvasPaperCoordinates( rect=Rect2DPS(pPaper, (0, 0)), canvasToPaper=False )[:2] @@ -2532,10 +2520,7 @@ def DrawBitmap(self, pdc, filePath, rotation, bbox): pdc.DrawBitmap(bitmap, bbox[0], bbox[1], useMask=True) def DrawRotText(self, pdc, drawId, textDict, coords, bounds): - if textDict["rotate"]: - rot = float(textDict["rotate"]) - else: - rot = 0 + rot = float(textDict["rotate"]) if textDict["rotate"] else 0 if textDict["background"] != "none": background = textDict["background"] @@ -2691,16 +2676,10 @@ def UpdateMapLabel(self): """Updates map frame label""" vector = self.instruction.FindInstructionByType("vector") - if vector: - vectorId = vector.id - else: - vectorId = None + vectorId = vector.id if vector else None raster = self.instruction.FindInstructionByType("raster") - if raster: - rasterId = raster.id - else: - rasterId = None + rasterId = raster.id if raster else None rasterName = "None" if rasterId: diff --git a/gui/wxpython/psmap/utils.py b/gui/wxpython/psmap/utils.py index 6bb53acd9f0..4e754fafd39 100644 --- a/gui/wxpython/psmap/utils.py +++ b/gui/wxpython/psmap/utils.py @@ -93,10 +93,7 @@ class UnitConversion: def __init__(self, parent=None): self.parent = parent - if self.parent: - ppi = wx.ClientDC(self.parent).GetPPI() - else: - ppi = (72, 72) + ppi = wx.ClientDC(self.parent).GetPPI() if self.parent else (72, 72) self._unitsPage = { "inch": {"val": 1.0, "tr": _("inch")}, "point": {"val": 72.0, "tr": _("point")}, @@ -323,10 +320,7 @@ def ComputeSetRegion(self, mapDict, env): centerN = mapDict["center"][1] raster = self.instruction.FindInstructionByType("raster") - if raster: - rasterId = raster.id - else: - rasterId = None + rasterId = raster.id if raster else None if rasterId: env["GRASS_REGION"] = gs.region_env( diff --git a/gui/wxpython/rdigit/controller.py b/gui/wxpython/rdigit/controller.py index 8018ee04e5c..9b70a2cc468 100644 --- a/gui/wxpython/rdigit/controller.py +++ b/gui/wxpython/rdigit/controller.py @@ -447,10 +447,7 @@ def _createNewMap(self, mapName, backgroundMap, mapType): name = mapName.split("@")[0] background = backgroundMap.split("@")[0] types = {"CELL": "int", "FCELL": "float", "DCELL": "double"} - if background: - back = background - else: - back = "null()" + back = background or "null()" try: grast.mapcalc( exp="{name} = {mtype}({back})".format( diff --git a/gui/wxpython/timeline/frame.py b/gui/wxpython/timeline/frame.py index fa2b16ad49f..0829d8bc3a0 100644 --- a/gui/wxpython/timeline/frame.py +++ b/gui/wxpython/timeline/frame.py @@ -272,10 +272,9 @@ def _draw3dFigure(self): self.axes3d.clear() self.axes3d.grid(False) # self.axes3d.grid(True) - if self.temporalType == "absolute": - convert = mdates.date2num - else: - convert = lambda x: x # noqa: E731 + convert = ( + mdates.date2num if self.temporalType == "absolute" else lambda x: x + ) # noqa: E731 colors = cycle(COLORS) plots = [] @@ -321,10 +320,9 @@ def _draw2dFigure(self): """Draws 2D plot (temporal extents)""" self.axes2d.clear() self.axes2d.grid(True) - if self.temporalType == "absolute": - convert = mdates.date2num - else: - convert = lambda x: x # noqa: E731 + convert = ( + mdates.date2num if self.temporalType == "absolute" else lambda x: x + ) # noqa: E731 colors = cycle(COLORS) diff --git a/gui/wxpython/tools/update_menudata.py b/gui/wxpython/tools/update_menudata.py index 2f548da3087..08897e6147d 100644 --- a/gui/wxpython/tools/update_menudata.py +++ b/gui/wxpython/tools/update_menudata.py @@ -136,10 +136,7 @@ def main(argv=None): if argv is None: argv = sys.argv - if len(argv) > 1 and argv[1] == "-d": - printDiff = True - else: - printDiff = False + printDiff = bool(len(argv) > 1 and argv[1] == "-d") if len(argv) > 1 and argv[1] == "-h": print(sys.stderr, __doc__, file=sys.stderr) diff --git a/gui/wxpython/tplot/frame.py b/gui/wxpython/tplot/frame.py index 07825058e2d..584d97fbf24 100755 --- a/gui/wxpython/tplot/frame.py +++ b/gui/wxpython/tplot/frame.py @@ -756,10 +756,7 @@ def _writeCSV(self, x, y): """Used to write CSV file of plotted data""" import csv - if isinstance(y[0], list): - zipped = list(zip(x, *y)) - else: - zipped = list(zip(x, y)) + zipped = list(zip(x, *y)) if isinstance(y[0], list) else list(zip(x, y)) with open(self.csvpath, "w", newline="") as fi: writer = csv.writer(fi) if self.header: diff --git a/gui/wxpython/vdigit/dialogs.py b/gui/wxpython/vdigit/dialogs.py index 43b277b2ca7..2e7333cf122 100644 --- a/gui/wxpython/vdigit/dialogs.py +++ b/gui/wxpython/vdigit/dialogs.py @@ -431,10 +431,7 @@ def ApplyChanges(self, fid): if layer not in catsCurr[1].keys() or cat not in catsCurr[1][layer]: catList.append(cat) if catList != []: - if action == "catadd": - add = True - else: - add = False + add = action == "catadd" newfid = self.digit.SetLineCats(fid, layer, catList, add) if len(self.cats.keys()) == 1: diff --git a/gui/wxpython/vdigit/mapwindow.py b/gui/wxpython/vdigit/mapwindow.py index ff7e62ebaa9..db9ebf57caf 100644 --- a/gui/wxpython/vdigit/mapwindow.py +++ b/gui/wxpython/vdigit/mapwindow.py @@ -834,10 +834,7 @@ def OnLeftUpVarious(self, event): self.digit.GetDisplay().SelectAreaByPoint(pos1)["area"] != -1 ) else: - if action == "moveLine": - drawSeg = True - else: - drawSeg = False + drawSeg = action == "moveLine" nselected = self.digit.GetDisplay().SelectLinesByBox( bbox=(pos1, pos2), drawSeg=drawSeg @@ -1103,10 +1100,7 @@ def _onRightUp(self, event): GError(parent=self, message=_("No vector map selected for editing.")) if mapName: - if self.toolbar.GetAction("type") == "line": - line = True - else: - line = False + line = self.toolbar.GetAction("type") == "line" if len(self.polycoords) < 2: # ignore 'one-point' lines return diff --git a/gui/wxpython/vdigit/preferences.py b/gui/wxpython/vdigit/preferences.py index e565c60dfe9..b8fd9096510 100644 --- a/gui/wxpython/vdigit/preferences.py +++ b/gui/wxpython/vdigit/preferences.py @@ -622,10 +622,7 @@ def _createAttributesPage(self, notebook): layer = UserSettings.Get(group="vdigit", key="layer", subkey="value") mapLayer = self.parent.toolbars["vdigit"].GetLayer() tree = self.parent.tree - if tree: - item = tree.FindItemByData("maplayer", mapLayer) - else: - item = None + item = tree.FindItemByData("maplayer", mapLayer) if tree else None row = 0 for attrb in ["length", "area", "perimeter"]: # checkbox @@ -664,10 +661,7 @@ def _createAttributesPage(self, notebook): column.SetStringSelection( tree.GetLayerInfo(item, key="vdigit")["geomAttr"][attrb]["column"] ) - if attrb == "area": - type = "area" - else: - type = "length" + type = "area" if attrb == "area" else "length" unitsIdx = Units.GetUnitsIndex( type, tree.GetLayerInfo(item, key="vdigit")["geomAttr"][attrb]["units"], @@ -987,10 +981,7 @@ def UpdateSettings(self): # geometry attributes (workspace) mapLayer = self.parent.toolbars["vdigit"].GetLayer() tree = self._giface.GetLayerTree() - if tree: - item = tree.FindItemByData("maplayer", mapLayer) - else: - item = None + item = tree.FindItemByData("maplayer", mapLayer) if tree else None for key, val in self.geomAttrb.items(): checked = self.FindWindowById(val["check"]).IsChecked() column = self.FindWindowById(val["column"]).GetValue() @@ -999,11 +990,8 @@ def UpdateSettings(self): tree.SetLayerInfo(item, key="vdigit", value={"geomAttr": {}}) if checked: # enable - if key == "area": - type = key - else: - type = "length" - unitsKey = Units.GetUnitsKey(type, unitsIdx) + _type = key if key == "area" else "length" + unitsKey = Units.GetUnitsKey(_type, unitsIdx) tree.GetLayerInfo(item, key="vdigit")["geomAttr"][key] = { "column": column, "units": unitsKey, diff --git a/gui/wxpython/vdigit/toolbars.py b/gui/wxpython/vdigit/toolbars.py index 6c5c8b21212..a9cafe3dfb4 100644 --- a/gui/wxpython/vdigit/toolbars.py +++ b/gui/wxpython/vdigit/toolbars.py @@ -1291,10 +1291,7 @@ def UpdateListOfLayers(self, updateTool=False): layerNameList.append(layer.GetName()) if updateTool: # update toolbar - if not self.mapLayer: - value = _("Select vector map") - else: - value = layerNameSelected + value = _("Select vector map") if not self.mapLayer else layerNameSelected if not self.comboid: if not self.tools or "selector" in self.tools: diff --git a/gui/wxpython/vdigit/wxdigit.py b/gui/wxpython/vdigit/wxdigit.py index eda912284a0..10cac8710b5 100644 --- a/gui/wxpython/vdigit/wxdigit.py +++ b/gui/wxpython/vdigit/wxdigit.py @@ -1896,10 +1896,7 @@ def _addFeature(self, ftype, coords, layer, cat, snap, threshold): modeSnap, ) - if ftype == GV_AREA: - ltype = GV_BOUNDARY - else: - ltype = ftype + ltype = GV_BOUNDARY if ftype == GV_AREA else ftype newline = Vect_write_line(self.poMapInfo, ltype, self.poPoints, self.poCats) if newline < 0: self._error.WriteLine() diff --git a/gui/wxpython/vdigit/wxdisplay.py b/gui/wxpython/vdigit/wxdisplay.py index d8e4d0a14bf..39662afb19f 100644 --- a/gui/wxpython/vdigit/wxdisplay.py +++ b/gui/wxpython/vdigit/wxdisplay.py @@ -982,15 +982,9 @@ def OpenMap(self, name, mapset, update=True, tmp=False): # open existing map if update: - if tmp: - open_fn = Vect_open_tmp_update - else: - open_fn = Vect_open_update - else: # noqa: PLR5501 - if tmp: - open_fn = Vect_open_tmp_old - else: - open_fn = Vect_open_old + open_fn = Vect_open_tmp_update if tmp else Vect_open_update + else: + open_fn = Vect_open_tmp_old if tmp else Vect_open_old ret = open_fn(self.poMapInfo, name, mapset) diff --git a/gui/wxpython/vnet/dialogs.py b/gui/wxpython/vnet/dialogs.py index ee553a0b741..396f0821c10 100644 --- a/gui/wxpython/vnet/dialogs.py +++ b/gui/wxpython/vnet/dialogs.py @@ -458,7 +458,7 @@ def _createParametersPage(self): # , 'turn_layer', 'turn_cat_layer']: for sel in ["input", "arc_layer", "node_layer"]: - if sel == "input": + if sel == "input": # noqa: SIM108 btn = self.addToTreeBtn # elif sel == "turn_layer": # btn = self.createTtbBtn @@ -843,10 +843,7 @@ def _setInputData(self): def _parseMapStr(self, vectMapStr): """Create full map name (add current mapset if it is not present in name)""" mapValSpl = vectMapStr.strip().split("@") - if len(mapValSpl) > 1: - mapSet = mapValSpl[1] - else: - mapSet = grass.gisenv()["MAPSET"] + mapSet = mapValSpl[1] if len(mapValSpl) > 1 else grass.gisenv()["MAPSET"] mapName = mapValSpl[0] return mapName, mapSet diff --git a/gui/wxpython/vnet/vnet_data.py b/gui/wxpython/vnet/vnet_data.py index f2f17b0bfb5..9971c9a9699 100644 --- a/gui/wxpython/vnet/vnet_data.py +++ b/gui/wxpython/vnet/vnet_data.py @@ -355,10 +355,8 @@ def SetPointStatus(self, item, itemIndex): item.hide = False elif len(cats) > 1: idx = self.data[itemIndex][1] - if idx == 2: # End/To/Sink point - wxPen = "used2cat" - else: - wxPen = "used1cat" + # End/To/Sink point + wxPen = "used2cat" if idx == 2 else "used1cat" else: wxPen = "used1cat" @@ -841,10 +839,7 @@ def GetRelevantParams(self, analysis): cols = self.vnetProperties[analysis]["cmdParams"]["cols"] for col, v in cols.items(): - if "inputField" in col: - colInptF = v["inputField"] - else: - colInptF = col + colInptF = v["inputField"] if "inputField" in col else col relevant_params.append(colInptF) return relevant_params @@ -900,10 +895,7 @@ def HasTmpVectMap(self, vectMapName): """ mapValSpl = vectMapName.strip().split("@") - if len(mapValSpl) > 1: - mapSet = mapValSpl[1] - else: - mapSet = grass.gisenv()["MAPSET"] + mapSet = mapValSpl[1] if len(mapValSpl) > 1 else grass.gisenv()["MAPSET"] mapName = mapValSpl[0] fullName = mapName + "@" + mapSet @@ -1324,10 +1316,7 @@ def _parseLine(self, line, histStepData): del kv[0] idx = 0 while idx < len(kv): - if subkeyMaster: - subkey = [subkeyMaster, kv[idx]] - else: - subkey = kv[idx] + subkey = [subkeyMaster, kv[idx]] if subkeyMaster else kv[idx] value = kv[idx + 1] value = self._parseValue(value, read=True) if key not in histStepData: @@ -1444,21 +1433,14 @@ def DataValidator(self, row, col, value): self.turn_data[i_row][1] = new_to_angle for i_row in inside_new: - if col == 1: - angle = new_from_angle - else: - angle = new_to_angle + angle = new_from_angle if col == 1 else new_to_angle self.turn_data[i_row][1] = angle self.turn_data[i_row][2] = angle def RemoveDataValidator(self, row): """Angle recalculation due to direction remove""" - if row == 0: - prev_row = self.GetLinesCount() - 1 - else: - prev_row = row - 1 - + prev_row = self.GetLinesCount() - 1 if row == 0 else row - 1 remove_to_angle = self.turn_data[row][2] self.turn_data[prev_row][2] = remove_to_angle diff --git a/gui/wxpython/vnet/vnet_utils.py b/gui/wxpython/vnet/vnet_utils.py index e3cc11aee17..7157ac8a20e 100644 --- a/gui/wxpython/vnet/vnet_utils.py +++ b/gui/wxpython/vnet/vnet_utils.py @@ -34,10 +34,7 @@ def ParseMapStr(mapStr): """Create full map name (add current mapset if it is not present in name)""" mapValSpl = mapStr.strip().split("@") - if len(mapValSpl) > 1: - mapSet = mapValSpl[1] - else: - mapSet = grass.gisenv()["MAPSET"] + mapSet = mapValSpl[1] if len(mapValSpl) > 1 else grass.gisenv()["MAPSET"] mapName = mapValSpl[0] return mapName, mapSet diff --git a/gui/wxpython/web_services/widgets.py b/gui/wxpython/web_services/widgets.py index 473a8fb58a2..e54bee5603f 100644 --- a/gui/wxpython/web_services/widgets.py +++ b/gui/wxpython/web_services/widgets.py @@ -384,10 +384,7 @@ def _advancedSettsPage(self): continue if k in labels or k == "o": - if k != "o": - label = labels[k] - else: - label = param + label = labels[k] if k != "o" else param gridSizer.Add( label, flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL, pos=(row, 0) @@ -454,10 +451,7 @@ def _updateLayerOrderList(self, selected=None): """Update order in list.""" def getlayercaption(layer): - if layer["title"]: - cap = layer["title"] - else: - cap = layer["name"] + cap = layer["title"] or layer["name"] if layer["style"]: if layer["style"]["title"]: diff --git a/gui/wxpython/wxgui.py b/gui/wxpython/wxgui.py index 8fac39c4d5a..fe1d6f21054 100644 --- a/gui/wxpython/wxgui.py +++ b/gui/wxpython/wxgui.py @@ -139,10 +139,7 @@ def process_opt(opts, args): printHelp() elif o in {"-w", "--workspace"}: - if a != "": - workspaceFile = str(a) - else: - workspaceFile = args.pop(0) + workspaceFile = str(a) if a != "" else args.pop(0) return workspaceFile diff --git a/gui/wxpython/wxplot/scatter.py b/gui/wxpython/wxplot/scatter.py index c57a3e1fac9..d87a03208e4 100644 --- a/gui/wxpython/wxplot/scatter.py +++ b/gui/wxpython/wxplot/scatter.py @@ -176,11 +176,7 @@ def CreateDatalist(self, rpair): frequency can be in cell counts, percents, or area """ datalist = [] - - if self.scattertype == "bubble": - freqflag = "cn" - else: - freqflag = "n" + freqflag = "cn" if self.scattertype == "bubble" else "n" try: ret = RunCommand( diff --git a/pyproject.toml b/pyproject.toml index a428c79106f..3c5950f922b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -241,7 +241,6 @@ ignore = [ "S608", # hardcoded-sql-expression "SIM102", # collapsible-if "SIM105", # suppressible-exception - "SIM108", # if-else-block-instead-of-if-exp "SIM113", # enumerate-for-loop "SIM116", # if-else-block-instead-of-dict-lookup "SIM118", # in-dict-keys @@ -365,7 +364,7 @@ ignore = [ "temporal/t.rast.algebra/testsu*/*_algebra_arithmetic.py" = ["FLY002"] "temporal/t.rast.colors/t.rast.colors.py" = ["SIM115"] "temporal/t.rast.series/t.rast.series.py" = ["SIM115"] -"temporal/t.rast.what/t.rast.what.py" = ["E265", "E266", "SIM115"] +"temporal/t.rast.what/t.rast.what.py" = ["SIM115"] "temporal/t.register/testsu*/*_raster_different_local.py" = ["FLY002"] "temporal/t.register/testsu*/*_raster_mapmetadata.py" = ["FLY002"] "temporal/t.register/testsuite/test_t_register_raster.py" = ["FLY002"] diff --git a/python/grass/script/core.py b/python/grass/script/core.py index 0b51e7fdbe9..f51814a5900 100644 --- a/python/grass/script/core.py +++ b/python/grass/script/core.py @@ -18,6 +18,8 @@ .. sectionauthor:: Michael Barton """ +from __future__ import annotations + import os import sys import atexit @@ -855,9 +857,9 @@ def get_capture_stderr(): # interface to g.parser -def _parse_opts(lines): - options = {} - flags = {} +def _parse_opts(lines: list) -> tuple[dict[str, str], dict[str, bool]]: + options: dict[str, str] = {} + flags: dict[str, bool] = {} for line in lines: if not line: break @@ -887,7 +889,7 @@ def _parse_opts(lines): return (options, flags) -def parser(): +def parser() -> tuple[dict[str, str], dict[str, bool]]: """Interface to g.parser, intended to be run from the top-level, e.g.: :: diff --git a/python/grass/temporal/abstract_dataset.py b/python/grass/temporal/abstract_dataset.py index bf30afa21dd..6df2fa1f81e 100644 --- a/python/grass/temporal/abstract_dataset.py +++ b/python/grass/temporal/abstract_dataset.py @@ -10,6 +10,8 @@ :authors: Soeren Gebbert """ +from __future__ import annotations + from abc import ABCMeta, abstractmethod from .core import get_current_mapset, get_tgis_message_interface, init_dbif @@ -504,7 +506,7 @@ def update_all(self, dbif=None, execute=True, ident=None): dbif.close() return statement - def is_time_absolute(self): + def is_time_absolute(self) -> bool | None: """Return True in case the temporal type is absolute :return: True if temporal type is absolute, False otherwise @@ -513,7 +515,7 @@ def is_time_absolute(self): return self.base.get_ttype() == "absolute" return None - def is_time_relative(self): + def is_time_relative(self) -> bool | None: """Return True in case the temporal type is relative :return: True if temporal type is relative, False otherwise From ac491770cb2aa2b349333c9ecb6c1ab9d650278d Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Fri, 25 Oct 2024 23:09:43 -0400 Subject: [PATCH 446/471] wxGUI: Reverted unused variable fix in wxgui/ (#4595) Seems to break 3D view --- gui/wxpython/wxgui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gui/wxpython/wxgui.py b/gui/wxpython/wxgui.py index fe1d6f21054..83fed965ecd 100644 --- a/gui/wxpython/wxgui.py +++ b/gui/wxpython/wxgui.py @@ -161,7 +161,7 @@ def main(argv=None): app = GMApp(workspaceFile) # suppress wxPython logs - wx.LogNull() + q = wx.LogNull() # noqa: F841 set_raise_on_error(True) # register GUI PID From 652a2f8c895321d5ae3470603025e822f58315ec Mon Sep 17 00:00:00 2001 From: Anna Petrasova Date: Fri, 25 Oct 2024 23:10:19 -0400 Subject: [PATCH 447/471] wxGUI/nviz: fix imports (#4592) --- gui/wxpython/nviz/wxnviz.py | 88 ++++++++++++++++++++++--------------- 1 file changed, 53 insertions(+), 35 deletions(-) diff --git a/gui/wxpython/nviz/wxnviz.py b/gui/wxpython/nviz/wxnviz.py index 35995d30e6b..32c2541f14e 100644 --- a/gui/wxpython/nviz/wxnviz.py +++ b/gui/wxpython/nviz/wxnviz.py @@ -44,7 +44,6 @@ try: from ctypes import ( CFUNCTYPE, - UNCHECKED, byref, c_char_p, c_double, @@ -58,7 +57,9 @@ print("wxnviz.py: {}".format(e), file=sys.stderr) try: + from grass.lib.ctypes_preamble import UNCHECKED, String from grass.lib.gis import ( + Colors, G_find_raster2, G_find_raster3d, G_find_vector2, @@ -73,20 +74,15 @@ G_warning, ) from grass.lib.nviz import ( - ATT_COLOR, - ATT_EMIT, - ATT_MASK, - ATT_SHINE, - ATT_TOPO, - ATT_TRANSP, - CONST_ATT, - MAP_ATT, + DRAW_QUICK_SURFACE, + DRAW_QUICK_VLINES, + DRAW_QUICK_VOLUME, + DRAW_QUICK_VPOINTS, MAP_OBJ_SITE, MAP_OBJ_SURF, MAP_OBJ_UNDEFINED, MAP_OBJ_VECT, MAP_OBJ_VOL, - Colors, Nviz_change_exag, Nviz_color_from_str, Nviz_del_texture, @@ -153,6 +149,27 @@ nv_data, ) from grass.lib.ogsf import ( + ATT_COLOR, + ATT_EMIT, + ATT_MASK, + ATT_SHINE, + ATT_TOPO, + ATT_TRANSP, + CONST_ATT, + DM_FLAT, + DM_GOURAUD, + MAP_ATT, + MAX_ISOSURFS, + GP_delete_site, + GP_get_sitename, + GP_select_surf, + GP_set_style, + GP_set_style_thematic, + GP_set_trans, + GP_set_zmode, + GP_site_exists, + GP_unselect_surf, + GP_unset_style_thematic, GS_clear, GS_delete_surface, GS_get_cat_at_xy, @@ -177,6 +194,16 @@ GS_surf_exists, GS_write_ppm, GS_write_tif, + GV_delete_vector, + GV_get_vectname, + GV_select_surf, + GV_set_style, + GV_set_style_thematic, + GV_set_trans, + GV_surf_is_selected, + GV_unselect_surf, + GV_unset_style_thematic, + GV_vect_exists, GVL_delete_vol, GVL_get_trans, GVL_init_region, @@ -205,35 +232,13 @@ GVL_slice_set_transp, GVL_vol_exists, ) - from grass.lib.raster import Rast__init_window, Rast_unset_window, Vect_read_colors - from grass.lib.raster3d import ( - GP_delete_site, - GP_get_sitename, - GP_select_surf, - GP_set_style, - GP_set_style_thematic, - GP_set_trans, - GP_set_zmode, - GP_site_exists, - GP_unselect_surf, - GP_unset_style_thematic, - ) - from grass.lib.vector import ( - GV_delete_vector, - GV_get_vectname, - GV_select_surf, - GV_set_style, - GV_set_style_thematic, - GV_set_trans, - GV_surf_is_selected, - GV_unselect_surf, - GV_unset_style_thematic, - GV_vect_exists, - ) + from grass.lib.raster import Rast__init_window, Rast_unset_window + from grass.lib.vector import Vect_read_colors except (ImportError, OSError, TypeError) as e: print("wxnviz.py: {}".format(e), file=sys.stderr) import grass.script as gs + from core.debug import Debug from core.gcmd import DecodeString from core.globalvar import wxPythonPhoenix @@ -2432,3 +2437,16 @@ def GetCmd(self): def Corresponds(self, item): return sorted(self.GetCmd()) == sorted(item.GetCmd()) + + +__all__ = [ + "DM_FLAT", + "DM_GOURAUD", + "DRAW_QUICK_SURFACE", + "DRAW_QUICK_VLINES", + "DRAW_QUICK_VOLUME", + "DRAW_QUICK_VPOINTS", + "MAX_ISOSURFS", + "ImageTexture", + "Nviz", +] From 5af22b2d4eae997953bc62cd865ab0d09883459b Mon Sep 17 00:00:00 2001 From: Vaclav Petras Date: Fri, 25 Oct 2024 23:41:06 -0400 Subject: [PATCH 448/471] lib/iostream: Remove redundant template parameters (#4586) In C++20, repeating template parameter names with the name of the class is no longer allowed as it is redundant (https://cplusplus.github.io/CWG/issues/2237.html). The new code is valid also in C++17, but the old code is not accepted in C++20. --- include/grass/iostream/replacementHeap.h | 6 +++--- include/grass/iostream/replacementHeapBlock.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/grass/iostream/replacementHeap.h b/include/grass/iostream/replacementHeap.h index 485d4cef99b..30c309cdda2 100644 --- a/include/grass/iostream/replacementHeap.h +++ b/include/grass/iostream/replacementHeap.h @@ -101,10 +101,10 @@ class ReplacementHeap { public: // allocate array mergeHeap and the runs in runList - ReplacementHeap(size_t arity, queue *runList); + ReplacementHeap(size_t arity, queue *runList); // delete array mergeHeap - ~ReplacementHeap(); + ~ReplacementHeap(); // is heap empty? int empty() const { return (size == 0); } @@ -159,7 +159,7 @@ ReplacementHeap::ReplacementHeap(size_t g_arity, /*****************************************************************/ template -ReplacementHeap::~ReplacementHeap() +ReplacementHeap::~ReplacementHeap() { if (!empty()) { diff --git a/include/grass/iostream/replacementHeapBlock.h b/include/grass/iostream/replacementHeapBlock.h index c1596d67585..f4614eb838b 100644 --- a/include/grass/iostream/replacementHeapBlock.h +++ b/include/grass/iostream/replacementHeapBlock.h @@ -102,10 +102,10 @@ class ReplacementHeapBlock { public: // allocate array mergeHeap, where the streams are stored in runList - ReplacementHeapBlock(queue *> *runList); + ReplacementHeapBlock(queue *> *runList); // delete array mergeHeap - ~ReplacementHeapBlock(); + ~ReplacementHeapBlock(); // is heap empty? int empty() const { return (size == 0); } @@ -161,7 +161,7 @@ ReplacementHeapBlock::ReplacementHeapBlock( /*****************************************************************/ template -ReplacementHeapBlock::~ReplacementHeapBlock() +ReplacementHeapBlock::~ReplacementHeapBlock() { if (!empty()) { From 4616b9d4a276ac1f3f4024e14f155a55557e80a7 Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Sat, 26 Oct 2024 16:06:20 -0400 Subject: [PATCH 449/471] lib/vector/Vlib: Fix Resource Leak issues in copy.c (#4533) --- lib/vector/Vlib/copy.c | 91 ++++++++++++++++++++++++++++-------------- 1 file changed, 61 insertions(+), 30 deletions(-) diff --git a/lib/vector/Vlib/copy.c b/lib/vector/Vlib/copy.c index bde7e577c8b..779b5ed4a0f 100644 --- a/lib/vector/Vlib/copy.c +++ b/lib/vector/Vlib/copy.c @@ -82,6 +82,8 @@ int Vect_copy_map_lines_field(struct Map_info *In, int field, struct Map_info *Out) { int ret, format, topo; + const char *geometry_type = NULL; + const char *map_name = NULL; if (Vect_level(In) < 1) G_fatal_error( @@ -127,24 +129,30 @@ int Vect_copy_map_lines_field(struct Map_info *In, int field, /* copy features */ ret += copy_lines_2(In, field, topo, Out); - if (topo == TOPO_NONE && + if (topo == TOPO_NONE) { /* check output feature type, centroids can be exported as * points; boundaries as linestrings */ - strcmp(Vect_get_finfo_geometry_type(Out), "polygon") == 0) { - /* copy areas - external formats and simple features access only */ - ret += Vect__copy_areas(In, field, Out); + geometry_type = Vect_get_finfo_geometry_type(Out); + if (geometry_type && strcmp(geometry_type, "polygon") == 0) { + /* copy areas - external formats and simple features access only + */ + ret += Vect__copy_areas(In, field, Out); + } + G_free((void *)geometry_type); } } else { /* -> copy features on level 1 */ - if (topo == TOPO_NONE) + if (topo == TOPO_NONE) { + map_name = Vect_get_full_name(In); G_warning(_("Vector map <%s> not open on topological level. " "Areas will be skipped!"), - Vect_get_full_name(In)); + map_name); + G_free((void *)map_name); + } ret += copy_lines_1(In, field, Out); } - return ret > 0 ? 1 : 0; } @@ -161,6 +169,7 @@ int Vect_copy_map_lines_field(struct Map_info *In, int field, int copy_lines_1(struct Map_info *In, int field, struct Map_info *Out) { int ret, type; + const char *map_name = NULL; struct line_pnts *Points; struct line_cats *Cats; @@ -174,8 +183,9 @@ int copy_lines_1(struct Map_info *In, int field, struct Map_info *Out) while (TRUE) { type = Vect_read_next_line(In, Points, Cats); if (type == -1) { - G_warning(_("Unable to read vector map <%s>"), - Vect_get_full_name(In)); + map_name = Vect_get_full_name(In); + G_warning(_("Unable to read vector map <%s>"), map_name); + G_free((void *)map_name); ret = 1; break; } @@ -193,7 +203,6 @@ int copy_lines_1(struct Map_info *In, int field, struct Map_info *Out) Vect_write_line(Out, type, Points, Cats); } - Vect_destroy_line_struct(Points); Vect_destroy_cats_struct(Cats); @@ -220,6 +229,7 @@ int copy_lines_2(struct Map_info *In, int field, int topo, struct Map_info *Out) struct line_cats *Cats, *CCats; const char *ftype = NULL; + const char *map_name = NULL; Points = Vect_new_line_struct(); CPoints = Vect_new_line_struct(); @@ -251,8 +261,9 @@ int copy_lines_2(struct Map_info *In, int field, int topo, struct Map_info *Out) G_percent(i, nlines, 2); type = Vect_read_line(In, Points, Cats, i); if (type == -1) { - G_warning(_("Unable to read vector map <%s>"), - Vect_get_full_name(In)); + map_name = Vect_get_full_name(In); + G_warning(_("Unable to read vector map <%s>"), map_name); + G_free((void *)map_name); ret = 1; break; /* free allocated space and return */ } @@ -364,7 +375,8 @@ int copy_lines_2(struct Map_info *In, int field, int topo, struct Map_info *Out) if (-1 == Vect_write_line(Out, type, Points, Cats)) { G_warning(_("Writing new feature failed")); - return 1; + ret = 1; + goto free_exit; } } @@ -372,12 +384,13 @@ int copy_lines_2(struct Map_info *In, int field, int topo, struct Map_info *Out) G_important_message( _("%d features without category or from different layer skipped"), nskipped); - +free_exit: Vect_destroy_line_struct(Points); Vect_destroy_line_struct(CPoints); Vect_destroy_line_struct(NPoints); Vect_destroy_cats_struct(Cats); Vect_destroy_cats_struct(CCats); + G_free((void *)ftype); return ret; } @@ -496,6 +509,7 @@ int is_isle(struct Map_info *Map, int area) int Vect__copy_areas(struct Map_info *In, int field, struct Map_info *Out) { int i, area, nareas, cat, isle, nisles, nparts_alloc, nskipped; + int ret = 0; struct line_pnts **Points; struct line_cats *Cats; @@ -567,7 +581,8 @@ int Vect__copy_areas(struct Map_info *In, int field, struct Map_info *Out) if (0 > V2__write_area_sfa(Out, (const struct line_pnts **)Points, nisles + 1, Cats)) { G_warning(_("Writing area %d failed"), area); - return -1; + ret = -1; + goto free_exit; } } #ifdef HAVE_POSTGRES @@ -575,7 +590,8 @@ int Vect__copy_areas(struct Map_info *In, int field, struct Map_info *Out) if (0 > V2__update_area_pg(Out, (const struct line_pnts **)Points, nisles + 1, cat)) { G_warning(_("Writing area %d failed"), area); - return -1; + ret = -1; + goto free_exit; } } #endif @@ -587,11 +603,13 @@ int Vect__copy_areas(struct Map_info *In, int field, struct Map_info *Out) nskipped); /* free allocated space for isles */ +free_exit: for (i = 0; i < nparts_alloc; i++) Vect_destroy_line_struct(Points[i]); Vect_destroy_cats_struct(Cats); + G_free(Points); - return 0; + return ret; } /*! @@ -615,6 +633,7 @@ int Vect_copy_tables(struct Map_info *In, struct Map_info *Out, int field) { int i, n, type; struct field_info *Fi; + const char *map_name = NULL; n = Vect_get_num_dblinks(In); @@ -631,20 +650,23 @@ int Vect_copy_tables(struct Map_info *In, struct Map_info *Out, int field) In->dblnk->field[i].number); return -1; } - if (field > 0 && Fi->number != field) + if (field > 0 && Fi->number != field) { + Vect_destroy_field_info(Fi); continue; + } if (Vect_copy_table(In, Out, Fi->number, Fi->number, Fi->name, type) != 0) { - + map_name = Vect_get_full_name(In); G_warning( _("Unable to copy table <%s> for layer %d from <%s> to <%s>"), - Fi->table, Fi->number, Vect_get_full_name(In), - Vect_get_name(Out)); + Fi->table, Fi->number, map_name, Vect_get_name(Out)); + G_free((void *)map_name); + Vect_destroy_field_info(Fi); return -1; } + Vect_destroy_field_info(Fi); } - return 0; } @@ -729,10 +751,10 @@ int Vect_copy_table_by_cats(struct Map_info *In, struct Map_info *Out, int field_in, int field_out, const char *field_name, int type, int *cats, int ncats) { - int ret; + int ret = 0; struct field_info *Fi, *Fin; const char *name, *key; - dbDriver *driver; + dbDriver *driver = NULL; G_debug(2, "Vect_copy_table_by_cats(): field_in = %d field_out = %d", field_in, field_out); @@ -757,7 +779,7 @@ int Vect_copy_table_by_cats(struct Map_info *In, struct Map_info *Out, if (ret == -1) { G_warning(_("Unable to add database link for vector map <%s>"), Out->name); - return -1; + goto free_exit; } if (cats) @@ -770,7 +792,8 @@ int Vect_copy_table_by_cats(struct Map_info *In, struct Map_info *Out, Fin->table, key, cats, ncats); if (ret == DB_FAILED) { G_warning(_("Unable to copy table <%s>"), Fin->table); - return -1; + ret = -1; + goto free_exit; } driver = db_start_driver_open_database(Fin->driver, @@ -779,22 +802,30 @@ int Vect_copy_table_by_cats(struct Map_info *In, struct Map_info *Out, if (!driver) { G_warning(_("Unable to open database <%s> with driver <%s>"), Fin->database, Fin->driver); - return -1; + ret = -1; + goto free_exit; } /* do not allow duplicate keys */ if (db_create_index2(driver, Fin->table, Fi->key) != DB_OK) { G_warning(_("Unable to create index")); - return -1; + ret = -1; + goto close_db_free_exit; } if (db_grant_on_table(driver, Fin->table, DB_PRIV_SELECT, DB_GROUP | DB_PUBLIC) != DB_OK) { G_warning(_("Unable to grant privileges on table <%s>"), Fin->table); - return -1; + ret = -1; + goto close_db_free_exit; } +close_db_free_exit: db_close_database_shutdown_driver(driver); - return 0; +free_exit: + Vect_destroy_field_info(Fi); + Vect_destroy_field_info(Fin); + + return ret; } From 791077766d2d93d4feea51b0973db73d3cf328d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Sun, 27 Oct 2024 05:24:18 -0400 Subject: [PATCH 450/471] style: Fix superfluous-else rules for raise, continue and break (RET506, RET507, RET508) (#4565) --- doc/python/m.distance.py | 3 +- gui/wxpython/animation/temporal_manager.py | 7 +- gui/wxpython/core/gcmd.py | 9 +- gui/wxpython/core/render.py | 16 +- gui/wxpython/dbmgr/base.py | 4 +- gui/wxpython/dbmgr/dialogs.py | 17 +- gui/wxpython/gui_core/widgets.py | 2 +- gui/wxpython/iclass/dialogs.py | 5 +- gui/wxpython/iscatt/frame.py | 5 +- gui/wxpython/location_wizard/wizard.py | 3 +- gui/wxpython/mapdisp/statusbar.py | 197 +++++++------ gui/wxpython/nviz/tools.py | 7 +- gui/wxpython/psmap/instructions.py | 4 +- gui/wxpython/timeline/frame.py | 2 +- gui/wxpython/tplot/frame.py | 2 +- gui/wxpython/vnet/dialogs.py | 15 +- gui/wxpython/vnet/vnet_data.py | 9 +- man/build_class_graphical.py | 2 +- pyproject.toml | 3 - python/grass/imaging/images2avi.py | 31 +-- python/grass/pygrass/gis/__init__.py | 2 +- python/grass/pygrass/messages/__init__.py | 3 +- .../pygrass/modules/interface/parameter.py | 10 +- python/grass/pygrass/raster/category.py | 2 +- python/grass/pygrass/vector/table.py | 3 +- python/grass/script/task.py | 3 +- python/grass/temporal/temporal_algebra.py | 82 +++--- python/grass/temporal/temporal_operator.py | 260 +++++++++--------- .../temporal/temporal_raster_base_algebra.py | 13 +- .../grass/temporal/temporal_vector_algebra.py | 2 +- scripts/db.in.ogr/db.in.ogr.py | 3 +- scripts/r.in.wms/wms_cap_parsers.py | 3 +- scripts/r.in.wms/wms_drv.py | 3 +- scripts/v.what.strds/v.what.strds.py | 3 +- .../t.vect.observe.strds.py | 3 +- 35 files changed, 352 insertions(+), 386 deletions(-) diff --git a/doc/python/m.distance.py b/doc/python/m.distance.py index 576760c3b40..2383cbfb669 100755 --- a/doc/python/m.distance.py +++ b/doc/python/m.distance.py @@ -82,8 +82,7 @@ def main(): line = sys.stdin.readline().strip() if not line: # EOF break - else: - coords.append(line.split(",")) + coords.append(line.split(",")) else: # read from coord= command line option p = None diff --git a/gui/wxpython/animation/temporal_manager.py b/gui/wxpython/animation/temporal_manager.py index 1b5037639cf..0f00cc6e329 100644 --- a/gui/wxpython/animation/temporal_manager.py +++ b/gui/wxpython/animation/temporal_manager.py @@ -297,10 +297,9 @@ def _getLabelsAndMaps(self, timeseries): # skip this one, already there followsPoint = False continue - else: - # append the last one (of point time) - listOfMaps.append(lastTimeseries) - end = None + # append the last one (of point time) + listOfMaps.append(lastTimeseries) + end = None else: # append series which is None listOfMaps.append(series) diff --git a/gui/wxpython/core/gcmd.py b/gui/wxpython/core/gcmd.py index 134ed756d23..dfd719b2ff0 100644 --- a/gui/wxpython/core/gcmd.py +++ b/gui/wxpython/core/gcmd.py @@ -311,9 +311,8 @@ def recv_some(p, t=0.1, e=1, tr=5, stderr=0): if r is None: if e: raise Exception(message) - else: - break - elif r: + break + if r: y.append(decode(r)) else: time.sleep(max((x - time.time()) / tr, 0)) @@ -415,7 +414,7 @@ def __init__( _("Error: ") + self.__GetError(), ) ) - elif rerr == sys.stderr: # redirect message to sys + if rerr == sys.stderr: # redirect message to sys stderr.write("Execution failed: '%s'" % (" ".join(self.cmd))) stderr.write( "%sDetails:%s%s" @@ -644,7 +643,7 @@ def _formatMsg(text): for line in text.splitlines(): if len(line) == 0: continue - elif ( + if ( "GRASS_INFO_MESSAGE" in line or "GRASS_INFO_WARNING" in line or "GRASS_INFO_ERROR" in line diff --git a/gui/wxpython/core/render.py b/gui/wxpython/core/render.py index 989287cd4a5..b5fdf34d7dd 100644 --- a/gui/wxpython/core/render.py +++ b/gui/wxpython/core/render.py @@ -1184,32 +1184,32 @@ def SetRegion(self, windres=False, windres3=False): if key == "north": grass_region += "north: %s; " % (region["n"]) continue - elif key == "south": + if key == "south": grass_region += "south: %s; " % (region["s"]) continue - elif key == "east": + if key == "east": grass_region += "east: %s; " % (region["e"]) continue - elif key == "west": + if key == "west": grass_region += "west: %s; " % (region["w"]) continue - elif key == "e-w resol": + if key == "e-w resol": grass_region += "e-w resol: %.10f; " % (region["ewres"]) continue - elif key == "n-s resol": + if key == "n-s resol": grass_region += "n-s resol: %.10f; " % (region["nsres"]) continue - elif key == "cols": + if key == "cols": if windres: continue grass_region += "cols: %d; " % region["cols"] continue - elif key == "rows": + if key == "rows": if windres: continue grass_region += "rows: %d; " % region["rows"] continue - elif key == "n-s resol3" and windres3: + if key == "n-s resol3" and windres3: grass_region += "n-s resol3: %f; " % (region["nsres3"]) elif key == "e-w resol3" and windres3: grass_region += "e-w resol3: %f; " % (region["ewres3"]) diff --git a/gui/wxpython/dbmgr/base.py b/gui/wxpython/dbmgr/base.py index c5eb8877b0a..aeb1a2b3090 100644 --- a/gui/wxpython/dbmgr/base.py +++ b/gui/wxpython/dbmgr/base.py @@ -1659,8 +1659,8 @@ def OnDataItemAdd(self, event): raise ValueError( _("Category number (column %s) is missing.") % keyColumn ) - else: - continue + + continue try: if tlist.columns[columnName[i]]["ctype"] == int: diff --git a/gui/wxpython/dbmgr/dialogs.py b/gui/wxpython/dbmgr/dialogs.py index 8ec2b12f36b..c94e2a71e40 100644 --- a/gui/wxpython/dbmgr/dialogs.py +++ b/gui/wxpython/dbmgr/dialogs.py @@ -648,15 +648,14 @@ def __init__( self.boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL) cId += 1 continue - else: - valueWin = SpinCtrl( - parent=self.dataPanel, - id=wx.ID_ANY, - value=value, - min=-1e9, - max=1e9, - size=(250, -1), - ) + valueWin = SpinCtrl( + parent=self.dataPanel, + id=wx.ID_ANY, + value=value, + min=-1e9, + max=1e9, + size=(250, -1), + ) else: valueWin = TextCtrl( parent=self.dataPanel, id=wx.ID_ANY, value=value, size=(250, -1) diff --git a/gui/wxpython/gui_core/widgets.py b/gui/wxpython/gui_core/widgets.py index 87a09a590d8..b787bf23037 100644 --- a/gui/wxpython/gui_core/widgets.py +++ b/gui/wxpython/gui_core/widgets.py @@ -1603,7 +1603,7 @@ def _loadSettings_v2(self, fd_lines): idx = line.find(";", i_last) if idx < 0: break - elif idx != 0: + if idx != 0: # find out whether it is separator # $$$$; - it is separator # $$$$$; - it is not separator diff --git a/gui/wxpython/iclass/dialogs.py b/gui/wxpython/iclass/dialogs.py index 615e614689f..c868c6e08cf 100644 --- a/gui/wxpython/iclass/dialogs.py +++ b/gui/wxpython/iclass/dialogs.py @@ -486,9 +486,8 @@ def GetSelectedIndices(self, state=wx.LIST_STATE_SELECTED): index = self.GetNextItem(lastFound, wx.LIST_NEXT_ALL, state) if index == -1: break - else: - lastFound = index - indices.append(index) + lastFound = index + indices.append(index) return indices def OnEdit(self, event): diff --git a/gui/wxpython/iscatt/frame.py b/gui/wxpython/iscatt/frame.py index 5350ecbd8a0..3addbad2f06 100644 --- a/gui/wxpython/iscatt/frame.py +++ b/gui/wxpython/iscatt/frame.py @@ -501,9 +501,8 @@ def GetSelectedIndices(self, state=wx.LIST_STATE_SELECTED): index = self.GetNextItem(lastFound, wx.LIST_NEXT_ALL, state) if index == -1: break - else: - lastFound = index - indices.append(index) + lastFound = index + indices.append(index) return indices def DeselectAll(self): diff --git a/gui/wxpython/location_wizard/wizard.py b/gui/wxpython/location_wizard/wizard.py index d553b4e11d9..9f3bf14c886 100644 --- a/gui/wxpython/location_wizard/wizard.py +++ b/gui/wxpython/location_wizard/wizard.py @@ -900,8 +900,7 @@ def OnPageChange(self, event=None): if param["type"] == "bool": if param["value"] is False: continue - else: - self.p4projparams += " +" + param["proj4"] + self.p4projparams += " +" + param["proj4"] elif param["value"] is None: wx.MessageBox( parent=self, diff --git a/gui/wxpython/mapdisp/statusbar.py b/gui/wxpython/mapdisp/statusbar.py index 4fea1ffcc81..8cf5c3b6ff7 100644 --- a/gui/wxpython/mapdisp/statusbar.py +++ b/gui/wxpython/mapdisp/statusbar.py @@ -542,23 +542,22 @@ def ReprojectENToMap(self, e, n, useDefinedProjection): ) if not settings: raise SbException(_("Projection not defined (check the settings)")) + # reproject values + projIn = settings + projOut = RunCommand("g.proj", flags="jf", read=True) + proj = projIn.split(" ")[0].split("=")[1] + if proj in {"ll", "latlong", "longlat"}: + e, n = utils.DMS2Deg(e, n) + proj, coord1 = utils.ReprojectCoordinates( + coord=(e, n), projIn=projIn, projOut=projOut, flags="d" + ) + e, n = coord1 else: - # reproject values - projIn = settings - projOut = RunCommand("g.proj", flags="jf", read=True) - proj = projIn.split(" ")[0].split("=")[1] - if proj in {"ll", "latlong", "longlat"}: - e, n = utils.DMS2Deg(e, n) - proj, coord1 = utils.ReprojectCoordinates( - coord=(e, n), projIn=projIn, projOut=projOut, flags="d" - ) - e, n = coord1 - else: - e, n = float(e), float(n) - proj, coord1 = utils.ReprojectCoordinates( - coord=(e, n), projIn=projIn, projOut=projOut, flags="d" - ) - e, n = coord1 + e, n = float(e), float(n) + proj, coord1 = utils.ReprojectCoordinates( + coord=(e, n), projIn=projIn, projOut=projOut, flags="d" + ) + e, n = coord1 elif self.mapFrame.GetMap().projinfo["proj"] == "ll": e, n = utils.DMS2Deg(e, n) else: @@ -620,32 +619,28 @@ def GetCenterString(self, map): if self.mapFrame.GetProperty("useDefinedProjection"): if not projection: raise SbException(_("Projection not defined (check the settings)")) - else: - proj, coord = utils.ReprojectCoordinates( - coord=(region["center_easting"], region["center_northing"]), - projOut=projection, - flags="d", - ) - if coord: - if proj in {"ll", "latlong", "longlat"} and format == "DMS": - return "%s" % utils.Deg2DMS( - coord[0], coord[1], precision=precision - ) - return "%.*f; %.*f" % (precision, coord[0], precision, coord[1]) - raise SbException(_("Error in projection (check the settings)")) - elif self.mapFrame.GetMap().projinfo["proj"] == "ll" and format == "DMS": + proj, coord = utils.ReprojectCoordinates( + coord=(region["center_easting"], region["center_northing"]), + projOut=projection, + flags="d", + ) + if coord: + if proj in {"ll", "latlong", "longlat"} and format == "DMS": + return "%s" % utils.Deg2DMS(coord[0], coord[1], precision=precision) + return "%.*f; %.*f" % (precision, coord[0], precision, coord[1]) + raise SbException(_("Error in projection (check the settings)")) + if self.mapFrame.GetMap().projinfo["proj"] == "ll" and format == "DMS": return "%s" % utils.Deg2DMS( region["center_easting"], region["center_northing"], precision=precision, ) - else: - return "%.*f; %.*f" % ( - precision, - region["center_easting"], - precision, - region["center_northing"], - ) + return "%.*f; %.*f" % ( + precision, + region["center_easting"], + precision, + region["center_northing"], + ) def SetCenter(self): """Set current map center as item value""" @@ -795,21 +790,19 @@ def ReprojectENFromMap(self, e, n, useDefinedProjection, precision, format): ) if not settings: raise SbException(_("Projection not defined (check the settings)")) - else: - # reproject values - proj, coord = utils.ReprojectCoordinates( - coord=(e, n), projOut=settings, flags="d" - ) - if coord: - e, n = coord - if proj in {"ll", "latlong", "longlat"} and format == "DMS": - return utils.Deg2DMS(e, n, precision=precision) - return "%.*f; %.*f" % (precision, e, precision, n) - raise SbException(_("Error in projection (check the settings)")) - elif self.mapFrame.GetMap().projinfo["proj"] == "ll" and format == "DMS": + # reproject values + proj, coord = utils.ReprojectCoordinates( + coord=(e, n), projOut=settings, flags="d" + ) + if coord: + e, n = coord + if proj in {"ll", "latlong", "longlat"} and format == "DMS": + return utils.Deg2DMS(e, n, precision=precision) + return "%.*f; %.*f" % (precision, e, precision, n) + raise SbException(_("Error in projection (check the settings)")) + if self.mapFrame.GetMap().projinfo["proj"] == "ll" and format == "DMS": return utils.Deg2DMS(e, n, precision=precision) - else: - return "%.*f; %.*f" % (precision, e, precision, n) + return "%.*f; %.*f" % (precision, e, precision, n) class SbRegionExtent(SbTextItem): @@ -872,54 +865,53 @@ def ReprojectRegionFromMap(self, region, useDefinedProjection, precision, format if not settings: raise SbException(_("Projection not defined (check the settings)")) - else: - projOut = settings - proj, coord1 = utils.ReprojectCoordinates( - coord=(region["w"], region["s"]), projOut=projOut, flags="d" - ) - proj, coord2 = utils.ReprojectCoordinates( - coord=(region["e"], region["n"]), projOut=projOut, flags="d" - ) - # useless, used in derived class - proj, coord3 = utils.ReprojectCoordinates( - coord=(0.0, 0.0), projOut=projOut, flags="d" - ) - proj, coord4 = utils.ReprojectCoordinates( - coord=(region["ewres"], region["nsres"]), projOut=projOut, flags="d" - ) - if coord1 and coord2: - if proj in {"ll", "latlong", "longlat"} and format == "DMS": - w, s = utils.Deg2DMS( - coord1[0], coord1[1], string=False, precision=precision - ) - e, n = utils.Deg2DMS( - coord2[0], coord2[1], string=False, precision=precision - ) - ewres, nsres = utils.Deg2DMS( - abs(coord3[0]) - abs(coord4[0]), - abs(coord3[1]) - abs(coord4[1]), - string=False, - hemisphere=False, - precision=precision, - ) - return self._formatRegion( - w=w, s=s, e=e, n=n, ewres=ewres, nsres=nsres - ) - w, s = coord1 - e, n = coord2 - ewres, nsres = coord3 - return self._formatRegion( - w=w, - s=s, - e=e, - n=n, - ewres=ewres, - nsres=nsres, + projOut = settings + proj, coord1 = utils.ReprojectCoordinates( + coord=(region["w"], region["s"]), projOut=projOut, flags="d" + ) + proj, coord2 = utils.ReprojectCoordinates( + coord=(region["e"], region["n"]), projOut=projOut, flags="d" + ) + # useless, used in derived class + proj, coord3 = utils.ReprojectCoordinates( + coord=(0.0, 0.0), projOut=projOut, flags="d" + ) + proj, coord4 = utils.ReprojectCoordinates( + coord=(region["ewres"], region["nsres"]), projOut=projOut, flags="d" + ) + if coord1 and coord2: + if proj in {"ll", "latlong", "longlat"} and format == "DMS": + w, s = utils.Deg2DMS( + coord1[0], coord1[1], string=False, precision=precision + ) + e, n = utils.Deg2DMS( + coord2[0], coord2[1], string=False, precision=precision + ) + ewres, nsres = utils.Deg2DMS( + abs(coord3[0]) - abs(coord4[0]), + abs(coord3[1]) - abs(coord4[1]), + string=False, + hemisphere=False, precision=precision, ) - raise SbException(_("Error in projection (check the settings)")) + return self._formatRegion( + w=w, s=s, e=e, n=n, ewres=ewres, nsres=nsres + ) + w, s = coord1 + e, n = coord2 + ewres, nsres = coord3 + return self._formatRegion( + w=w, + s=s, + e=e, + n=n, + ewres=ewres, + nsres=nsres, + precision=precision, + ) + raise SbException(_("Error in projection (check the settings)")) - elif self.mapFrame.GetMap().projinfo["proj"] == "ll" and format == "DMS": + if self.mapFrame.GetMap().projinfo["proj"] == "ll" and format == "DMS": w, s = utils.Deg2DMS( region["w"], region["s"], string=False, precision=precision ) @@ -930,13 +922,12 @@ def ReprojectRegionFromMap(self, region, useDefinedProjection, precision, format region["ewres"], region["nsres"], string=False, precision=precision ) return self._formatRegion(w=w, s=s, e=e, n=n, ewres=ewres, nsres=nsres) - else: - w, s = region["w"], region["s"] - e, n = region["e"], region["n"] - ewres, nsres = region["ewres"], region["nsres"] - return self._formatRegion( - w=w, s=s, e=e, n=n, ewres=ewres, nsres=nsres, precision=precision - ) + w, s = region["w"], region["s"] + e, n = region["e"], region["n"] + ewres, nsres = region["ewres"], region["nsres"] + return self._formatRegion( + w=w, s=s, e=e, n=n, ewres=ewres, nsres=nsres, precision=precision + ) class SbCompRegionExtent(SbRegionExtent): diff --git a/gui/wxpython/nviz/tools.py b/gui/wxpython/nviz/tools.py index 150fe519635..dce751e6b27 100644 --- a/gui/wxpython/nviz/tools.py +++ b/gui/wxpython/nviz/tools.py @@ -3933,8 +3933,7 @@ def OnSurfacePosition(self, event): self.win["surface"]["position"]["reset"], }: continue - else: - self.FindWindowById(win).SetValue(value) + self.FindWindowById(win).SetValue(value) data = self.GetLayerData("surface") id = data["surface"]["object"]["id"] @@ -4747,8 +4746,8 @@ def OnVolumePosition(self, event): self.win["volume"]["position"]["reset"], }: continue - else: - self.FindWindowById(win).SetValue(value) + + self.FindWindowById(win).SetValue(value) data = self.GetLayerData("volume") id = data["volume"]["object"]["id"] diff --git a/gui/wxpython/psmap/instructions.py b/gui/wxpython/psmap/instructions.py index 82560353806..dcdfb907383 100644 --- a/gui/wxpython/psmap/instructions.py +++ b/gui/wxpython/psmap/instructions.py @@ -223,7 +223,7 @@ def Read(self, filename): buffer = [] continue - elif line.startswith("paper"): + if line.startswith("paper"): instruction = "paper" isBuffer = True buffer.append(line) @@ -698,7 +698,7 @@ def Read(self, instruction, text, **kwargs): if line.split()[1].lower() in {"n", "no", "none"}: instr["border"] = "n" break - elif line.split()[1].lower() in {"y", "yes"}: + if line.split()[1].lower() in {"y", "yes"}: instr["border"] = "y" elif line.startswith("width"): instr["width"] = line.split()[1] diff --git a/gui/wxpython/timeline/frame.py b/gui/wxpython/timeline/frame.py index 0829d8bc3a0..c5c54982ed3 100644 --- a/gui/wxpython/timeline/frame.py +++ b/gui/wxpython/timeline/frame.py @@ -518,7 +518,7 @@ def _checkDatasets(self, datasets): if len(indices) == 0: raise GException(errorMsg) - elif len(indices) >= 2: + if len(indices) >= 2: dlg = wx.SingleChoiceDialog( self, message=_("Please specify the space time dataset <%s>.") % dataset, diff --git a/gui/wxpython/tplot/frame.py b/gui/wxpython/tplot/frame.py index 584d97fbf24..db798081c51 100755 --- a/gui/wxpython/tplot/frame.py +++ b/gui/wxpython/tplot/frame.py @@ -1175,7 +1175,7 @@ def _checkDatasets(self, datasets, typ): if len(indices) == 0: raise GException(errorMsg) - elif len(indices) >= 2: + if len(indices) >= 2: dlg = wx.SingleChoiceDialog( self, message=_("Please specify the space time dataset <%s>.") % dataset, diff --git a/gui/wxpython/vnet/dialogs.py b/gui/wxpython/vnet/dialogs.py index 396f0821c10..2979429a572 100644 --- a/gui/wxpython/vnet/dialogs.py +++ b/gui/wxpython/vnet/dialogs.py @@ -647,11 +647,11 @@ def _updateInputDbMgrData(self): if inpLayer in browseLayers: needLayers.append(inpLayer) continue - else: - wx.BeginBusyCursor() - self.inpDbMgrData["browse"].AddLayer(inpLayer) - wx.EndBusyCursor() - needLayers.append(inpLayer) + + wx.BeginBusyCursor() + self.inpDbMgrData["browse"].AddLayer(inpLayer) + wx.EndBusyCursor() + needLayers.append(inpLayer) for layer in browseLayers: if layer not in needLayers: @@ -1977,7 +1977,6 @@ def GetSelectedIndices(self, state=wx.LIST_STATE_SELECTED): index = self.GetNextItem(lastFound, wx.LIST_NEXT_ALL, state) if index == -1: break - else: - lastFound = index - indices.append(index) + lastFound = index + indices.append(index) return indices diff --git a/gui/wxpython/vnet/vnet_data.py b/gui/wxpython/vnet/vnet_data.py index 9971c9a9699..2074e037350 100644 --- a/gui/wxpython/vnet/vnet_data.py +++ b/gui/wxpython/vnet/vnet_data.py @@ -1187,9 +1187,8 @@ def _savePreviousHist(self, newHist, oldHist): if newHistStepsNum >= self.maxHistSteps: removedHistStep = removedHistData[line] = {} continue - else: - newHist.write("%s%s%s" % ("\n", line, "\n")) - self.histStepsNum = newHistStepsNum + newHist.write("%s%s%s" % ("\n", line, "\n")) + self.histStepsNum = newHistStepsNum elif newHistStepsNum >= self.maxHistSteps: self._parseLine(line, removedHistStep) else: @@ -1289,10 +1288,10 @@ def _getHistStepData(self, histStep): for line in hist: if not line.strip() and isSearchedHistStep: break - elif not line.strip(): + if not line.strip(): newHistStep = True continue - elif isSearchedHistStep: + if isSearchedHistStep: self._parseLine(line, histStepData) if newHistStep: diff --git a/man/build_class_graphical.py b/man/build_class_graphical.py index 554c950d3a8..83338f1942c 100644 --- a/man/build_class_graphical.py +++ b/man/build_class_graphical.py @@ -165,7 +165,7 @@ def generate_page_for_category( img_class = "linkimg" if skip_no_image and not img: continue - elif not img: + if not img: img = "grass_logo.png" img_class = "default-img" if basename.startswith("wxGUI"): diff --git a/pyproject.toml b/pyproject.toml index 3c5950f922b..d739653809e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -208,9 +208,6 @@ ignore = [ "RET501", # unnecessary-return-none "RET502", # implicit-return-value "RET503", # implicit-return - "RET506", # superfluous-else-raise - "RET507", # superfluous-else-continue - "RET508", # superfluous-else-break "RUF003", # ambiguous-unicode-character-comment "RUF005", # collection-literal-concatenation "RUF012", # mutable-class-default diff --git a/python/grass/imaging/images2avi.py b/python/grass/imaging/images2avi.py index 1b0d9d8cd20..9776b9c0db2 100644 --- a/python/grass/imaging/images2avi.py +++ b/python/grass/imaging/images2avi.py @@ -141,19 +141,18 @@ def writeAvi( print(gs.decode(outPut)) print(gs.decode(S.stderr.read())) raise RuntimeError(_("Could not write avi.")) - else: - try: - # Copy avi - shutil.copy(os.path.join(tempDir, "output.avi"), filename) - except Exception as err: - # Clean up - _cleanDir(tempDir) - if bg_task: - return str(err) - raise - + try: + # Copy avi + shutil.copy(os.path.join(tempDir, "output.avi"), filename) + except Exception as err: # Clean up _cleanDir(tempDir) + if bg_task: + return str(err) + raise + + # Clean up + _cleanDir(tempDir) def readAvi(filename, asNumpy=True): @@ -195,11 +194,11 @@ def readAvi(filename, asNumpy=True): # Clean up _cleanDir(tempDir) raise RuntimeError("Could not read avi.") - else: - # Read images - images = images2ims.readIms(os.path.join(tempDir, "im*.jpg"), asNumpy) - # Clean up - _cleanDir(tempDir) + + # Read images + images = images2ims.readIms(os.path.join(tempDir, "im*.jpg"), asNumpy) + # Clean up + _cleanDir(tempDir) # Done return images diff --git a/python/grass/pygrass/gis/__init__.py b/python/grass/pygrass/gis/__init__.py index 597c4bf75fa..29fecd3d699 100644 --- a/python/grass/pygrass/gis/__init__.py +++ b/python/grass/pygrass/gis/__init__.py @@ -114,7 +114,7 @@ def make_mapset(mapset, location=None, gisdbase=None): res = libgis.G_make_mapset(gisdbase, location, mapset) if res == -1: raise GrassError("Cannot create new mapset") - elif res == -2: + if res == -2: raise GrassError("Illegal name") diff --git a/python/grass/pygrass/messages/__init__.py b/python/grass/pygrass/messages/__init__.py index 36356433381..54d9af1b1c5 100644 --- a/python/grass/pygrass/messages/__init__.py +++ b/python/grass/pygrass/messages/__init__.py @@ -262,8 +262,7 @@ def fatal(self, message): if self.raise_on_error is True: raise FatalError(message) - else: - sys.exit(1) + sys.exit(1) def debug(self, level, message): """Send a debug message to stderr diff --git a/python/grass/pygrass/modules/interface/parameter.py b/python/grass/pygrass/modules/interface/parameter.py index 1ec5466bc74..d950cd40008 100644 --- a/python/grass/pygrass/modules/interface/parameter.py +++ b/python/grass/pygrass/modules/interface/parameter.py @@ -18,15 +18,15 @@ def _check_value(param, value): string = (bytes, str) def raiseexcpet(exc, param, ptype, value): - """Function to modifa the error message""" + """Function to modify the error message""" msg = req % (param.name, param.typedesc, ptype, value, str(exc)) if isinstance(exc, ValueError): raise ValueError(msg) - elif isinstance(exc, TypeError): + if isinstance(exc, TypeError): raise TypeError(msg) - else: - exc.message = msg - raise exc + + exc.message = msg + raise exc def check_string(value): """Function to check that a string parameter is already a string""" diff --git a/python/grass/pygrass/raster/category.py b/python/grass/pygrass/raster/category.py index fa4e8d37b12..9c34bb8390f 100644 --- a/python/grass/pygrass/raster/category.py +++ b/python/grass/pygrass/raster/category.py @@ -189,7 +189,7 @@ def _set_c_cat(self, label, min_cat, max_cat=None): return None if err == 0: raise GrassError(_("Null value detected")) - elif err == -1: + if err == -1: raise GrassError(_("Error executing: Rast_set_cat")) def __del__(self): diff --git a/python/grass/pygrass/vector/table.py b/python/grass/pygrass/vector/table.py index 5442d2f1e51..b83f751a0dd 100644 --- a/python/grass/pygrass/vector/table.py +++ b/python/grass/pygrass/vector/table.py @@ -134,8 +134,7 @@ def limit(self, number): """ if not isinstance(number, int): raise ValueError("Must be an integer.") - else: - self._limit = "LIMIT {number}".format(number=number) + self._limit = "LIMIT {number}".format(number=number) return self def group_by(self, *groupby): diff --git a/python/grass/script/task.py b/python/grass/script/task.py index 9867b3d8c49..758372938e3 100644 --- a/python/grass/script/task.py +++ b/python/grass/script/task.py @@ -153,8 +153,7 @@ def get_param(self, value, element="name", raiseError=True): _("Parameter element '%(element)s' not found: '%(value)s'") % {"element": element, "value": value} ) - else: - return None + return None def get_flag(self, aFlag): """Find and return a flag by name diff --git a/python/grass/temporal/temporal_algebra.py b/python/grass/temporal/temporal_algebra.py index 2429b61e35e..a27fa91cd1d 100644 --- a/python/grass/temporal/temporal_algebra.py +++ b/python/grass/temporal/temporal_algebra.py @@ -1156,11 +1156,7 @@ def set_temporal_extent_list(self, maplist, topolist=["EQUAL"], temporal="l"): if returncode == 0: break # Append map to result map list. - elif returncode == 1: - # print(map_new.get_id() + " " + - # str(map_new.get_temporal_extent_as_tuple())) - # print(map_new.condition_value) - # print(map_new.cmd_list) + if returncode == 1: # resultlist.append(map_new) resultdict[map_new.get_id()] = map_new @@ -1243,42 +1239,41 @@ def check_stds(self, input, clear=False, stds_type=None, check_type=True): _("Space time %s dataset <%s> not found") % (stds.get_new_map_instance(None).get_type(), id_input) ) + # Select temporal dataset entry from database. + stds.select(dbif=self.dbif) + if self.use_granularity: + # We create the maplist out of the map array from none-gap objects + maplist = [] + map_array = stds.get_registered_maps_as_objects_by_granularity( + gran=self.granularity, dbif=self.dbif + ) + for entry in map_array: + # Ignore gap objects + if entry[0].get_id() is not None: + maplist.append(entry[0]) else: - # Select temporal dataset entry from database. - stds.select(dbif=self.dbif) - if self.use_granularity: - # We create the maplist out of the map array from none-gap objects - maplist = [] - map_array = stds.get_registered_maps_as_objects_by_granularity( - gran=self.granularity, dbif=self.dbif - ) - for entry in map_array: - # Ignore gap objects - if entry[0].get_id() is not None: - maplist.append(entry[0]) - else: - maplist = stds.get_registered_maps_as_objects(dbif=self.dbif) - # Create map_value as empty list item. - for map_i in maplist: - if "map_value" not in dir(map_i): - map_i.map_value = [] - if "condition_value" not in dir(map_i): - map_i.condition_value = [] - # Set and check global temporal type variable and map. - if map_i.is_time_absolute() and self.temporaltype is None: - self.temporaltype = "absolute" - elif map_i.is_time_relative() and self.temporaltype is None: - self.temporaltype = "relative" - elif ( - map_i.is_time_absolute() and self.temporaltype == "relative" - ) or (map_i.is_time_relative() and self.temporaltype == "absolute"): - self.msgr.fatal( - _( - "Wrong temporal type of space time dataset " - "<%s> <%s> time is required" - ) - % (id_input, self.temporaltype) + maplist = stds.get_registered_maps_as_objects(dbif=self.dbif) + # Create map_value as empty list item. + for map_i in maplist: + if "map_value" not in dir(map_i): + map_i.map_value = [] + if "condition_value" not in dir(map_i): + map_i.condition_value = [] + # Set and check global temporal type variable and map. + if map_i.is_time_absolute() and self.temporaltype is None: + self.temporaltype = "absolute" + elif map_i.is_time_relative() and self.temporaltype is None: + self.temporaltype = "relative" + elif (map_i.is_time_absolute() and self.temporaltype == "relative") or ( + map_i.is_time_relative() and self.temporaltype == "absolute" + ): + self.msgr.fatal( + _( + "Wrong temporal type of space time dataset " + "<%s> <%s> time is required" ) + % (id_input, self.temporaltype) + ) elif isinstance(input, self.mapclass): # Check if the input is a single map and return it as list with one entry. maplist = [input] @@ -2637,9 +2632,9 @@ def p_expr_tmap_function(self, t): _("%s map <%s> not found in GRASS spatial database") % (map_i.get_type(), id_input) ) - else: - # Select dataset entry from database. - map_i.select(dbif=self.dbif) + + # Select dataset entry from database. + map_i.select(dbif=self.dbif) else: raise FatalError( _( @@ -3383,8 +3378,7 @@ def p_error(self, t): "syntax error on line %d, position %i token %s near '%s' expression " "'%s'" % (t.lineno, t.lexpos, t.type, t.value, self.expression) ) - else: - raise SyntaxError("Unexpected syntax error") + raise SyntaxError("Unexpected syntax error") if __name__ == "__main__": diff --git a/python/grass/temporal/temporal_operator.py b/python/grass/temporal/temporal_operator.py index 7d91ed0f65c..d99244c7c49 100644 --- a/python/grass/temporal/temporal_operator.py +++ b/python/grass/temporal/temporal_operator.py @@ -352,16 +352,16 @@ def p_relation_operator(self, t): # Check for correct type. if not self.optype == "relation": raise SyntaxError('Wrong optype "%s" must be "relation"' % self.optype) + + # Set three operator components. + if isinstance(t[2], list): + self.relations = t[2] else: - # Set three operator components. - if isinstance(t[2], list): - self.relations = t[2] - else: - self.relations = [t[2]] - self.temporal = None - self.function = None + self.relations = [t[2]] + self.temporal = None + self.function = None - t[0] = t[2] + t[0] = t[2] def p_relation_bool_operator(self, t): # {||, during} @@ -374,17 +374,17 @@ def p_relation_bool_operator(self, t): """ if not self.optype == "boolean": raise SyntaxError('Wrong optype "%s" must be "boolean"' % self.optype) + + # Set three operator components. + if isinstance(t[5], list): + self.relations = t[5] else: - # Set three operator components. - if isinstance(t[5], list): - self.relations = t[5] - else: - self.relations = [t[5]] - self.temporal = "l" - self.function = t[2] + t[3] - self.aggregate = t[2] + self.relations = [t[5]] + self.temporal = "l" + self.function = t[2] + t[3] + self.aggregate = t[2] - t[0] = t[2] + t[0] = t[2] def p_relation_bool_combi_operator(self, t): # {||, during, &} @@ -401,17 +401,17 @@ def p_relation_bool_combi_operator(self, t): """ if not self.optype == "boolean": raise SyntaxError('Wrong optype "%s" must be "boolean"' % self.optype) + + # Set three operator components. + if isinstance(t[5], list): + self.relations = t[5] else: - # Set three operator components. - if isinstance(t[5], list): - self.relations = t[5] - else: - self.relations = [t[5]] - self.temporal = "l" - self.function = t[2] + t[3] - self.aggregate = t[7] + self.relations = [t[5]] + self.temporal = "l" + self.function = t[2] + t[3] + self.aggregate = t[7] - t[0] = t[2] + t[0] = t[2] def p_relation_bool_combi_operator2(self, t): # {||, during, left} @@ -424,17 +424,17 @@ def p_relation_bool_combi_operator2(self, t): """ if not self.optype == "boolean": raise SyntaxError('Wrong optype "%s" must be "boolean"' % self.optype) + + # Set three operator components. + if isinstance(t[5], list): + self.relations = t[5] else: - # Set three operator components. - if isinstance(t[5], list): - self.relations = t[5] - else: - self.relations = [t[5]] - self.temporal = t[7] - self.function = t[2] + t[3] - self.aggregate = t[2] + self.relations = [t[5]] + self.temporal = t[7] + self.function = t[2] + t[3] + self.aggregate = t[2] - t[0] = t[2] + t[0] = t[2] def p_relation_bool_combi_operator3(self, t): # {||, during, |, left} @@ -451,17 +451,17 @@ def p_relation_bool_combi_operator3(self, t): """ if not self.optype == "boolean": raise SyntaxError('Wrong optype "%s" must be "relation"' % self.optype) + + # Set three operator components. + if isinstance(t[5], list): + self.relations = t[5] else: - # Set three operator components. - if isinstance(t[5], list): - self.relations = t[5] - else: - self.relations = [t[5]] - self.temporal = t[9] - self.function = t[2] + t[3] - self.aggregate = t[7] + self.relations = [t[5]] + self.temporal = t[9] + self.function = t[2] + t[3] + self.aggregate = t[7] - t[0] = t[2] + t[0] = t[2] def p_select_relation_operator(self, t): # {!:} @@ -477,27 +477,28 @@ def p_select_relation_operator(self, t): """ if not self.optype == "select": raise SyntaxError('Wrong optype "%s" must be "select"' % self.optype) - else: - if len(t) == 4: - # Set three operator components. - self.relations = ["equal", "equivalent"] - self.temporal = "l" - self.function = t[2] - elif len(t) == 6: - if isinstance(t[4], list): - self.relations = t[4] - else: - self.relations = [t[4]] - self.temporal = "l" - self.function = t[2] - elif len(t) == 8: - if isinstance(t[4], list): - self.relations = t[4] - else: - self.relations = [t[4]] - self.temporal = t[6] - self.function = t[2] - t[0] = t[2] + + if len(t) == 4: + # Set three operator components. + self.relations = ["equal", "equivalent"] + self.temporal = "l" + self.function = t[2] + elif len(t) == 6: + if isinstance(t[4], list): + self.relations = t[4] + else: + self.relations = [t[4]] + self.temporal = "l" + self.function = t[2] + elif len(t) == 8: + if isinstance(t[4], list): + self.relations = t[4] + else: + self.relations = [t[4]] + self.temporal = t[6] + self.function = t[2] + + t[0] = t[2] def p_hash_relation_operator(self, t): # {#} @@ -513,27 +514,28 @@ def p_hash_relation_operator(self, t): """ if not self.optype == "hash": raise SyntaxError('Wrong optype "%s" must be "hash"' % self.optype) - else: - if len(t) == 4: - # Set three operator components. - self.relations = ["equal"] - self.temporal = "l" - self.function = t[2] - elif len(t) == 6: - if isinstance(t[4], list): - self.relations = t[4] - else: - self.relations = [t[4]] - self.temporal = "l" - self.function = t[2] - elif len(t) == 8: - if isinstance(t[4], list): - self.relations = t[4] - else: - self.relations = [t[4]] - self.temporal = t[6] - self.function = t[2] - t[0] = t[2] + + if len(t) == 4: + # Set three operator components. + self.relations = ["equal"] + self.temporal = "l" + self.function = t[2] + elif len(t) == 6: + if isinstance(t[4], list): + self.relations = t[4] + else: + self.relations = [t[4]] + self.temporal = "l" + self.function = t[2] + elif len(t) == 8: + if isinstance(t[4], list): + self.relations = t[4] + else: + self.relations = [t[4]] + self.temporal = t[6] + self.function = t[2] + + t[0] = t[2] def p_raster_relation_operator(self, t): # {+} @@ -549,27 +551,28 @@ def p_raster_relation_operator(self, t): """ if not self.optype == "raster": raise SyntaxError('Wrong optype "%s" must be "raster"' % self.optype) - else: - if len(t) == 4: - # Set three operator components. - self.relations = ["equal"] - self.temporal = "l" - self.function = t[2] - elif len(t) == 6: - if isinstance(t[4], list): - self.relations = t[4] - else: - self.relations = [t[4]] - self.temporal = "l" - self.function = t[2] - elif len(t) == 8: - if isinstance(t[4], list): - self.relations = t[4] - else: - self.relations = [t[4]] - self.temporal = t[6] - self.function = t[2] - t[0] = t[2] + + if len(t) == 4: + # Set three operator components. + self.relations = ["equal"] + self.temporal = "l" + self.function = t[2] + elif len(t) == 6: + if isinstance(t[4], list): + self.relations = t[4] + else: + self.relations = [t[4]] + self.temporal = "l" + self.function = t[2] + elif len(t) == 8: + if isinstance(t[4], list): + self.relations = t[4] + else: + self.relations = [t[4]] + self.temporal = t[6] + self.function = t[2] + + t[0] = t[2] def p_overlay_relation_operator(self, t): # {+} @@ -585,27 +588,28 @@ def p_overlay_relation_operator(self, t): """ if not self.optype == "overlay": raise SyntaxError('Wrong optype "%s" must be "overlay"' % self.optype) - else: - if len(t) == 4: - # Set three operator components. - self.relations = ["equal"] - self.temporal = "l" - self.function = t[2] - elif len(t) == 6: - if isinstance(t[4], list): - self.relations = t[4] - else: - self.relations = [t[4]] - self.temporal = "l" - self.function = t[2] - elif len(t) == 8: - if isinstance(t[4], list): - self.relations = t[4] - else: - self.relations = [t[4]] - self.temporal = t[6] - self.function = t[2] - t[0] = t[2] + + if len(t) == 4: + # Set three operator components. + self.relations = ["equal"] + self.temporal = "l" + self.function = t[2] + elif len(t) == 6: + if isinstance(t[4], list): + self.relations = t[4] + else: + self.relations = [t[4]] + self.temporal = "l" + self.function = t[2] + elif len(t) == 8: + if isinstance(t[4], list): + self.relations = t[4] + else: + self.relations = [t[4]] + self.temporal = t[6] + self.function = t[2] + + t[0] = t[2] def p_relation(self, t): # The list of relations. Temporal and spatial relations are supported diff --git a/python/grass/temporal/temporal_raster_base_algebra.py b/python/grass/temporal/temporal_raster_base_algebra.py index cb2122f4a37..bb58c8405f7 100644 --- a/python/grass/temporal/temporal_raster_base_algebra.py +++ b/python/grass/temporal/temporal_raster_base_algebra.py @@ -610,7 +610,7 @@ def set_temporal_extent_list( if returncode == 0: break # Append map to result map list. - elif returncode == 1: + if returncode == 1: # print(map_new.cmd_list) # resultlist.append(map_new) if cmd_bool: @@ -969,12 +969,11 @@ def p_expr_spmap_function(self, t): _("%s map <%s> not found in GRASS spatial database") % (map_i.get_type(), id_input) ) - else: - # Select dataset entry from database. - map_i.select(dbif=self.dbif) - # Create command list for map object. - cmdstring = "(%s)" % (map_i.get_map_id()) - map_i.cmd_list = cmdstring + # Select dataset entry from database. + map_i.select(dbif=self.dbif) + # Create command list for map object. + cmdstring = "(%s)" % (map_i.get_map_id()) + map_i.cmd_list = cmdstring # Return map object. t[0] = cmdstring else: diff --git a/python/grass/temporal/temporal_vector_algebra.py b/python/grass/temporal/temporal_vector_algebra.py index 551c69783f5..2f28da0c7e5 100644 --- a/python/grass/temporal/temporal_vector_algebra.py +++ b/python/grass/temporal/temporal_vector_algebra.py @@ -386,7 +386,7 @@ def set_temporal_extent_list(self, maplist, topolist=["EQUAL"], temporal="l"): if returncode == 0: break # Append map to result map list. - elif returncode == 1: + if returncode == 1: # resultlist.append(map_new) resultdict[map_new.get_id()] = map_new if returncode == 0: diff --git a/scripts/db.in.ogr/db.in.ogr.py b/scripts/db.in.ogr/db.in.ogr.py index a95919f4936..b7d7b01b51e 100755 --- a/scripts/db.in.ogr/db.in.ogr.py +++ b/scripts/db.in.ogr/db.in.ogr.py @@ -115,8 +115,7 @@ def main(): "db.execute", input="-", stdin="DROP TABLE %s" % output ) break - else: - gs.fatal(_("Table <%s> already exists") % output) + gs.fatal(_("Table <%s> already exists") % output) # treat DB as real vector map... layer = db_table or None diff --git a/scripts/r.in.wms/wms_cap_parsers.py b/scripts/r.in.wms/wms_cap_parsers.py index 723a63f0b23..e9704651c7b 100644 --- a/scripts/r.in.wms/wms_cap_parsers.py +++ b/scripts/r.in.wms/wms_cap_parsers.py @@ -105,8 +105,7 @@ def __init__(self, cap_file, force_version=None): raise ParseError( _("Missing version attribute root node in Capabilities XML file") ) - else: - wms_version = self.getroot().attrib["version"] + wms_version = self.getroot().attrib["version"] if wms_version == "1.3.0": self.proj_tag = "CRS" diff --git a/scripts/r.in.wms/wms_drv.py b/scripts/r.in.wms/wms_drv.py index e1131918c4a..e825aabe438 100644 --- a/scripts/r.in.wms/wms_drv.py +++ b/scripts/r.in.wms/wms_drv.py @@ -155,8 +155,7 @@ def _download(self): sleep(sleep_time) continue - else: - gs.fatal(_("Unable to write data into tempfile.\n%s") % str(e)) + gs.fatal(_("Unable to write data into tempfile.\n%s") % str(e)) finally: temp_tile_opened.close() diff --git a/scripts/v.what.strds/v.what.strds.py b/scripts/v.what.strds/v.what.strds.py index 8be00f3536d..2c819fd0e44 100644 --- a/scripts/v.what.strds/v.what.strds.py +++ b/scripts/v.what.strds/v.what.strds.py @@ -202,8 +202,7 @@ def main(): if name is None: isvalid = False break - else: - mapname_list.append(name) + mapname_list.append(name) if isvalid: entry = mapmatrizes[0][i] diff --git a/temporal/t.vect.observe.strds/t.vect.observe.strds.py b/temporal/t.vect.observe.strds/t.vect.observe.strds.py index 547f60a3250..7e9ac225890 100755 --- a/temporal/t.vect.observe.strds/t.vect.observe.strds.py +++ b/temporal/t.vect.observe.strds/t.vect.observe.strds.py @@ -185,8 +185,7 @@ def main(): if name is None: isvalid = False break - else: - mapname_list.append(name) + mapname_list.append(name) if isvalid: entry = mapmatrizes[0][i] From ffcb9e1621579bbfbf56e5417cb7946b4d2fe9cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Sun, 27 Oct 2024 09:56:30 -0400 Subject: [PATCH 451/471] temporal: Add precise typing overloads to dataset_factory (#4600) * temporal: Add precise typing overloads to dataset_factory This enables type checkers like mypy or Pyright to understand the returned class from the string passed as the type argument * temporal: Add typing overloads to dataset_factory * temporal: Add dataset_factory implementation with str type for type argument * temporal: Remove dataset_factory overload that listed all literal types * temporal: Update dataset_factory file header * temporal: Add dataset_factory overload with str type for type argument * temporal: Accept None for id argument in dataset_factory overload as used in existing calls --- python/grass/temporal/factory.py | 109 +++++++++++++++++++++++-------- 1 file changed, 80 insertions(+), 29 deletions(-) diff --git a/python/grass/temporal/factory.py b/python/grass/temporal/factory.py index ba7f5e7d4f0..f246700ef44 100644 --- a/python/grass/temporal/factory.py +++ b/python/grass/temporal/factory.py @@ -1,23 +1,18 @@ """ Object factory -Usage: - -.. code-block:: python - - import grass.temporal as tgis - - tgis.register_maps_in_space_time_dataset(type, name, maps) - - -(C) 2012-2013 by the GRASS Development Team +(C) 2012-2024 by the GRASS Development Team This program is free software under the GNU General Public License (>=v2). Read the file COPYING that comes with GRASS for details. -:authors: Soeren Gebbert +:authors: Soeren Gebbert, Edouard Choinière """ +from __future__ import annotations + +from typing import Literal, overload + from .core import get_tgis_message_interface from .space_time_datasets import ( Raster3DDataset, @@ -31,7 +26,65 @@ ############################################################################### -def dataset_factory(type, id): +@overload +def dataset_factory(type: Literal["strds"], id: str) -> SpaceTimeRasterDataset: + pass + + +@overload +def dataset_factory(type: Literal["str3ds"], id: str) -> SpaceTimeRaster3DDataset: + pass + + +@overload +def dataset_factory(type: Literal["stvds"], id: str) -> SpaceTimeVectorDataset: + pass + + +@overload +def dataset_factory(type: Literal["rast", "raster"], id: str) -> RasterDataset: + pass + + +@overload +def dataset_factory( + type: Literal["raster_3d", "rast3d", "raster3d"], + id: str, +) -> Raster3DDataset: + pass + + +@overload +def dataset_factory(type: Literal["vect", "vector"], id: str) -> VectorDataset: + pass + + +@overload +def dataset_factory( + type: str, id: str +) -> ( + SpaceTimeRasterDataset + | SpaceTimeRaster3DDataset + | SpaceTimeVectorDataset + | RasterDataset + | Raster3DDataset + | VectorDataset + | None +): + pass + + +def dataset_factory( + type: str, id: str | None +) -> ( + SpaceTimeRasterDataset + | SpaceTimeRaster3DDataset + | SpaceTimeVectorDataset + | RasterDataset + | Raster3DDataset + | VectorDataset + | None +): """A factory functions to create space time or map datasets :param type: the dataset type: rast or raster; rast3d, raster3d or raster_3d; @@ -39,20 +92,18 @@ def dataset_factory(type, id): :param id: The id of the dataset ("name@mapset") """ if type == "strds": - sp = SpaceTimeRasterDataset(id) - elif type == "str3ds": - sp = SpaceTimeRaster3DDataset(id) - elif type == "stvds": - sp = SpaceTimeVectorDataset(id) - elif type in {"rast", "raster"}: - sp = RasterDataset(id) - elif type in {"raster_3d", "rast3d", "raster3d"}: - sp = Raster3DDataset(id) - elif type in {"vect", "vector"}: - sp = VectorDataset(id) - else: - msgr = get_tgis_message_interface() - msgr.error(_("Unknown dataset type: %s") % type) - return None - - return sp + return SpaceTimeRasterDataset(id) + if type == "str3ds": + return SpaceTimeRaster3DDataset(id) + if type == "stvds": + return SpaceTimeVectorDataset(id) + if type in {"rast", "raster"}: + return RasterDataset(id) + if type in {"raster_3d", "rast3d", "raster3d"}: + return Raster3DDataset(id) + if type in {"vect", "vector"}: + return VectorDataset(id) + + msgr = get_tgis_message_interface() + msgr.error(_("Unknown dataset type: %s") % type) + return None From 84afcf47aaf49dcea2eaec1c0d93143a2b7dc069 Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Sun, 27 Oct 2024 17:35:45 -0400 Subject: [PATCH 452/471] r.in.poly: Fix Resource Leak issue in poly2rast.c (#4605) --- raster/r.in.poly/poly2rast.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/raster/r.in.poly/poly2rast.c b/raster/r.in.poly/poly2rast.c index e81d6f421a3..bb707d03e25 100644 --- a/raster/r.in.poly/poly2rast.c +++ b/raster/r.in.poly/poly2rast.c @@ -90,6 +90,7 @@ int poly_to_rast(char *input_file, char *raster_map, char *title, int nrows, if (stat < 0) { Rast_unopen(rfd); + fclose(ifd); return 1; } @@ -98,6 +99,7 @@ int poly_to_rast(char *input_file, char *raster_map, char *title, int nrows, Rast_short_history(raster_map, "raster", &history); Rast_command_history(&history); Rast_write_history(raster_map, &history); + fclose(ifd); return 0; } From 937b2f48d024e9913e5be37dc0b829f1e037160c Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Sun, 27 Oct 2024 17:38:58 -0400 Subject: [PATCH 453/471] lib/gis: Fix resource leak issue in copy_dir.c (#4606) --- lib/gis/copy_dir.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/gis/copy_dir.c b/lib/gis/copy_dir.c index 226e25a5a39..8babbc2e964 100644 --- a/lib/gis/copy_dir.c +++ b/lib/gis/copy_dir.c @@ -141,8 +141,10 @@ int G_recursive_copy(const char *src, const char *dst) sprintf(path, "%s/%s", src, dp->d_name); sprintf(path2, "%s/%s", dst, dp->d_name); - if (G_recursive_copy(path, path2) != 0) + if (G_recursive_copy(path, path2) != 0) { + closedir(dirp); return 1; + } } closedir(dirp); From 7d101ed70a6cc87d5f40850ece3184b04d89ee95 Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Mon, 28 Oct 2024 04:34:49 -0400 Subject: [PATCH 454/471] r.watershed: Fix Resource Leak issues in close_maps.c (#4607) --- raster/r.watershed/seg/close_maps.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/raster/r.watershed/seg/close_maps.c b/raster/r.watershed/seg/close_maps.c index 1669bbd89c3..56320428c32 100644 --- a/raster/r.watershed/seg/close_maps.c +++ b/raster/r.watershed/seg/close_maps.c @@ -325,6 +325,8 @@ int close_maps(void) } Rast_close(fd); + G_free(afbuf); + G_free(cbuf); Rast_init_colors(&colors); Rast_make_aspect_colors(&colors, -8, 8); From 24469d8dcb73fdcec1c59713de4a8c82ce3eea3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hern=C3=A1n=20De=20Angelis?= <51515911+dhdeangelis@users.noreply.github.com> Date: Mon, 28 Oct 2024 11:16:17 +0100 Subject: [PATCH 455/471] docs: v.build manual typo fix (#4604) --- vector/v.build/v.build.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/vector/v.build/v.build.html b/vector/v.build/v.build.html index 9f51afdb22b..c66da69110a 100644 --- a/vector/v.build/v.build.html +++ b/vector/v.build/v.build.html @@ -27,7 +27,7 @@

    NOTES

    If error vector map is specified, v.build checks:
      -
    • isolated bondaries (which are not forming any areas),
    • +
    • isolated boundaries (which are not forming any areas),
    • centroids outside of area,
    • duplicated centroids.
    @@ -38,7 +38,7 @@

    NOTES

    • lines or boundaries of zero length,
    • -
    • intersecting boundaries, ie. overlapping areas,
    • +
    • intersecting boundaries, i.e. overlapping areas,
    • areas without centroids that are not isles.
    @@ -46,7 +46,7 @@

    EXAMPLES

    Build topology

    -Note that option=build recreates also spatial and category +Note that option=build also recreates spatial and category indices, not only topology. For linked OGR layers (see v.external) also feature index is created. @@ -61,7 +61,7 @@

    Build topology

    Dump topology or indices

    Dump options print topology, spatial, category or feature index to -standard output. Such information can be printed also for vector maps +standard output. Such information can also be printed for vector maps from other mapsets. A description of the vector topology is available in the GRASS GIS 8 Programmer's Manual, section "Vector library topology management". From 6bceb98965a00957beb64fb1d05e29a96e0f87b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hern=C3=A1n=20De=20Angelis?= <51515911+dhdeangelis@users.noreply.github.com> Date: Mon, 28 Oct 2024 15:03:35 +0100 Subject: [PATCH 456/471] docs: v.outlier.html fix typos (#4610) --- vector/v.outlier/v.outlier.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vector/v.outlier/v.outlier.html b/vector/v.outlier/v.outlier.html index 3cd3ee46a5a..7e615364916 100644 --- a/vector/v.outlier/v.outlier.html +++ b/vector/v.outlier/v.outlier.html @@ -13,12 +13,12 @@

    DESCRIPTION

    (default), or only positive or only negative outliers. Filtering out only positive outliers can be useful to filter out vegetation returns (e.g. from forest canopies) from LIDAR point clouds, in order to -extract Digital Terrain Models. Filtering out only negative outliers +extract digital terrain models (DTMs). Filtering out only negative outliers can be useful to estimate vegetation height.

    -There is a flag to create a vector that can be visualizated by -qgis. That means that topology is build and the z coordinate is +There is a flag to create a vector that can be visualized in +QGIS. That means that topology is built and the z coordinate is considered as a category.

    EXAMPLES

    From b9a543a6233c186840d941d1bd600c7886409948 Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Mon, 28 Oct 2024 10:09:56 -0400 Subject: [PATCH 457/471] i.pansharpen: Fixed bare except clause (#4596) --- .flake8 | 2 +- scripts/i.pansharpen/i.pansharpen.py | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.flake8 b/.flake8 index c29f447c597..e3b2ecf8251 100644 --- a/.flake8 +++ b/.flake8 @@ -100,7 +100,7 @@ per-file-ignores = scripts/v.import/v.import.py: E722, E501 scripts/db.univar/db.univar.py: E501 scripts/d.frame/d.frame.py: E722 - scripts/i.pansharpen/i.pansharpen.py: E722, E501 + scripts/i.pansharpen/i.pansharpen.py: E501 scripts/v.what.strds/v.what.strds.py: E501 # Line too long (esp. module interface definitions) scripts/*/*.py: E501 diff --git a/scripts/i.pansharpen/i.pansharpen.py b/scripts/i.pansharpen/i.pansharpen.py index e78581c8845..730701bfb91 100755 --- a/scripts/i.pansharpen/i.pansharpen.py +++ b/scripts/i.pansharpen/i.pansharpen.py @@ -93,6 +93,7 @@ # %end import os +from grass.exceptions import CalledModuleError try: import numpy as np @@ -458,7 +459,7 @@ def main(): gs.run_command( "g.remove", flags="f", type="raster", pattern="tmp%s*" % pid, quiet=True ) - except: + except CalledModuleError: pass @@ -523,7 +524,7 @@ def brovey(pan, ms1, ms2, ms3, out, pid, sproc): pb.wait(), pg.wait(), pr.wait() try: pb.terminate(), pg.terminate(), pr.terminate() - except: + except OSError: pass # Cleanup @@ -535,7 +536,7 @@ def brovey(pan, ms1, ms2, ms3, out, pid, sproc): type="raster", name="%s,%s,%s" % (panmatch1, panmatch2, panmatch3), ) - except: + except CalledModuleError: pass @@ -575,7 +576,7 @@ def ihs(pan, ms1, ms2, ms3, out, pid, sproc): # Cleanup try: gs.run_command("g.remove", flags="f", quiet=True, type="raster", name=panmatch) - except: + except CalledModuleError: pass @@ -701,7 +702,7 @@ def pca(pan, ms1, ms2, ms3, out, pid, sproc): pb.wait(), pg.wait(), pr.wait() try: pb.terminate(), pg.terminate(), pr.terminate() - except: + except OSError: pass # Cleanup From 94034a3adb01bd8bd415da452f7235b58e1b6a2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hern=C3=A1n=20De=20Angelis?= <51515911+dhdeangelis@users.noreply.github.com> Date: Tue, 29 Oct 2024 15:20:56 +0100 Subject: [PATCH 458/471] docs: v.to.db.html fix manual typo (#4615) --- vector/v.to.db/v.to.db.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vector/v.to.db/v.to.db.html b/vector/v.to.db/v.to.db.html index 99c2d5b3dbc..f0ba460d450 100644 --- a/vector/v.to.db/v.to.db.html +++ b/vector/v.to.db/v.to.db.html @@ -25,7 +25,7 @@

    NOTES

    all features of same category taken together.

    Line azimuth is calculated as angle from the North direction to the line endnode direction at the line statnode. By default it's reported in decimal degrees (0-360, CW) but -it also may be repored in radians with unit=radians. Azimuth value +it also may be reported in radians with unit=radians. Azimuth value -1 is used to report closed line with it's startnode and endnode being in same place. Azimuth values make sense only if every vector line has only one entry in database (unique CAT value). @@ -165,4 +165,4 @@

    SEE ALSO

    AUTHORS

    Radim Blazek, ITC-irst, Trento, Italy
    -Line sinuousity implemented by Wolf Bergenheim +Line sinuosity implemented by Wolf Bergenheim From ba809db39314aee6e59de23ed3615ea9daedfc23 Mon Sep 17 00:00:00 2001 From: Anna Petrasova Date: Tue, 29 Oct 2024 10:41:47 -0400 Subject: [PATCH 459/471] wxGUI/nviz: fix missing imports (#4611) --- gui/wxpython/nviz/wxnviz.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/gui/wxpython/nviz/wxnviz.py b/gui/wxpython/nviz/wxnviz.py index 32c2541f14e..d11e03d290b 100644 --- a/gui/wxpython/nviz/wxnviz.py +++ b/gui/wxpython/nviz/wxnviz.py @@ -158,6 +158,11 @@ CONST_ATT, DM_FLAT, DM_GOURAUD, + DM_GRID_SURF, + DM_GRID_WIRE, + DM_POLY, + DM_WIRE, + DM_WIRE_POLY, MAP_ATT, MAX_ISOSURFS, GP_delete_site, @@ -2442,6 +2447,11 @@ def Corresponds(self, item): __all__ = [ "DM_FLAT", "DM_GOURAUD", + "DM_GRID_SURF", + "DM_GRID_WIRE", + "DM_POLY", + "DM_WIRE", + "DM_WIRE_POLY", "DRAW_QUICK_SURFACE", "DRAW_QUICK_VLINES", "DRAW_QUICK_VOLUME", From e6fd11079b82d38745f875301c022914e0474887 Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Tue, 29 Oct 2024 11:29:11 -0400 Subject: [PATCH 460/471] d.frame: Fix bare except clause (#4597) --- .flake8 | 1 - scripts/d.frame/d.frame.py | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.flake8 b/.flake8 index e3b2ecf8251..075fb37d075 100644 --- a/.flake8 +++ b/.flake8 @@ -99,7 +99,6 @@ per-file-ignores = scripts/v.unpack/v.unpack.py: E722, E501 scripts/v.import/v.import.py: E722, E501 scripts/db.univar/db.univar.py: E501 - scripts/d.frame/d.frame.py: E722 scripts/i.pansharpen/i.pansharpen.py: E501 scripts/v.what.strds/v.what.strds.py: E501 # Line too long (esp. module interface definitions) diff --git a/scripts/d.frame/d.frame.py b/scripts/d.frame/d.frame.py index ff77316f0ec..165dd712648 100755 --- a/scripts/d.frame/d.frame.py +++ b/scripts/d.frame/d.frame.py @@ -206,7 +206,7 @@ def calculate_frame(frame, at, width, height): """ try: b, t, l, r = list(map(float, at.split(","))) - except: + except ValueError: fatal(_("Invalid frame position: %s") % at) top = round(height - (t / 100.0 * height)) @@ -238,7 +238,7 @@ def create_frame(monitor, frame, at, overwrite=False): width = int(line.split("=", 1)[1].rsplit(" ", 1)[0]) elif "HEIGHT" in line: height = int(line.split("=", 1)[1].rsplit(" ", 1)[0]) - except: + except (ValueError, IndexError): pass if width < 0 or height < 0: From 6e5643d2b91372acc1a0106400670b61b41f1eb9 Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Tue, 29 Oct 2024 14:03:17 -0400 Subject: [PATCH 461/471] v.import: Fixed E722 bare except (#4614) --- .flake8 | 2 +- scripts/v.import/v.import.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.flake8 b/.flake8 index 075fb37d075..21272791e52 100644 --- a/.flake8 +++ b/.flake8 @@ -97,7 +97,7 @@ per-file-ignores = scripts/db.out.ogr/db.out.ogr.py: F841 scripts/g.extension/g.extension.py: F841, E722, E501 scripts/v.unpack/v.unpack.py: E722, E501 - scripts/v.import/v.import.py: E722, E501 + scripts/v.import/v.import.py: E501 scripts/db.univar/db.univar.py: E501 scripts/i.pansharpen/i.pansharpen.py: E501 scripts/v.what.strds/v.what.strds.py: E501 diff --git a/scripts/v.import/v.import.py b/scripts/v.import/v.import.py index 4f579c4bf45..1e467cd190b 100755 --- a/scripts/v.import/v.import.py +++ b/scripts/v.import/v.import.py @@ -263,7 +263,7 @@ def main(): if OGRdatasource.lower().endswith("gml"): try: from osgeo import gdal - except: + except ImportError: gs.fatal( _( "Unable to load GDAL Python bindings (requires package " @@ -338,7 +338,7 @@ def main(): if OGRdatasource.lower().endswith("gml"): try: from osgeo import gdal - except: + except ImportError: gs.fatal( _( "Unable to load GDAL Python bindings (requires package " From 86497131a7d1999936d380d1f57870e87690b9db Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 29 Oct 2024 21:02:19 +0000 Subject: [PATCH 462/471] CI(deps): Lock file maintenance (#4564) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index eacc92d3c4f..c507c1ec951 100644 --- a/flake.lock +++ b/flake.lock @@ -19,11 +19,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1728538411, - "narHash": "sha256-f0SBJz1eZ2yOuKUr5CA9BHULGXVSn6miBuUWdTyhUhU=", + "lastModified": 1730045389, + "narHash": "sha256-4spSNTZ6h8Xmvrr9oqfuxc9jarasGj1QOcsgw8BfNd8=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "b69de56fac8c2b6f8fd27f2eca01dcda8e0a4221", + "rev": "0fcb98acb6633445764dafe180e6833eb0f95208", "type": "github" }, "original": { From 9be02eeba6bfd198acb46aacf522f676fcd90fa2 Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Wed, 30 Oct 2024 16:55:17 -0400 Subject: [PATCH 463/471] v.unpack: Fixed bare 'except' (#4616) * updated E722 * updated .flake8 * Update v.unpack.py --- .flake8 | 4 ++-- scripts/v.unpack/v.unpack.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.flake8 b/.flake8 index 21272791e52..4bc6b924620 100644 --- a/.flake8 +++ b/.flake8 @@ -95,8 +95,8 @@ per-file-ignores = scripts/r.semantic.label/r.semantic.label.py: E501 scripts/v.report/v.report.py: E721 scripts/db.out.ogr/db.out.ogr.py: F841 - scripts/g.extension/g.extension.py: F841, E722, E501 - scripts/v.unpack/v.unpack.py: E722, E501 + scripts/g.extension/g.extension.py: E501 + scripts/v.unpack/v.unpack.py: E501 scripts/v.import/v.import.py: E501 scripts/db.univar/db.univar.py: E501 scripts/i.pansharpen/i.pansharpen.py: E501 diff --git a/scripts/v.unpack/v.unpack.py b/scripts/v.unpack/v.unpack.py index c3a7a55deb7..7baa1042ccd 100644 --- a/scripts/v.unpack/v.unpack.py +++ b/scripts/v.unpack/v.unpack.py @@ -77,7 +77,7 @@ def main(): tar = tarfile.TarFile.open(name=input_base, mode="r") try: data_name = tar.getnames()[0] - except: + except IndexError: grass.fatal(_("Pack file unreadable")) if flags["p"]: From 9581ca185bf001301f3907fd3f1ca6da09e9e287 Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Wed, 30 Oct 2024 17:14:00 -0400 Subject: [PATCH 464/471] wxGUI: Fixed bare 'except' in nviz/ (#4613) * updated E722 * updates * updates --------- Co-authored-by: Anna Petrasova --- .flake8 | 1 - gui/wxpython/nviz/mapwindow.py | 2 +- gui/wxpython/nviz/tools.py | 62 ++++++++++------------------------ 3 files changed, 19 insertions(+), 46 deletions(-) diff --git a/.flake8 b/.flake8 index 4bc6b924620..f0ac4155990 100644 --- a/.flake8 +++ b/.flake8 @@ -23,7 +23,6 @@ per-file-ignores = doc/python/m.distance.py: E501 gui/scripts/d.wms.py: E501 gui/wxpython/image2target/g.gui.image2target.py: E501 - gui/wxpython/nviz/*: E722 gui/wxpython/photo2image/g.gui.photo2image.py: E501 gui/wxpython/psmap/*: E501, E722 gui/wxpython/vdigit/*: F841, E722, F405, F403 diff --git a/gui/wxpython/nviz/mapwindow.py b/gui/wxpython/nviz/mapwindow.py index d3f93f94938..b6609427895 100644 --- a/gui/wxpython/nviz/mapwindow.py +++ b/gui/wxpython/nviz/mapwindow.py @@ -1390,7 +1390,7 @@ def LoadDataLayers(self): GError(parent=self, message=e.value) # when nviz.tools is not yet ready # during opening 3D view 2nd time - except: + except Exception: pass stop = gs.clock() diff --git a/gui/wxpython/nviz/tools.py b/gui/wxpython/nviz/tools.py index dce751e6b27..77f379ef019 100644 --- a/gui/wxpython/nviz/tools.py +++ b/gui/wxpython/nviz/tools.py @@ -160,7 +160,7 @@ def SetInitialMaps(self): else: try: selection = layers[0].GetName() - except: + except (AttributeError, IndexError): continue if ltype == "raster": self.FindWindowById(self.win["surface"]["map"]).SetValue(selection) @@ -734,24 +734,11 @@ def _createDataPage(self): self.mainPanelData = SP.ScrolledPanel(parent=self) self.mainPanelData.SetupScrolling(scroll_x=False) self.mainPanelData.AlwaysShowScrollbars(hflag=False) - try: # wxpython <= 2.8.10 - self.foldpanelData = fpb.FoldPanelBar( - parent=self.mainPanelData, - id=wx.ID_ANY, - style=fpb.FPB_DEFAULT_STYLE, - extraStyle=fpb.FPB_SINGLE_FOLD, - ) - except: - try: # wxpython >= 2.8.11 - self.foldpanelData = fpb.FoldPanelBar( - parent=self.mainPanelData, - id=wx.ID_ANY, - agwStyle=fpb.FPB_SINGLE_FOLD, - ) - except: # to be sure - self.foldpanelData = fpb.FoldPanelBar( - parent=self.mainPanelData, id=wx.ID_ANY, style=fpb.FPB_SINGLE_FOLD - ) + self.foldpanelData = fpb.FoldPanelBar( + parent=self.mainPanelData, + id=wx.ID_ANY, + agwStyle=fpb.FPB_SINGLE_FOLD, + ) self.foldpanelData.Bind(fpb.EVT_CAPTIONBAR, self.OnPressCaption) @@ -814,24 +801,11 @@ def _createAppearancePage(self): self.mainPanelAppear = SP.ScrolledPanel(parent=self) self.mainPanelAppear.SetupScrolling(scroll_x=False) self.mainPanelAppear.AlwaysShowScrollbars(hflag=False) - try: # wxpython <= 2.8.10 - self.foldpanelAppear = fpb.FoldPanelBar( - parent=self.mainPanelAppear, - id=wx.ID_ANY, - style=fpb.FPB_DEFAULT_STYLE, - extraStyle=fpb.FPB_SINGLE_FOLD, - ) - except: - try: # wxpython >= 2.8.11 - self.foldpanelAppear = fpb.FoldPanelBar( - parent=self.mainPanelAppear, - id=wx.ID_ANY, - agwStyle=fpb.FPB_SINGLE_FOLD, - ) - except: # to be sure - self.foldpanelAppear = fpb.FoldPanelBar( - parent=self.mainPanelAppear, id=wx.ID_ANY, style=fpb.FPB_SINGLE_FOLD - ) + self.foldpanelAppear = fpb.FoldPanelBar( + parent=self.mainPanelAppear, + id=wx.ID_ANY, + agwStyle=fpb.FPB_SINGLE_FOLD, + ) self.foldpanelAppear.Bind(fpb.EVT_CAPTIONBAR, self.OnPressCaption) # light page @@ -3360,7 +3334,7 @@ def OnSetSurface(self, event): name = event.GetString() try: self._getLayerPropertiesByName(name, mapType="raster")["surface"] - except: + except (AttributeError, TypeError, KeyError): self.EnablePage("fringe", False) return @@ -3384,7 +3358,7 @@ def OnSetVector(self, event): name = event.GetString() try: data = self._getLayerPropertiesByName(name, mapType="vector")["vector"] - except: + except (AttributeError, TypeError, KeyError): self.EnablePage("vector", False) return layer = self._getMapLayerByName(name, mapType="vector") @@ -3396,7 +3370,7 @@ def OnSetRaster3D(self, event): name = event.GetString() try: data = self._getLayerPropertiesByName(name, mapType="raster_3d")["volume"] - except: + except (AttributeError, TypeError, KeyError): self.EnablePage("volume", False) return @@ -4925,7 +4899,7 @@ def OnCPlaneSelection(self, event): try: planeIndex = int(plane.split()[-1]) - 1 self.EnablePage("cplane", enabled=True) - except: + except (ValueError, IndexError): planeIndex = -1 self.EnablePage("cplane", enabled=False) self.mapWindow.SelectCPlane(planeIndex) @@ -4942,7 +4916,7 @@ def OnCPlaneChanging(self, event): plane = self.FindWindowById(self.win["cplane"]["planes"]).GetStringSelection() try: planeIndex = int(plane.split()[-1]) - 1 - except: # TODO disabled page + except (ValueError, IndexError): # TODO disabled page planeIndex = -1 if event.GetId() in ( @@ -4984,7 +4958,7 @@ def OnCPlaneShading(self, event): plane = self.FindWindowById(self.win["cplane"]["planes"]).GetStringSelection() try: planeIndex = int(plane.split()[-1]) - 1 - except: # TODO disabled page + except (ValueError, IndexError): # TODO disabled page planeIndex = -1 self.mapWindow.cplanes[planeIndex]["shading"] = shading @@ -4999,7 +4973,7 @@ def OnCPlaneReset(self, event): plane = self.FindWindowById(self.win["cplane"]["planes"]).GetStringSelection() try: planeIndex = int(plane.split()[-1]) - 1 - except: # TODO disabled page + except (ValueError, IndexError): # TODO disabled page planeIndex = -1 self.mapWindow.cplanes[planeIndex] = copy.deepcopy( From 528763fb33ed7c696ea7fa19505b5d9ad02fb62e Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Wed, 30 Oct 2024 17:28:17 -0400 Subject: [PATCH 465/471] lib/ogsf: Dereference after null check in gvl2.c (#4588) Dereference after null check --- lib/ogsf/gvl2.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/ogsf/gvl2.c b/lib/ogsf/gvl2.c index 473d3e9406e..2c861c4b5ad 100644 --- a/lib/ogsf/gvl2.c +++ b/lib/ogsf/gvl2.c @@ -316,10 +316,16 @@ void GVL_get_dims(int id, int *rows, int *cols, int *depths) *rows = gvl->rows; *cols = gvl->cols; *depths = gvl->depths; - } - G_debug(3, "GVL_get_dims() id=%d, rows=%d, cols=%d, depths=%d", - gvl->gvol_id, gvl->rows, gvl->cols, gvl->depths); + G_debug(3, "GVL_get_dims() id=%d, rows=%d, cols=%d, depths=%d", + gvl->gvol_id, gvl->rows, gvl->cols, gvl->depths); + } + else { + G_debug(2, + "GVL_get_dims(): Attempted to access a null volume structure " + "for id=%d", + id); + } return; } From 00f51c9e7c22b9643de2b472bc4f6ef4296ceb90 Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Thu, 31 Oct 2024 11:27:33 -0400 Subject: [PATCH 466/471] v.report: Updated instance checks (#4618) --- .flake8 | 1 - scripts/v.report/v.report.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.flake8 b/.flake8 index f0ac4155990..3adda73f281 100644 --- a/.flake8 +++ b/.flake8 @@ -92,7 +92,6 @@ per-file-ignores = scripts/r.in.wms/wms_drv.py: E402, E722 scripts/r.in.wms/srs.py: E722 scripts/r.semantic.label/r.semantic.label.py: E501 - scripts/v.report/v.report.py: E721 scripts/db.out.ogr/db.out.ogr.py: F841 scripts/g.extension/g.extension.py: E501 scripts/v.unpack/v.unpack.py: E501 diff --git a/scripts/v.report/v.report.py b/scripts/v.report/v.report.py index 29d9d3c914e..80e9e8a1189 100755 --- a/scripts/v.report/v.report.py +++ b/scripts/v.report/v.report.py @@ -224,7 +224,7 @@ def main(): # calculate percentages records4 = [float(r[-1]) * 100 / total for r in records3] - if type(records1[0]) == int: + if isinstance(records1[0], int): records3 = [[r1] + [r4] for r1, r4 in zip(records1, records4)] else: records3 = [r1 + [r4] for r1, r4 in zip(records1, records4)] From ba0a4951958b20b0c1fca339c4fa92714095bacc Mon Sep 17 00:00:00 2001 From: Vaclav Petras Date: Thu, 31 Oct 2024 12:49:36 -0400 Subject: [PATCH 467/471] r.mask.status: Always output name of the mask (#4531) For both active and inactive raster mask, show the name of the raster which is used (or would be used) for the mask. This will allow tools like r.mask or GUI to do lower-level operations with or around mask without a need to know about defaults or user mechanism to change the name. I'm repurposing the existing 'name' (full_name) key which is now always set (as opposed to being null when no mask is present) as the 'present' boolean key already has the information on the mask presence. I'm renaming full_name to name because that creates a simpler interface (which is whole point of outputting full name as opposed to two keys to get name and mapset). --- include/grass/defs/raster.h | 1 + lib/raster/mask_info.c | 21 ++++++++++++ raster/r.mask.status/main.c | 34 +++++++------------ raster/r.mask.status/r.mask.status.html | 20 ++++++++--- .../r.mask.status/tests/r_mask_status_test.py | 23 +++++++------ 5 files changed, 63 insertions(+), 36 deletions(-) diff --git a/include/grass/defs/raster.h b/include/grass/defs/raster.h index 7f358562c72..bbdc8bfeaa1 100644 --- a/include/grass/defs/raster.h +++ b/include/grass/defs/raster.h @@ -392,6 +392,7 @@ int Rast_option_to_interp_type(const struct Option *); /* mask_info.c */ char *Rast_mask_info(void); +char *Rast_mask_name(void); bool Rast_mask_status(char *, char *, bool *, char *, char *); int Rast__mask_info(char *, char *); bool Rast_mask_is_present(void); diff --git a/lib/raster/mask_info.c b/lib/raster/mask_info.c index 317bab75b63..25d61341ebe 100644 --- a/lib/raster/mask_info.c +++ b/lib/raster/mask_info.c @@ -49,6 +49,27 @@ char *Rast_mask_info(void) return G_store(text); } +/** + * @brief Retrieves the name of the raster mask to use. + * + * The returned raster map name is fully qualified, i.e., in the form + % "name@mapset". + * + * The mask name is "MASK@", where is the current + * mapset. + * + * The memory for the returned mask name is dynamically allocated using + * G_store(). It is the caller's responsibility to free the memory with + * G_free() when it is no longer needed. + * + * @returns A dynamically allocated string containing the mask name. + */ +char *Rast_mask_name(void) +{ + // Mask name is always "MASK@". + return G_fully_qualified_name("MASK", G_mapset()); +} + /** * @brief Get raster mask status information * diff --git a/raster/r.mask.status/main.c b/raster/r.mask.status/main.c index 16790adf35c..b0a7b9185c0 100644 --- a/raster/r.mask.status/main.c +++ b/raster/r.mask.status/main.c @@ -89,7 +89,7 @@ int report_status(struct Parameters *params) } // Mask raster - char *full_mask = G_fully_qualified_name(name, mapset); + char *full_mask = Rast_mask_name(); // Underlying raster if applicable char *full_underlying = NULL; if (is_mask_reclass) @@ -99,10 +99,7 @@ int report_status(struct Parameters *params) JSON_Value *root_value = json_value_init_object(); JSON_Object *root_object = json_object(root_value); json_object_set_boolean(root_object, "present", present); - if (present) - json_object_set_string(root_object, "full_name", full_mask); - else - json_object_set_null(root_object, "full_name"); + json_object_set_string(root_object, "name", full_mask); if (is_mask_reclass) json_object_set_string(root_object, "is_reclass_of", full_underlying); @@ -121,9 +118,7 @@ int report_status(struct Parameters *params) printf("1"); else printf("0"); - printf("\nfull_name="); - if (present) - printf("%s", full_mask); + printf("\nname=%s", full_mask); printf("\nis_reclass_of="); if (is_mask_reclass) printf("%s", full_underlying); @@ -135,19 +130,16 @@ int report_status(struct Parameters *params) printf("true"); else printf("false"); - printf("\nfull_name: "); - if (present) - printf("|-\n %s", full_mask); - else - printf("null"); - // Null values in YAML can be an empty (no) value (rather than null), - // so we could use that, but using the explicit null as a reasonable - // starting point. + printf("\nname: "); + printf("|-\n %s", full_mask); printf("\nis_reclass_of: "); // Using block scalar with |- to avoid need for escaping. // Alternatively, we could check mapset naming limits against YAML // escaping needs for different types of strings and do the necessary // escaping here. + // Null values in YAML can be an empty (no) value (rather than null), + // so we could use that, but using the explicit null as a reasonable + // starting point. if (is_mask_reclass) printf("|-\n %s", full_underlying); else @@ -155,14 +147,14 @@ int report_status(struct Parameters *params) printf("\n"); } else { - if (present) - printf(_("Mask is active")); - else - printf(_("Mask is not present")); if (present) { - printf("\n"); + printf(_("Mask is active")); printf(_("Mask name: %s"), full_mask); } + else { + printf(_("Mask is not present")); + printf(_("If activated, mask name will be: %s"), full_mask); + } if (is_mask_reclass) { printf("\n"); printf(_("Mask is a raster reclassified from: %s"), diff --git a/raster/r.mask.status/r.mask.status.html b/raster/r.mask.status/r.mask.status.html index 248ee3ea317..cb3897820f5 100644 --- a/raster/r.mask.status/r.mask.status.html +++ b/raster/r.mask.status/r.mask.status.html @@ -1,11 +1,21 @@

    DESCRIPTION

    The r.mask.status reports information about the 2D raster mask and its -status. If the mask is present, the tool reports a full name of the raster (name -including the mapset) which represents the mask. It can also report full name of -the underlying raster if the mask is reclassified from another raster. - -

    +status. The tool reports whether the mask is present or not. For both active +and inactive mask, the tool reports a full name of the raster (name including +the mapset) which represents or would represent the mask. +It can also report full name of the underlying raster if the mask is +reclassified from another raster. + +The tool can be used to check if the mask is currently set +(present boolean in JSON), what is raster name used to represent +the mask (name string in JSON), and whether the raster is +reclassifed from another (is_reclass_of string or null in JSON). +YAML and shell script style outputs are following the JSON output if possible. +The plain text format outputs multi-line human-readable information in natural +language. + +

    With the -t flag, no output is printed, instead a return code is used to indicate presence or absence. The convention is the same same the POSIX test utility, so r.mask.status returns 0 when the mask is diff --git a/raster/r.mask.status/tests/r_mask_status_test.py b/raster/r.mask.status/tests/r_mask_status_test.py index deafdfb145b..a5d406ad581 100644 --- a/raster/r.mask.status/tests/r_mask_status_test.py +++ b/raster/r.mask.status/tests/r_mask_status_test.py @@ -15,10 +15,11 @@ def test_json_no_mask(session_no_data): session = session_no_data data = gs.parse_command("r.mask.status", format="json", env=session.env) assert "present" in data - assert "full_name" in data + assert "name" in data + assert data["name"], "Mask name needs to be always set" + assert data["name"] == "MASK@PERMANENT", "Default mask name and current mapset" assert "is_reclass_of" in data assert data["present"] is False - assert not data["full_name"] assert not data["is_reclass_of"] @@ -28,13 +29,13 @@ def test_json_with_r_mask(session_with_data): gs.run_command("r.mask", raster="a", env=session.env) data = gs.parse_command("r.mask.status", format="json", env=session.env) assert data["present"] is True - assert data["full_name"] == "MASK@PERMANENT" + assert data["name"] == "MASK@PERMANENT" assert data["is_reclass_of"] == "a@PERMANENT" # Now remove the mask. gs.run_command("r.mask", flags="r", env=session.env) data = gs.parse_command("r.mask.status", format="json", env=session.env) assert data["present"] is False - assert not data["full_name"] + assert data["name"] == "MASK@PERMANENT" assert not data["is_reclass_of"] @@ -44,13 +45,13 @@ def test_json_with_g_copy(session_with_data): gs.run_command("g.copy", raster="a,MASK", env=session.env) data = gs.parse_command("r.mask.status", format="json", env=session.env) assert data["present"] is True - assert data["full_name"] == "MASK@PERMANENT" + assert data["name"] == "MASK@PERMANENT" assert not data["is_reclass_of"] # Now remove the mask. gs.run_command("g.remove", type="raster", name="MASK", flags="f", env=session.env) data = gs.parse_command("r.mask.status", format="json", env=session.env) assert data["present"] is False - assert not data["full_name"] + assert data["name"] == "MASK@PERMANENT" assert not data["is_reclass_of"] @@ -60,13 +61,13 @@ def test_shell(session_with_data): gs.run_command("r.mask", raster="a", env=session.env) data = gs.parse_command("r.mask.status", format="shell", env=session.env) assert int(data["present"]) - assert data["full_name"] == "MASK@PERMANENT" + assert data["name"] == "MASK@PERMANENT" assert data["is_reclass_of"] == "a@PERMANENT" # Now remove the mask. gs.run_command("r.mask", flags="r", env=session.env) data = gs.parse_command("r.mask.status", format="shell", env=session.env) assert not int(data["present"]) - assert not data["full_name"] + assert data["name"] == "MASK@PERMANENT" assert not data["is_reclass_of"] @@ -78,14 +79,14 @@ def test_yaml(session_with_data): text = gs.read_command("r.mask.status", format="yaml", env=session.env) data = yaml.safe_load(text) assert data["present"] is True - assert data["full_name"] == "MASK@PERMANENT" + assert data["name"] == "MASK@PERMANENT" assert data["is_reclass_of"] == "a@PERMANENT" # Now remove the mask. gs.run_command("r.mask", flags="r", env=session.env) text = gs.read_command("r.mask.status", format="yaml", env=session.env) data = yaml.safe_load(text) assert data["present"] is False - assert not data["full_name"] + assert data["name"] == "MASK@PERMANENT" assert not data["is_reclass_of"] @@ -101,6 +102,8 @@ def test_plain(session_with_data): gs.run_command("r.mask", flags="r", env=session.env) text = gs.read_command("r.mask.status", format="plain", env=session.env) assert text + assert "MASK@PERMANENT" in text + assert "a@PERMANENT" not in text def test_without_parameters(session_no_data): From f9f01e1e40ab9eb8a28fd91717178935946e6606 Mon Sep 17 00:00:00 2001 From: Stefan Blumentrath Date: Thu, 31 Oct 2024 20:23:21 +0100 Subject: [PATCH 468/471] r.buildvrt: document performance issues with external data (#4441) * document performance issue * address code review * Apply suggestions from code review Co-authored-by: Markus Neteler * consistent recommendation * remove leftover dot --------- Co-authored-by: Markus Neteler --- raster/r.buildvrt/r.buildvrt.html | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/raster/r.buildvrt/r.buildvrt.html b/raster/r.buildvrt/r.buildvrt.html index 67368179efb..d5118ba5150 100644 --- a/raster/r.buildvrt/r.buildvrt.html +++ b/raster/r.buildvrt/r.buildvrt.html @@ -12,6 +12,8 @@

    NOTES

    the original raster maps which is only valid if the original raster maps remain in the originally indicated mapset. A VRT can also be built from raster maps registered with r.external. +However, GRASS VRTs built from external registered data (see below) +are known to have performance issues.

    Reading the whole VRT is slower than reading the equivalent single @@ -48,12 +50,23 @@

    VRT from a DEM in the North Carolina sample dataset

    r.buildvrt file=tilelist.csv output=elev_state_50m_vrt
    +

    KNOWN ISSUES

    + +Users may experience significant performance degradation with virtual rasters built +with r.buildvrt over GDAL-linked (r.external) raster maps, +especially on slower file systems with latency like NFS. Performance degradation +may also occur on local file systems, but is usually less severe. For such use cases +consider using the GRASS GIS addon +r.buildvrt.gdal +or building GDAL VRTs, e.g. with gdalbuildvrt. +

    SEE ALSO

    r.tile, r.patch, r.external +r.buildvrt.gdal

    From d5a87229ae30cf04ff90f7cfcdc923ec0afa5a36 Mon Sep 17 00:00:00 2001 From: Arohan Ajit Date: Thu, 31 Oct 2024 16:12:20 -0400 Subject: [PATCH 469/471] db.out.ogr: Removed unused variable (#4617) --- .flake8 | 2 +- scripts/db.out.ogr/db.out.ogr.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.flake8 b/.flake8 index 3adda73f281..33920703ca6 100644 --- a/.flake8 +++ b/.flake8 @@ -94,7 +94,7 @@ per-file-ignores = scripts/r.semantic.label/r.semantic.label.py: E501 scripts/db.out.ogr/db.out.ogr.py: F841 scripts/g.extension/g.extension.py: E501 - scripts/v.unpack/v.unpack.py: E501 + scripts/v.unpack/v.unpack.py: E501n scripts/v.import/v.import.py: E501 scripts/db.univar/db.univar.py: E501 scripts/i.pansharpen/i.pansharpen.py: E501 diff --git a/scripts/db.out.ogr/db.out.ogr.py b/scripts/db.out.ogr/db.out.ogr.py index 4fdfed23648..4fe5b4f9854 100755 --- a/scripts/db.out.ogr/db.out.ogr.py +++ b/scripts/db.out.ogr/db.out.ogr.py @@ -67,7 +67,6 @@ def main(): layer = options["layer"] format = options["format"] output = options["output"] - table = options["table"] if format.lower() == "dbf": format = "ESRI_Shapefile" From 5b9b46a0cb1606ea7fd0c69d5145ce879b9a6c31 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 31 Oct 2024 20:37:29 +0000 Subject: [PATCH 470/471] CI(deps): Update softprops/action-gh-release action to v2.0.9 (#4624) --- .github/workflows/create_release_draft.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/create_release_draft.yml b/.github/workflows/create_release_draft.yml index efefb0fd0c7..fb71e35d2ff 100644 --- a/.github/workflows/create_release_draft.yml +++ b/.github/workflows/create_release_draft.yml @@ -73,7 +73,7 @@ jobs: sha256sum ${{ env.GRASS }}.tar.xz > ${{ env.GRASS }}.tar.xz.sha256 - name: Publish draft distribution to GitHub (for tags only) if: startsWith(github.ref, 'refs/tags/') - uses: softprops/action-gh-release@c062e08bd532815e2082a85e87e3ef29c3e6d191 # v2.0.8 + uses: softprops/action-gh-release@e7a8f85e1c67a31e6ed99a94b41bd0b71bbee6b8 # v2.0.9 with: name: GRASS GIS ${{ github.ref_name }} body: | From 3f0b69fdf56879fcd35d36da51c0227765948dca Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 31 Oct 2024 17:23:31 -0400 Subject: [PATCH 471/471] CI(deps): Update docker/dockerfile Docker tag to v1.11 (#4621) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Dockerfile | 2 +- docker/ubuntu/Dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index a5c6ab1ee1d..6b928fb46c9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -# syntax=docker/dockerfile:1.10@sha256:865e5dd094beca432e8c0a1d5e1c465db5f998dca4e439981029b3b81fb39ed5 +# syntax=docker/dockerfile:1.11@sha256:1f2be5a2aa052cbd9aedf893d17c63277c3d1c51b3fb0f3b029c6b34f658d057 # Note: This file must be kept in sync in ./Dockerfile and ./docker/ubuntu/Dockerfile. # Changes to this file must be copied over to the other file. diff --git a/docker/ubuntu/Dockerfile b/docker/ubuntu/Dockerfile index a5c6ab1ee1d..6b928fb46c9 100644 --- a/docker/ubuntu/Dockerfile +++ b/docker/ubuntu/Dockerfile @@ -1,4 +1,4 @@ -# syntax=docker/dockerfile:1.10@sha256:865e5dd094beca432e8c0a1d5e1c465db5f998dca4e439981029b3b81fb39ed5 +# syntax=docker/dockerfile:1.11@sha256:1f2be5a2aa052cbd9aedf893d17c63277c3d1c51b3fb0f3b029c6b34f658d057 # Note: This file must be kept in sync in ./Dockerfile and ./docker/ubuntu/Dockerfile. # Changes to this file must be copied over to the other file.