Skip to content

Commit

Permalink
Merge branch 'main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
gregorywaynepower authored Dec 15, 2024
2 parents 8baa0c4 + 5e27d1a commit 37c6051
Show file tree
Hide file tree
Showing 19 changed files with 388 additions and 24 deletions.
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ dist.*
!.git/refs/heads
!.git/objects
.git/objects/*
!.git/objects/pack
3 changes: 0 additions & 3 deletions .flake8
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,6 @@ per-file-ignores =
# TODO: Is this really needed?
python/grass/pygrass/vector/__init__.py: E402
python/grass/pygrass/vector/__init__.py: E402
python/grass/temporal/abstract_space_time_dataset.py: E722
python/grass/temporal/c_libraries_interface.py: E722
python/grass/temporal/core.py: E722
python/grass/temporal/datetime_math.py: E722
python/grass/temporal/spatial_topology_dataset_connector.py: E722
python/grass/temporal/temporal_algebra.py: E722
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ jobs:
if: ${{ matrix.language == 'c-cpp' }}

- name: Initialize CodeQL
uses: github/codeql-action/init@babb554ede22fd5605947329c4d04d8e7a0b8155 # v3.27.7
uses: github/codeql-action/init@df409f7d9260372bd5f19e5b04e83cb3c43714ae # v3.27.9
with:
languages: ${{ matrix.language }}
config-file: ./.github/codeql/codeql-config.yml
Expand All @@ -81,6 +81,6 @@ jobs:
run: .github/workflows/build_ubuntu-22.04.sh "${HOME}/install"

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@babb554ede22fd5605947329c4d04d8e7a0b8155 # v3.27.7
uses: github/codeql-action/analyze@df409f7d9260372bd5f19e5b04e83cb3c43714ae # v3.27.9
with:
category: "/language:${{matrix.language}}"
2 changes: 1 addition & 1 deletion .github/workflows/create_release_draft.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,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@01570a1f39cb168c169c802c3bceb9e93fb10974 # v2.1.0
uses: softprops/action-gh-release@7b4da11513bf3f43f9999e90eabced41ab8bb048 # v2.2.0
with:
name: GRASS GIS ${{ github.ref_name }}
body: |
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/python-code-quality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:
# renovate: datasource=pypi depName=bandit
BANDIT_VERSION: "1.8.0"
# renovate: datasource=pypi depName=ruff
RUFF_VERSION: "0.8.2"
RUFF_VERSION: "0.8.3"

runs-on: ${{ matrix.os }}
permissions:
Expand Down Expand Up @@ -135,7 +135,7 @@ jobs:
path: bandit.sarif

- name: Upload SARIF File into Security Tab
uses: github/codeql-action/upload-sarif@babb554ede22fd5605947329c4d04d8e7a0b8155 # v3.27.7
uses: github/codeql-action/upload-sarif@df409f7d9260372bd5f19e5b04e83cb3c43714ae # v3.27.9
with:
sarif_file: bandit.sarif

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/super-linter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
# list of files that changed across commits
fetch-depth: 0
- name: Lint code base
uses: super-linter/super-linter/slim@e1cb86b6e8d119f789513668b4b30bf17fe1efe4 # v7.2.0
uses: super-linter/super-linter/slim@85f7611e0f7b53c8573cca84aa0ed4344f6f6a4d # v7.2.1
env:
DEFAULT_BRANCH: main
# To report GitHub Actions status checks
Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ repos:
)
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.8.2
rev: v0.8.3
hooks:
# Run the linter.
- id: ruff
Expand Down
2 changes: 1 addition & 1 deletion docker/alpine/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM alpine:3.20@sha256:1e42bbe2508154c9126d48c2b8a75420c3544343bf86fd041fb7527e017a4b4a as common
FROM alpine:3.21@sha256:21dc6063fd678b478f57c0e13f47560d0ea4eeba26dfc947b2a4f81f686b9f45 as common

# Based on:
# https://github.com/mundialis/docker-grass-gis/blob/master/Dockerfile
Expand Down
3 changes: 1 addition & 2 deletions gui/wxpython/core/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -849,8 +849,7 @@ def StoreEnvVariable(key, value=None, envFile=None):

# update environmental variables
if value is None:
if key in environ:
del environ[key]
environ.pop(key, None)
else:
environ[key] = value

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,7 @@ ignore = [

[tool.pytest.ini_options]
minversion = "6.0"
python_files = "*/tests/*_test.py"
python_files = "*/tests/*_test.py */tests/test_*.py"
addopts = """
--ignore-glob='dist.*'
--ignore-glob='bin.*'
Expand Down
3 changes: 1 addition & 2 deletions python/grass/pydispatch/signal.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,8 +266,7 @@ def __call__(self, *args, **kwargs):
Traceback (most recent call last):
TypeError: mywrite() takes exactly 1 argument (0 given)
"""
if "signal" in kwargs:
del kwargs["signal"]
kwargs.pop("signal", None)
self.emit(*args, **kwargs)


