Skip to content

Commit

Permalink
Fix warnings and raise UserWarning as error in CI (#458)
Browse files Browse the repository at this point in the history
  • Loading branch information
rhugonnet authored Jan 31, 2024
1 parent a2a66c9 commit 14dd19d
Show file tree
Hide file tree
Showing 12 changed files with 202 additions and 120 deletions.
54 changes: 54 additions & 0 deletions .github/scripts/generate_yml_env_fixed_py.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
from __future__ import annotations

import argparse

import yaml # type: ignore


def environment_yml_nopy(fn_env: str, py_version: str, add_deps: list[str] = None) -> None:
"""
Generate temporary environment-py3.XX.yml files forcing python versions for setup of continuous integration.
:param fn_env: Filename path to environment.yml
:param py_version: Python version to force.
:param add_deps: Additional dependencies to solve for directly (for instance graphviz fails with mamba update).
"""

# Load the yml as dictionary
yaml_env = yaml.safe_load(open(fn_env))
conda_dep_env = list(yaml_env["dependencies"])

# Force python version
conda_dep_env_forced_py = ["python=" + py_version if "python" in dep else dep for dep in conda_dep_env]

# Optionally, add other dependencies
if add_deps is not None:
conda_dep_env_forced_py.extend(add_deps)

# Copy back to new yaml dict
yaml_out = yaml_env.copy()
yaml_out["dependencies"] = conda_dep_env_forced_py

with open("environment-ci-py" + py_version + ".yml", "w") as outfile:
yaml.dump(yaml_out, outfile, default_flow_style=False)


if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Generate environment files for CI with fixed python versions.")
parser.add_argument("fn_env", metavar="fn_env", type=str, help="Path to the generic environment file.")
parser.add_argument(
"--pyv",
dest="py_version",
default="3.9",
type=str,
help="List of Python versions to force.",
)
parser.add_argument(
"--add",
dest="add_deps",
default=None,
type=str,
help="List of dependencies to add.",
)
args = parser.parse_args()
environment_yml_nopy(fn_env=args.fn_env, py_version=args.py_version, add_deps=args.add_deps.split(","))
53 changes: 0 additions & 53 deletions .github/scripts/get_yml_env_nopy.py

This file was deleted.

12 changes: 4 additions & 8 deletions .github/workflows/python-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,20 +51,16 @@ jobs:
CACHE_NUMBER: 0 # Increase this value to reset cache if environment.yml has not changed
id: cache

# The trick below is necessary because the generic environment file does not specify a Python version, and only
# "conda env update" can be used to update with an environment file, which upgrades the Python version
# The trick below is necessary because the generic environment file does not specify a Python version, and ONLY
# "conda env update" CAN BE USED WITH CACHING, which upgrades the Python version when using the base environment
# (we add "graphviz" from dev-environment to solve all dependencies at once, at graphviz relies on image
# processing packages very much like geo-packages; not a problem for docs, dev installs where all is done at once)
- name: Install base environment with a fixed Python version
if: steps.cache.outputs.cache-hit != 'true'
run: |
mamba install pyyaml python=${{ matrix.python-version }}
pkgs_conda_base=`python .github/scripts/get_yml_env_nopy.py "environment.yml" --p "conda"`
pkgs_pip_base=`python .github/scripts/get_yml_env_nopy.py "environment.yml" --p "pip"`
mamba install python=${{ matrix.python-version }} $pkgs_conda_base graphviz
if [[ "$pkgs_pip_base" != "None" ]]; then
pip install $pkgs_pip_base
fi
python .github/scripts/generate_yml_env_fixed_py.py --pyv ${{ matrix.python-version }} --add "graphviz" "environment.yml"
mamba env update -n xdem-dev -f environment-ci-py${{ matrix.python-version }}.yml
- name: Install project
run: pip install -e . --no-dependencies
Expand Down
4 changes: 2 additions & 2 deletions doc/source/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ documentation.
Raster.width
Raster.count
Raster.count_on_disk
Raster.indexes
Raster.indexes_on_disk
Raster.bands
Raster.bands_on_disk
Raster.res
Raster.bounds
Raster.dtypes
Expand Down
4 changes: 2 additions & 2 deletions geoutils/projtools.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ def align_bounds(


def reproject_points(
points: list[list[float]] | tuple[list[float], list[float]] | NDArrayNum, in_crs: CRS, out_crs: CRS
points: list[list[float]] | list[float] | tuple[list[float], list[float]] | NDArrayNum, in_crs: CRS, out_crs: CRS
) -> tuple[list[float], list[float]]:
"""
Reproject a set of point from input_crs to output_crs.
Expand All @@ -269,7 +269,7 @@ def reproject_points(


def reproject_to_latlon(
points: list[list[float]] | NDArrayNum, in_crs: CRS, round_: int = 8
points: list[list[float]] | list[float] | NDArrayNum, in_crs: CRS, round_: int = 8
) -> tuple[list[float], list[float]]:
"""
Reproject a set of point from in_crs to lat/lon.
Expand Down
33 changes: 24 additions & 9 deletions geoutils/raster/raster.py
Original file line number Diff line number Diff line change
Expand Up @@ -986,8 +986,8 @@ def raster_equal(self, other: object) -> bool:
- The raster's transform, crs and nodata values.
"""

if not isinstance(other, type(self)): # TODO: Possibly add equals to SatelliteImage?
return NotImplemented
if not isinstance(other, Raster): # TODO: Possibly add equals to SatelliteImage?
raise NotImplementedError("Equality with other object than Raster not supported by raster_equal.")
return all(
[
np.array_equal(self.data.data, other.data.data, equal_nan=True),
Expand Down Expand Up @@ -2541,7 +2541,7 @@ def save(
save_data = self.data

# If the raster is a mask, convert to uint8 before saving and force nodata to 255
if save_data.dtype == bool:
if self.data.dtype == bool:
save_data = save_data.astype("uint8")
nodata = 255

Expand Down Expand Up @@ -3744,12 +3744,17 @@ def reproject(
memory_limit=memory_limit,
)

# Transform back to a boolean array
# Transform output back to a boolean array
output._data = output.data.astype(bool) # type: ignore

# Transform self back to boolean array
self._data = self.data.astype(bool) # type: ignore

if inplace:
self._transform = output._transform # type: ignore
self._crs = output._crs # type: ignore
# Little trick to force the shape, same as in Raster.reproject()
self._data = output._data # type: ignore
self.data = output._data # type: ignore
return None
else:
Expand Down Expand Up @@ -3820,7 +3825,14 @@ def polygonize(

# Convert to unsigned integer and pass to parent method
self._data = self.data.astype("uint8") # type: ignore
return super().polygonize(target_values=target_values)

# Get output from parent method
output = super().polygonize(target_values=target_values)

# Convert array back to boolean
self._data = self.data.astype(bool) # type: ignore

return output

def proximity(
self,
Expand All @@ -3841,12 +3853,11 @@ def proximity(
# warnings.warn("In-value converted to 1 for polygonizing boolean mask.")
# target_values = [1]

# Convert to unsigned integer and pass to parent method
self._data = self.data.astype("uint8") # type: ignore

# Need to cast output to Raster before computing proximity, as output will not be boolean
# (super() would instantiate Mask() again)
raster = Raster({"data": self.data, "transform": self.transform, "crs": self.crs, "nodata": self.nodata})
raster = Raster(
{"data": self.data.astype("uint8"), "transform": self.transform, "crs": self.crs, "nodata": self.nodata}
)
return raster.proximity(
vector=vector,
target_values=target_values,
Expand Down Expand Up @@ -3933,6 +3944,10 @@ def proximity_from_vector_or_raster(
# 1/ First, if there is a vector input, we rasterize the geometry type
# (works with .boundary that is a LineString (.exterior exists, but is a LinearRing)
if vector is not None:

# TODO: Only when using centroid... Maybe we should leave this operation to the user anyway?
warnings.filterwarnings("ignore", message="Geometry is in a geographic CRS.*")

# We create a geodataframe with the geometry type
boundary_shp = gpd.GeoDataFrame(geometry=vector.ds.__getattr__(geometry_type), crs=vector.crs)
# We mask the pixels that make up the geometry type
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ fallback_version = "0.0.1"
target_version = ['py36']

[tool.pytest.ini_options]
addopts = "--doctest-modules"
addopts = "--doctest-modules -W error::UserWarning"
testpaths = [
"tests",
"geoutils"
Expand Down
6 changes: 6 additions & 0 deletions tests/test_doc.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import os
import platform
import shutil
import warnings

import sphinx.cmd.build

Expand All @@ -13,6 +14,11 @@ class TestDocs:
def test_build(self) -> None:
"""Try building the documentation and see if it works."""

# Ignore all warnings raised in the documentation
# (some UserWarning are shown on purpose in certain examples, so they shouldn't make the test fail,
# and most other warnings are for Sphinx developers, not meant to be seen by us; or we can check on RTD)
warnings.filterwarnings("ignore")

# Building the doc fails on Windows for the CLI section
if (platform.system() == "Linux") or (platform.system() == "Darwin"):

Expand Down
15 changes: 15 additions & 0 deletions tests/test_geoviewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import os
import sys
import warnings

import matplotlib.pyplot as plt
import pytest
Expand Down Expand Up @@ -40,9 +41,14 @@ def test_geoviewer_valid_1band(capsys, monkeypatch, filename, option): # type:
# To avoid having the plots popping up during execution
monkeypatch.setattr(plt, "show", lambda: None)

# The everest example will raise errors when setting a nodata value that exists
if "B4" in os.path.basename(filename) and len(option) > 0 and option[0] == "-nodata":
warnings.filterwarnings("ignore", category=UserWarning, message="New nodata value found in the data array.*")

# To not get exception when testing generic functions such as --help
try:
geoviewer.main([filename, *option])
plt.close()
except SystemExit:
pass

Expand Down Expand Up @@ -81,6 +87,10 @@ def test_geoviewer_invalid_1band(capsys, monkeypatch, filename, args): # type:
# To avoid having the plots popping up during execution
monkeypatch.setattr(plt, "show", lambda: None)

# The everest example will raise errors when setting a nodata value that exists
if "B4" in os.path.basename(filename) and len(args) > 0 and args[0] == "-nodata":
warnings.filterwarnings("ignore", category=UserWarning, message="New nodata value found in the data array.*")

# To not get exception when testing generic functions such as --help
option, error = args
with pytest.raises(error):
Expand Down Expand Up @@ -108,9 +118,14 @@ def test_geoviewer_valid_3band(capsys, monkeypatch, filename, option): # type:
# To avoid having the plots popping up during execution
monkeypatch.setattr(plt, "show", lambda: None)

# The everest RGB example will raise errors when setting a nodata value that exists
if "RGB" in os.path.basename(filename) and len(option) > 0 and option[0] == "-nodata":
warnings.filterwarnings("ignore", category=UserWarning, message="New nodata value found in the data array.*")

# To not get exception when testing generic functions such as --help
try:
geoviewer.main([filename, *option])
plt.close()
except SystemExit:
pass

Expand Down
Loading

0 comments on commit 14dd19d

Please sign in to comment.