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/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..25bfafdf865 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 @@ -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@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@b611370bb5703a7efb587f9d136a52ea24c5c38c # v3.25.11 + uses: github/codeql-action/analyze@2d790406f505036ef40ecba973cc774a50395aac # v3.25.13 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/create_release_draft.yml b/.github/workflows/create_release_draft.yml index 11f806e2f55..d43e1248555 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 @@ -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@c062e08bd532815e2082a85e87e3ef29c3e6d191 # v2.0.8 with: name: GRASS GIS ${{ github.ref_name }} body: | diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index a10ec7545a0..041c4dffa2b 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -66,17 +66,17 @@ 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@4fd812986e6c8c2a69e18311145f9371337f27d4 # v3.4.0 + uses: docker/setup-buildx-action@aa33708b10e362ff993539393ff100fa93ed6a27 # v3.5.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 }} - name: Build and push id: docker_build - uses: docker/build-push-action@1a162644f9a7e87d8f4b053101d1d9a712edc18c # v6.3.0 + uses: docker/build-push-action@5176d81f87c23d6fc96624dfdbcd9f3830bbe445 # v6.5.0 with: push: true pull: true diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 3a91534a80b..70ad658265f 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 @@ -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/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index 802dc18b227..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.1" + RUFF_VERSION: "0.5.4" runs-on: ${{ matrix.os }} permissions: @@ -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 @@ -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@2d790406f505036ef40ecba973cc774a50395aac # v3.25.13 with: sarif_file: bandit.sarif diff --git a/.github/workflows/test-nix.yml b/.github/workflows/test-nix.yml index 42b5eee93a5..a73ac835414 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: @@ -25,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 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/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8146d2b1a9c..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.1 + rev: v0.5.4 hooks: # Run the linter. - id: ruff 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/display/d.text/test.py b/display/d.text/test.py index ffc50cfc8aa..6e7fe676a65 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 @@ -55,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))]) @@ -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/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) 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 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. diff --git a/flake.lock b/flake.lock index ff511760ebb..c30f9b90775 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": 1720955038, + "narHash": "sha256-GaliJqfFwyYxReFywxAa8orCO+EnDq2NK2F+5aSc8vo=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "e6cea36f83499eb4e9cd184c8a8e823296b50ad5", + "rev": "aa247c0c90ecf4ae7a032c54fdc21b91ca274062", "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/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 3e914e68112..1a920f77de7 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): @@ -119,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() @@ -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: @@ -557,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 @@ -595,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..437bba7946f 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 @@ -288,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/animation/temporal_manager.py b/gui/wxpython/animation/temporal_manager.py index 4c1816fea19..2273ccfd9d6 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)." @@ -296,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/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/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/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/globalvar.py b/gui/wxpython/core/globalvar.py index 74138058ee6..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(): @@ -230,11 +225,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 40cd82defc2..021f3aa6500 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 @@ -263,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""" @@ -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""" @@ -725,10 +723,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(): @@ -882,8 +879,8 @@ def _projInfo(self): for line in ret.splitlines(): if ":" in line: - key, val = map(lambda x: x.strip(), line.split(":", 1)) - if key in {"units"}: + key, val = (x.strip() for x in line.split(":", 1)) + if key == "units": val = val.lower() projinfo[key] = val elif "XY location (unprojected)" in line: @@ -908,7 +905,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 a4f2b60e3fd..45620220aed 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:] @@ -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) @@ -1221,14 +1224,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/core/toolboxes.py b/gui/wxpython/core/toolboxes.py index c10167b639a..2fca91f8a90 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 @@ -82,7 +83,7 @@ def getMessages(): def clearMessages(): - del _MESSAGES[:] + _MESSAGES.clear() def _debug(level, message): @@ -346,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): @@ -846,8 +846,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/core/treemodel.py b/gui/wxpython/core/treemodel.py index 4c5de36f701..dd84fed7bf9 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 @@ -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): @@ -291,7 +287,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 = {} @@ -322,11 +318,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/core/utils.py b/gui/wxpython/core/utils.py index fd6803ecf74..68242f04984 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" @@ -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 [] @@ -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 @@ -840,10 +840,10 @@ 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 = 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") @@ -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(): @@ -1179,8 +1176,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) @@ -1188,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/core/watchdog.py b/gui/wxpython/core/watchdog.py index 06cf8f3d4a5..9315a1010d6 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( @@ -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/core/workspace.py b/gui/wxpython/core/workspace.py index 12fd08247a9..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""" @@ -1757,7 +1753,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/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/datacatalog/tree.py b/gui/wxpython/datacatalog/tree.py index 542282802be..9ea22c1c327 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"]: @@ -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""" @@ -1294,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"] @@ -2092,10 +2090,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/gui/wxpython/dbmgr/base.py b/gui/wxpython/dbmgr/base.py index abef5b8daec..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): """ @@ -2165,11 +2162,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 cee0ff416f3..afc3b239037 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": ""} @@ -122,7 +120,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 @@ -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) @@ -978,7 +970,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 @@ -1460,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" @@ -1616,7 +1607,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() @@ -1792,7 +1783,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 +1795,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): """ @@ -1868,7 +1858,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 +2726,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 @@ -2909,10 +2899,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 @@ -3366,9 +3360,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 @@ -3545,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/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..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 @@ -493,7 +490,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 +692,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 +732,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 = [] @@ -822,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""" @@ -949,12 +941,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 @@ -1042,10 +1032,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 @@ -2029,9 +2016,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""" @@ -2344,8 +2329,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""" @@ -2668,9 +2652,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 +3481,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") @@ -3797,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/dialogs.py b/gui/wxpython/gui_core/dialogs.py index 6c0c75f5507..05bb71ed7fa 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: @@ -1331,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: @@ -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()) @@ -2334,7 +2332,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/gui_core/forms.py b/gui/wxpython/gui_core/forms.py index 95cbf55d7e9..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", "")} @@ -2490,7 +2486,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 @@ -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) @@ -3062,8 +3055,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/ghelp.py b/gui/wxpython/gui_core/ghelp.py index 8366e464a66..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) @@ -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/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 bed849f0c26..215a7bba23d 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 @@ -754,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() @@ -2291,8 +2289,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 +2319,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) @@ -2335,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) @@ -2461,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) @@ -2888,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: @@ -3041,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() @@ -3082,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 7c56a7f7c07..9fa75fa74c1 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,9 +301,7 @@ def _openFile(self, file_path): :return str or None: file content or None """ try: - with open(file_path, "r") as f: - content = f.read() - return content + return Path(file_path).read_text() except PermissionError: GError( message=_( @@ -328,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=_( @@ -516,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/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/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/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/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/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/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/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/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/history/tree.py b/gui/wxpython/history/tree.py index 2ab158c45c5..98ab8eb0994 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() @@ -246,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 = ( @@ -279,7 +274,7 @@ def _initHistoryModel(self): data={ "type": COMMAND, "name": entry["command"].strip(), - "timestamp": timestamp if timestamp else None, + "timestamp": timestamp or None, "status": status, }, ) @@ -545,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 691c1e75080..85334993db1 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 @@ -161,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 3a8626a76ee..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 @@ -857,8 +853,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 @@ -929,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 @@ -958,9 +953,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. @@ -1305,10 +1299,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/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_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 2367c053748..b4bc3d60f97 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": ""} @@ -162,7 +160,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 @@ -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) @@ -974,7 +966,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 @@ -1443,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" @@ -1640,7 +1631,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 +1812,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 +2673,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 @@ -2855,12 +2846,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 @@ -3314,9 +3309,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 @@ -3465,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/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 9930364c1b3..08afe5906ed 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: @@ -564,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: @@ -573,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): @@ -710,8 +708,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 752f50bf597..ddb91789508 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) @@ -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..e2204555415 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 @@ -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)) @@ -2428,7 +2424,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 +2432,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/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/location_wizard/wizard.py b/gui/wxpython/location_wizard/wizard.py index f4a4cc766ef..5a206fafa52 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 @@ -300,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 = _( @@ -743,8 +734,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 @@ -811,11 +801,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): @@ -911,19 +900,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""" @@ -1497,9 +1485,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() @@ -1574,9 +1561,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): @@ -2168,9 +2154,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): @@ -2540,14 +2525,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 @@ -2563,7 +2541,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 +2564,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 +2581,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 +2599,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 +2615,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(":") @@ -2795,9 +2773,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 361111d389f..6b9a9be640f 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): @@ -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) @@ -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/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 3c819c2d765..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""" @@ -637,20 +635,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 +810,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 +926,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/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/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/buffered.py b/gui/wxpython/mapwin/buffered.py index ad7bcd986a1..e0442ed94ae 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( @@ -1123,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 @@ -1526,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/mapwin/decorations.py b/gui/wxpython/mapwin/decorations.py index 9e2051a435d..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,18 +205,15 @@ 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("=") if len(param) == 1: inputs += 1 - else: - if param[0] == "text" and len(param) == 2: - inputs += 1 - if inputs >= 1: - return True - return False + elif param[0] == "text" and len(param) == 2: + inputs += 1 + return inputs >= 1 class BarscaleController(OverlayController): @@ -312,20 +309,17 @@ 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("=") 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 - if inputs == 1: - return True - return False + elif param[0] == "raster" and len(param) == 2: + inputs += 1 + elif param[0] == "raster_3d" and len(param) == 2: + inputs += 1 + 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 6feeb22cab2..0772dc7f9d5 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() @@ -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( @@ -1695,7 +1689,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 +1803,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 @@ -1901,12 +1894,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): @@ -1939,17 +1932,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 2b83bd8e040..f7822e0cb0e 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", "") @@ -386,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 3d282cc3c14..bbd6bcaa3bb 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": @@ -569,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) @@ -2079,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") # @@ -2491,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 1814772839c..02341420cc3 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") @@ -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""" @@ -4044,7 +4038,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(): @@ -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/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/photo2image/ip2i_manager.py b/gui/wxpython/photo2image/ip2i_manager.py index 10439b7b390..b4d24d19430 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 @@ -123,7 +119,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 @@ -806,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: @@ -963,7 +959,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() @@ -1977,10 +1973,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 @@ -2402,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 @@ -2529,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 6094c805e60..740678de3cb 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() @@ -2086,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() @@ -3200,8 +3197,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 @@ -3979,7 +3975,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) @@ -3991,7 +3987,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) @@ -4412,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""" @@ -4905,13 +4899,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 650d970b10d..8725a0a957d 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: @@ -571,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() @@ -724,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: @@ -1691,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. @@ -2614,9 +2612,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 a123baaaba4..f52bad96069 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): @@ -1914,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""" @@ -2085,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/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/rdigit/g.gui.rdigit.py b/gui/wxpython/rdigit/g.gui.rdigit.py index 0a9a72e4acb..a05fdb547bb 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) @@ -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/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/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/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/startup/guiutils.py b/gui/wxpython/startup/guiutils.py index baa74fd6b68..fa4d0a8760a 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( @@ -586,7 +585,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"] @@ -716,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/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/timeline/frame.py b/gui/wxpython/timeline/frame.py index b8b4fa3c2fd..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): @@ -572,9 +569,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): @@ -630,10 +625,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/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/tplot/frame.py b/gui/wxpython/tplot/frame.py index 5a035657455..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): @@ -739,11 +736,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: @@ -1268,7 +1264,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 +1272,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) @@ -1368,8 +1364,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/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..b747f3fa44f 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) @@ -457,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 @@ -465,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 @@ -557,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 @@ -565,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 @@ -878,10 +871,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 +917,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 +989,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 c82bc25a157..3d829ffd7be 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..."), @@ -1144,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_core.py b/gui/wxpython/vnet/vnet_core.py index 97f40458008..17d85ef6ca4 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) @@ -1027,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 f7803439e60..a1332398a1e 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: @@ -667,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"]) @@ -1073,7 +1071,7 @@ def GetLastModified(self): ) try: head = open(headPath, "r") - for line in head.readlines(): + for line in head: i = line.find( "MAP DATE:", ) @@ -1202,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 @@ -1253,8 +1250,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 @@ -1275,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 @@ -1300,7 +1296,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(): @@ -1468,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 2da957ef948..67d000d75df 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): @@ -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) @@ -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/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/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..f8319747dab 100644 --- a/gui/wxpython/wxplot/histogram.py +++ b/gui/wxpython/wxplot/histogram.py @@ -145,17 +145,16 @@ 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 # 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 1941022fb59..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) @@ -472,7 +470,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/imagery/i.atcorr/create_iwave.py b/imagery/i.atcorr/create_iwave.py index 5fc1fa78559..243e7a22542 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 " @@ -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/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); 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; diff --git a/lib/init/grass.py b/lib/init/grass.py index 0a3dfe26434..1231e299049 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 @@ -732,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): @@ -872,103 +868,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( @@ -1947,7 +1936,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")) @@ -1990,7 +1979,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( @@ -2438,25 +2427,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, @@ -2520,7 +2508,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/man/build_class_graphical.py b/man/build_class_graphical.py index 31caffcdf75..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): @@ -128,7 +126,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/man/build_manual_gallery.py b/man/build_manual_gallery.py index 10fdb143989..3d381e27877 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 @@ -93,14 +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)) - with open(filename) as file: - if re.search(pattern, file.read()): - return True - return False + return bool(re.search(pattern, Path(filename).read_text())) def file_matches(filename, patterns): @@ -123,8 +121,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/man/parser_standard_options.py b/man/parser_standard_options.py index 554c0846203..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 @@ -138,22 +137,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): @@ -218,7 +217,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/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 diff --git a/pyproject.toml b/pyproject.toml index f7cc89a7257..574b99df4cb 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 @@ -108,8 +107,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 "D1", @@ -147,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 @@ -155,24 +151,7 @@ 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. - "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 - "FURB129", # readlines-in-for - "FURB131", # delete-full-slice - "FURB136", # if-expr-min-max - "FURB140", # reimplemented-starmap - "FURB142", # for-loop-set-mutations - "FURB148", # unnecessary-enumerate - "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 @@ -202,11 +181,8 @@ 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 - "PLR2044", # empty-comment - "PLR5501", # collapsible-else-if "PLR6104", # non-augmented-assignment "PLR6201", # literal-membership "PLR6301", # no-self-use @@ -251,7 +227,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 @@ -286,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 @@ -298,7 +279,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 @@ -314,7 +294,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 ] @@ -371,7 +350,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" = ["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"] @@ -383,14 +362,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"] @@ -406,7 +382,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/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/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/checks.py b/python/grass/grassdb/checks.py index c1ef7cc76c2..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): @@ -605,9 +599,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/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/grassdb/history.py b/python/grass/grassdb/history.py index 2657632c9e1..60b5459700d 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): @@ -86,8 +85,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( @@ -105,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) @@ -164,7 +159,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 @@ -176,7 +171,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( @@ -186,7 +181,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 @@ -216,7 +211,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 @@ -276,14 +271,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/checkers.py b/python/grass/gunittest/checkers.py index 80ee38e5fdc..fb46a1deb66 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 @@ -496,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.", @@ -538,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 7c1f2248089..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 @@ -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" @@ -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/gunittest/invoker.py b/python/grass/gunittest/invoker.py index 2b2de7f5e40..f07e8dd5a84 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,16 +242,14 @@ 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)) + 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( @@ -337,18 +334,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 e5455c33b39..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 @@ -295,7 +296,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 +312,6 @@ def get_html_test_authors_table(directory, tests_authors): not_testing=", ".join(sorted(not_testing_authors)), ) ) - return test_authors class GrassTestFilesMultiReporter: @@ -907,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 @@ -1026,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}") @@ -1117,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 @@ -1212,7 +1209,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 ( "" '{d}{status}' "{nfiles}{sfiles}{pfiles}" @@ -1231,7 +1228,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/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/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/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/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) diff --git a/python/grass/jupyter/baseseriesmap.py b/python/grass/jupyter/baseseriesmap.py index e28572e1f3e..db915297e9c 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 @@ -73,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 @@ -177,8 +177,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/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/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/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..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), ) @@ -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/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/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..0356513e017 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(): @@ -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: @@ -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/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/raster/category.py b/python/grass/pygrass/raster/category.py index 04049754954..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 @@ -299,7 +300,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 @@ -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/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/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/shell/show.py b/python/grass/pygrass/shell/show.py index 94b0807debe..8465dc9e92d 100644 --- a/python/grass/pygrass/shell/show.py +++ b/python/grass/pygrass/shell/show.py @@ -4,8 +4,8 @@ @author: pietro """ +from pathlib import Path + def raw_figure(figpath): - with open(figpath, mode="rb") as data: - res = data.read() - return res + return Path(figpath).read_bytes() diff --git a/python/grass/pygrass/utils.py b/python/grass/pygrass/utils.py index 02719c3730f..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): @@ -332,7 +330,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: @@ -348,7 +346,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..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} # ============================================= @@ -299,7 +296,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 +355,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 +393,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, @@ -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/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..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 @@ -792,7 +791,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,12 +1375,11 @@ 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: - 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 @@ -1497,7 +1495,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 +1698,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 +1796,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 +1818,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 +1895,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 +1943,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/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/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 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/core.py b/python/grass/script/core.py index 6647661b379..d46c35d3352 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): @@ -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"] @@ -985,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): @@ -1170,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 @@ -1197,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. @@ -1205,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): @@ -1286,8 +1278,8 @@ 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)) + for line in fd: + key, value = (x.strip() for x in line.split(":", 1)) if kwargs and key not in {"proj", "zone"}: continue if ( @@ -1571,13 +1563,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/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/python/grass/script/task.py b/python/grass/script/task.py index 66f7667f5e4..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( @@ -429,8 +428,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 @@ -448,15 +446,12 @@ 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 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): @@ -509,13 +504,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/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/python/grass/script/utils.py b/python/grass/script/utils.py index 57a3f3cd2de..13d049412a4 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 @@ -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/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/semantic_label/reader.py b/python/grass/semantic_label/reader.py index bd19af69110..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( @@ -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 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/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 68a8c5498e9..890061ee64d 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): @@ -590,7 +587,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 +963,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 @@ -1554,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: @@ -2154,7 +2156,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(): @@ -2406,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/python/grass/temporal/base.py b/python/grass/temporal/base.py index a275fa1353f..9296a22dfa1 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: @@ -314,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, @@ -343,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 @@ -741,7 +737,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..0df95106e0f 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 @@ -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) @@ -1279,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: @@ -1414,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 7ed3c7fb8f5..3d4b2869854 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: @@ -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/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/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/mapcalc.py b/python/grass/temporal/mapcalc.py index b3db20eef98..5f29cd32747 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: @@ -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/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..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 @@ -183,29 +183,22 @@ 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 - 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 @@ -285,16 +272,16 @@ 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 - new = SpatialExtent( + return SpatialExtent( north=nN, south=nS, east=nE, @@ -303,7 +290,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 @@ -396,9 +382,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) @@ -450,16 +436,16 @@ 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 - new = SpatialExtent( + return SpatialExtent( north=nN, south=nS, east=nE, @@ -468,7 +454,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 @@ -584,9 +569,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) @@ -594,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. @@ -638,18 +623,9 @@ def is_in_2d(self, extent): eE -= 360.0 eW -= 360.0 - if W <= eW: - return False - if E >= eE: - return False - if N >= eN: - return False - if S <= eS: - return False - - return True + return not (eW >= W or eE <= E or eN <= N or eS >= S) - 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. @@ -680,12 +656,7 @@ def is_in(self, extent): T = self.get_top() B = self.get_bottom() - if B <= eB: - return False - if T >= eT: - 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 @@ -731,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. @@ -778,18 +749,9 @@ def equivalent_2d(self, extent): eE -= 360.0 eW -= 360.0 - if W != eW: - return False - if E != eE: - return False - if N != eN: - return False - if S != eS: - return False - - return True + return not (eW != W or eE != E or eN != N or eS != S) - 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. @@ -821,14 +783,9 @@ def equivalent(self, extent): T = self.get_top() B = self.get_bottom() - if B != eB: - return False - if T != eT: - 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. @@ -894,36 +851,33 @@ 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: - 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. @@ -976,46 +930,33 @@ def cover(self, extent): eW -= 360.0 # Edges of extent located outside of self are not allowed - if E <= eW: - return False - if W >= eE: - return False - if N <= eS: - return False - if S >= eN: - return False - if T <= eB: - return False - if B >= eT: + 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 - 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: - 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 @@ -1049,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 @@ -1097,29 +1038,22 @@ 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 - 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. @@ -1159,33 +1093,22 @@ 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 - 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 @@ -1244,16 +1167,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 @@ -1262,17 +1185,17 @@ def meet_2d(self, extent): return False # Check boundaries of the faces - if edge == "E" or edge == "W": - if N < eS or S > eN: + if edge in {"E", "W"}: + if eS > N or eN < S: return False - if edge == "N" or edge == "S": - if E < eW or W > eE: + if edge in {"N", "S"}: + if eW > E or eE < W: return False 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. @@ -1308,22 +1231,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 @@ -1332,27 +1255,27 @@ def meet(self, extent): return False # Check boundaries of the faces - if edge == "E" or edge == "W": - if N < eS or S > eN: + if edge in {"E", "W"}: + if eS > N or eN < S: return False - if T < eB or B > eT: + if eB > T or eT < B: return False - if edge == "N" or edge == "S": - if E < eW or W > eE: + if edge in {"N", "S"}: + if eW > E or eE < W: return False - if T < eB or B > eT: + if eB > T or eT < B: return False - if edge == "T" or edge == "B": - if E < eW or W > eE: + if edge in {"T", "B"}: + if eW > E or eE < W: return False - if N < eS or S > eN: + if eS > N or eN < S: return False 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. @@ -1370,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 @@ -1401,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 @@ -1806,7 +1703,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_algebra.py b/python/grass/temporal/temporal_algebra.py index 6bf96eea68e..c2546ec0d9e 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=[] @@ -1728,8 +1725,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: @@ -1872,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 @@ -2007,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 @@ -2197,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 @@ -2250,7 +2240,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: @@ -2260,12 +2250,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: @@ -2275,7 +2265,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()) @@ -2286,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. @@ -2337,9 +2325,7 @@ def recurse_compare(conditionlist): conditionlist[ele_index - 2] = result recurse_compare(conditionlist) - resultlist = conditionlist - - return resultlist + return conditionlist resultlist = [] inverselist = [] @@ -2550,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: @@ -3255,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_extent.py b/python/grass/temporal/temporal_extent.py index b3ce5714c3b..42cce4599b1 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,12 +423,12 @@ 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) - 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 c23dd701c5c..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): @@ -1000,14 +994,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 +1023,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 +1046,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 +1066,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: @@ -1228,11 +1231,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_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_raster_base_algebra.py b/python/grass/temporal/temporal_raster_base_algebra.py index 4207e251b38..02e28e0e435 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, @@ -464,8 +462,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( @@ -637,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, @@ -740,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 @@ -934,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/python/grass/temporal/temporal_vector_algebra.py b/python/grass/temporal/temporal_vector_algebra.py index a5c5bc27533..d8e8474f26e 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() @@ -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/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/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 ================= 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.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 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.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/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.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( 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; 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": { diff --git a/renovate.json5 b/renovate.json5 index f0d45a3e2eb..b5c0aeba9b8 100644 --- a/renovate.json5 +++ b/renovate.json5 @@ -19,5 +19,37 @@ // Renovate's pre-commit support is still opt-in ":enablePreCommit", - ] + + // weekly update of lock files (flake.lock) + ":maintainLockFilesWeekly", + "group:githubArtifactActions", + ], + + // enable Nix lock file update (flake.lock) + "nix": { + "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", + ], + }, + ], } diff --git a/scripts/d.frame/d.frame.py b/scripts/d.frame/d.frame.py index 2d75457bee0..e9b44b21650 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() @@ -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/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] 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 0e7d9296b57..fb399db9d06 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): @@ -667,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): @@ -722,11 +719,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()) @@ -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 @@ -1292,8 +1290,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 @@ -1796,16 +1793,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): @@ -1898,7 +1893,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 @@ -1998,7 +1993,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() @@ -2076,7 +2071,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"]: @@ -2195,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) @@ -2593,7 +2587,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/scripts/g.extension/testsuite/test_addons_download.py b/scripts/g.extension/testsuite/test_addons_download.py index e5077f9894e..c4618306d12 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): @@ -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/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/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/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.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.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 e88b2fe1886..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): @@ -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.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( 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/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/r.tileset/r.tileset.py b/scripts/r.tileset/r.tileset.py index ac5cd5c07a0..f5e7ab17b13 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 @@ -213,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"]) @@ -229,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(): @@ -344,8 +345,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/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.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.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/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.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/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/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/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) 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.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.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__": 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.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/temporal/t.vect.db.select/t.vect.db.select.py b/temporal/t.vect.db.select/t.vect.db.select.py index 96b76f8361b..a8f2490749e 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 @@ -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/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 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() 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 2ccac59ae8f..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): @@ -113,7 +114,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 +142,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 +199,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)) @@ -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/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/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 737f4698ddd..210e83b3fd6 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 @@ -406,23 +407,18 @@ 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") - 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] @@ -517,9 +513,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 Path(name).read_text() except OSError: return "" @@ -777,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) @@ -928,7 +921,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": 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 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] )