Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix warnings and raise UserWarning as error in CI #458

Merged
merged 14 commits into from
Jan 31, 2024
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
Loading