Expand Down
2 changes: 1 addition & 1 deletion python/grass/script/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ include $(MODULE_TOPDIR)/include/Make/Python.make

DSTDIR = $(ETC)/python/grass/script

MODULES = core db raster raster3d vector array setup task utils
MODULES = core db imagery raster raster3d vector array setup task utils

PYFILES := $(patsubst %,$(DSTDIR)/%.py,$(MODULES) __init__)
PYCFILES := $(patsubst %,$(DSTDIR)/%.pyc,$(MODULES) __init__)
Expand Down
2 changes: 2 additions & 0 deletions python/grass/script/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
db_table_exist,
db_table_in_vector,
)
from .imagery import group_to_dict
from .raster import mapcalc, mapcalc_start, raster_history, raster_info, raster_what
from .raster3d import mapcalc3d, raster3d_info
from .utils import (
Expand Down Expand Up @@ -146,6 +147,7 @@
"get_raise_on_error",
"get_real_command",
"gisenv",
"group_to_dict",
"handle_errors",
"info",
"legal_name",
Expand Down
150 changes: 150 additions & 0 deletions python/grass/script/imagery.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
"""
Imagery related functions to be used in Python scripts.
Usage:
::
import grass.script as gs
gs.imagery.group_to_dict(imagery_group)
...
(C) 2024 by Stefan Blumentrath and the GRASS Development Team
This program is free software under the GNU General Public
License (>=v2). Read the file COPYING that comes with GRASS
for details.
.. sectionauthor:: Stefan Blumentrath
"""

from .core import read_command, warning, fatal
from .raster import raster_info


def group_to_dict(
imagery_group_name,
subgroup=None,
dict_keys="semantic_labels",
dict_values="map_names",
fill_semantic_label=True,
env=None,
):
"""Create a dictionary to represent an imagery group with metadata.
Depending on the dict_keys option, the returned dictionary uses either
the names of the raster maps ("map_names"), their row indices in the group
("indices") or their associated semantic_labels ("semantic_labels") as keys.
The default is to use semantic_labels. Note that map metadata
of the maps in the group have to be read to get the semantic label,
in addition to the group file. The same metadata is read when the
"metadata" is requested as dict_values. Other supported dict_values
are "map_names" (default), "semantic_labels", or "indices".
The function can also operate on the level of subgroups. In case a
non-existing (or empty sub-group) is requested a warning is printed
and an empty dictionary is returned (following the behavior of i.group).
Example::
>>> run_command("g.copy", raster="lsat7_2000_10,lsat7_2000_10")
>>> run_command("r.support", raster="lsat7_2000_10", semantic_label="L8_1")
>>> run_command("g.copy", raster="lsat7_2000_20,lsat7_2000_20")
>>> run_command("r.support", raster="lsat7_2000_20", semantic_label="L8_2")
>>> run_command("g.copy", raster="lsat7_2000_30,lsat7_2000_30")
>>> run_command("r.support", raster="lsat7_2000_30", semantic_label="L8_3")
>>> run_command("i.group", group="L8_group",
>>> input="lsat7_2000_10,lsat7_2000_20,lsat7_2000_30")
>>> group_to_dict("L8_group") # doctest: +ELLIPSIS
{"L8_1": "lsat7_2000_10", ... "L8_3": "lsat7_2000_30"}
>>> run_command("g.remove", flags="f", type="group", name="L8_group")
>>> run_command("g.remove", flags="f", type="raster",
>>> name="lsat7_2000_10,lsat7_2000_20,lsat7_2000_30")
:param str imagery_group_name: Name of the imagery group to process (or None)
:param str subgroup: Name of the imagery sub-group to process (or None)
:param str dict_keys: What to use as key for dictionary. It can be either
"semantic_labels" (default), "map_names" or "indices"
:param str dict_values: What to use as values for dictionary. It can be either
"map_names" (default), "semanic_labels", "indices" or
"metadata" (to return dictionaries with full map metadata)
:param bool fill_semantic_label: If maps in a group do not have a semantic
label, their index in the group is used
instead (default). Otherwise None / "none"
is used.
:param dict env: Environment to use when parsing the imagery group
:return: dictionary representing an imagery group with it's maps and their
semantic labels, row indices in the group, or metadata
:rtype: dict
"""
group_dict = {}
maps_in_group = (
read_command(
"i.group",
group=imagery_group_name,
subgroup=subgroup,
flags="g",
quiet=True,
env=env,
)
.strip()
.split()
)

if dict_keys not in {"indices", "map_names", "semantic_labels"}:
raise ValueError(f"Invalid dictionary keys <{dict_keys}> requested")

if dict_values not in {"indices", "map_names", "semantic_labels", "metadata"}:
raise ValueError(f"Invalid dictionary values <{dict_values}> requested")

if subgroup and not maps_in_group:
warning(
_("Empty result returned for subgroup <{sg}> in group <{g}>").format(
sg=subgroup, g=imagery_group_name
)
)

for idx, raster_map in enumerate(maps_in_group):
raster_map_info = None
# Get raster metadata if needed
if (
dict_values in {"semantic_labels", "metadata"}
or dict_keys == "semantic_labels"
):
raster_map_info = raster_info(raster_map, env=env)

# Get key for dictionary
if dict_keys == "indices":
key = str(idx + 1)
elif dict_keys == "map_names":
key = raster_map
elif dict_keys == "semantic_labels":
key = raster_map_info["semantic_label"]
if not key or key == '"none"':
if fill_semantic_label:
key = str(idx + 1)
else:
fatal(
_(
"Semantic label missing for raster map {m} in group <{g}>."
).format(m=raster_map, g=imagery_group_name)
)

if dict_values == "indices":
val = str(idx + 1)
elif dict_values == "map_names":
val = raster_map
elif dict_values == "semantic_labels":
val = raster_map_info["semantic_label"]
elif dict_values == "metadata":
val = raster_map_info
if key in group_dict:
warning(
_(
"Key {k} from raster map {m} already present in group dictionary."
"Overwriting existing entry..."
).format(k=key, r=raster_map)
)
group_dict[key] = val
return group_dict
47 changes: 45 additions & 2 deletions python/grass/script/tests/test_script_task.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,54 @@
import os

import pytest

import grass.script as gs
from grass.script.task import grassTask


def test_mapcalc_simple_e_name():
@pytest.fixture
def xy_session_patched_env(tmp_path, monkeypatch):
"""Active session in an XY location (scope: function), patching env vars directly.
This allows functions not accepting an env dictionary argument to work in tests"""
location = "xy_test"
gs.core._create_location_xy(tmp_path, location) # pylint: disable=protected-access
with gs.setup.init(tmp_path / location, env=os.environ.copy()) as session:
for key, value in session.env.items():
monkeypatch.setenv(key, value)
yield session


def test_mapcalc_simple_e_name(xy_session_patched_env):
gt = grassTask("r.mapcalc.simple")
assert gt.get_param("e")["name"] == "e"


def test_mapcalc_simple_expession_name():
def test_mapcalc_simple_expression_name(xy_session_patched_env):
gt = grassTask("r.mapcalc.simple")
assert gt.get_param("expression")["name"] == "expression"


def test_d_vect_from_bin(xy_session_patched_env):
"""Tests that a module installed in "$GISBASE/bin can be used with grassTask"""
task = grassTask("d.vect")
task.get_param("map")["value"] = "map_name"
task.get_flag("i")["value"] = True
task.get_param("layer")["value"] = 1
task.get_param("label_bcolor")["value"] = "red"
# the default parameter display is added automatically
actual = " ".join(task.get_cmd())
expected = "d.vect -i map=map_name layer=1 display=shape label_bcolor=red"
assert actual == expected


def test_v_clip_from_scripts(xy_session_patched_env):
"""Tests that a module installed in "$GISBASE/scripts can be used with grassTask"""
task = grassTask("v.clip")
task.get_param("input")["value"] = "map_name"
task.get_flag("r")["value"] = True
task.get_param("clip")["value"] = "clip_map_name"
task.get_param("output")["value"] = "output_map_name"
actual = " ".join(task.get_cmd())
expected = "v.clip -r input=map_name clip=clip_map_name output=output_map_name"
assert actual == expected
Loading

0 comments on commit 37c6051

Please sign in to comment.