diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 86d85580..55da8511 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -21,7 +21,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: - python-version: "3.11" + python-version: "3.x" - uses: pre-commit/action@v3.0.1 conda: @@ -36,7 +36,6 @@ jobs: matrix: os: [ubuntu-latest] python-version: - - "3.8" - "3.9" - "3.10" - "3.11" @@ -50,7 +49,6 @@ jobs: with: cache-downloads: true cache-environment: true - activate-environment: raven environment-file: environment.yml create-args: >- python=${{ matrix.python-version }} @@ -60,14 +58,14 @@ jobs: echo "micromamba: $(micromamba --version)" - name: Install RavenWPS run: | - python3 -m pip install --no-user --editable ".[dev]" + python -m pip install --no-user --editable ".[dev]" - name: Check versions run: | conda list - pip check + python -m pip check - name: Test RavenWPS run: | - python3 -m pytest -m "not very_slow" tests + python -m pytest -m "not very_slow" tests finish: name: Finish diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8c2ae549..fa03b47d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,7 +6,7 @@ repos: rev: v3.15.2 hooks: - id: pyupgrade - args: [ '--py38-plus' ] + args: [ '--py39-plus' ] - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.6.0 hooks: @@ -47,7 +47,7 @@ repos: rev: 1.8.5 hooks: - id: nbqa-pyupgrade - args: [ '--py38-plus' ] + args: [ '--py39-plus' ] additional_dependencies: [ 'pyupgrade==v3.15.2' ] - id: nbqa-black additional_dependencies: [ 'black==24.4.2' ] diff --git a/Makefile b/Makefile index 2f0098c8..8de60a89 100644 --- a/Makefile +++ b/Makefile @@ -233,7 +233,10 @@ notebook: .PHONY: lint lint: @echo "Running flake8 code style checks ..." - @bash -c 'flake8 raven tests' + black --check raven tests + isort --check raven tests + flake8 raven tests + yamllint --config-file=.yamllint.yaml raven # Only works for notebooks that passed ``make test-notebooks`` above. For # those that failed, manually starting a local Jupyter server and refresh them diff --git a/client_environment.yml b/client_environment.yml deleted file mode 100644 index 8d6785c8..00000000 --- a/client_environment.yml +++ /dev/null @@ -1,32 +0,0 @@ -# conda env create -f environment.yml -name: birdy -channels: - - birdhouse - - conda-forge - - defaults -dependencies: - - matplotlib - - xarray - - numpy - - netcdf4 - - pydap - - proj<8.0.0 - - cartopy - - rioxarray - - xclim - # to edit .ipynb - - jupyter - # to be launched by image jupyterhub/jupyterhub - - notebook - # for pip packages - - pip - - nc-time-axis - - ipyleaflet - - hvplot - - holoviews - - pip: - # visual debugger for Jupyter Notebook, not working with JupyterLab at this moment - - owslib - - pixiedust - - birdhouse-birdy - - ravenpy diff --git a/environment-dev.yml b/environment-dev.yml index b7cf7bcb..9577e5dc 100644 --- a/environment-dev.yml +++ b/environment-dev.yml @@ -5,11 +5,12 @@ channels: dependencies: - python >=3.8,<3.12 - birdy - - black + - black >=24.4.2 - bump2version - flake8 - ipykernel - ipywidgets + - isort >=5.13.2 - nbconvert - nbval - pip diff --git a/environment.yml b/environment.yml index 916415d2..103e7bf8 100644 --- a/environment.yml +++ b/environment.yml @@ -7,7 +7,7 @@ dependencies: - pywps ==4.5.1 - affine - anyascii - - cartopy + - cartopy >=0.23.0 - click - fiona >=1.9 - geopandas >=0.12.0 diff --git a/pyproject.toml b/pyproject.toml index 559314be..b0c9316f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,7 +13,7 @@ maintainers = [ {name = "Tuan Long Vu", email = "vu.long@ouranos.ca"} ] readme = {file = "README.rst", content-type = "text/x-rst"} -requires-python = ">=3.8.0" +requires-python = ">=3.9.0" keywords = ["wps", "pywps", "birdhouse", "raven", "hydrology", "gis", "analysis"] license = {file = "LICENSE.txt"} classifiers = [ @@ -27,7 +27,6 @@ classifiers = [ "Programming Language :: Python", "Natural Language :: English", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", @@ -45,8 +44,7 @@ dependencies = [ "netCDF4", "numpy", "owslib", - "pandas <2.0; python_version == '3.8'", - "pandas; python_version >= '3.9'", + "pandas >=2.0", "psutil", "pywps ==4.5.1", "requests", @@ -54,6 +52,7 @@ dependencies = [ "xclim >=0.31.0", # GIS libraries "affine", + "cartopy >=0.23.0", "fiona >=1.9.0", "geojson", "geopandas >=0.12.0", @@ -74,11 +73,12 @@ dependencies = [ dev = [ # Dev tools and testing "birdhouse-birdy", - "black >=24.1.1", + "black >=24.4.2", "black-nb", "bumpversion", "flake8 >=7.0.0", "ipykernel", + "isort >=5.13.2", "nbconvert", "nbval", "nc-time-axis", @@ -119,7 +119,6 @@ raven-wps = "raven.cli:cli" [tool.black] target-version = [ - "py38", "py39", "py310", "py311" @@ -169,7 +168,7 @@ exclude = [ [tool.isort] profile = "black" -py_version = 38 +py_version = 39 append_only = true [tool.pytest.ini_options] diff --git a/raven/utilities/analysis.py b/raven/utilities/analysis.py index 06a5e813..343bb2d7 100644 --- a/raven/utilities/analysis.py +++ b/raven/utilities/analysis.py @@ -61,7 +61,7 @@ def geom_prop(geom: Union[Polygon, MultiPolygon, GeometryCollection]) -> dict: def dem_prop( dem: Union[str, Path], - geom: Union[Polygon, MultiPolygon, List[Union[Polygon, MultiPolygon]]] = None, + geom: Union[Polygon, MultiPolygon, list[Union[Polygon, MultiPolygon]]] = None, directory: Union[str, Path] = None, ) -> dict: """Return raster properties for each geometry. diff --git a/raven/utilities/checks.py b/raven/utilities/checks.py index b2dbd3d8..c3ed03a3 100644 --- a/raven/utilities/checks.py +++ b/raven/utilities/checks.py @@ -3,8 +3,9 @@ import collections import logging import warnings +from collections.abc import Sequence from pathlib import Path -from typing import Any, List, Sequence, Tuple, Union +from typing import Any, List, Tuple, Union from . import gis_import_error_message @@ -125,8 +126,8 @@ def multipolygon_check(geom: GeometryCollection) -> None: def feature_contains( - point: Union[Tuple[Union[int, float, str], Union[str, float, int]], Point], - shp: Union[str, Path, List[Union[str, Path]]], + point: Union[tuple[Union[int, float, str], Union[str, float, int]], Point], + shp: Union[str, Path, list[Union[str, Path]]], ) -> Union[dict, bool]: """Return the first feature containing a location. diff --git a/raven/utilities/geo.py b/raven/utilities/geo.py index e8088fce..a7b826c8 100644 --- a/raven/utilities/geo.py +++ b/raven/utilities/geo.py @@ -87,7 +87,7 @@ def geom_transform( def generic_raster_clip( raster: Union[str, Path], output: Union[str, Path], - geometry: Union[Polygon, MultiPolygon, List[Union[Polygon, MultiPolygon]]], + geometry: Union[Polygon, MultiPolygon, list[Union[Polygon, MultiPolygon]]], touches: bool = False, fill_with_nodata: bool = True, padded: bool = True, diff --git a/raven/utilities/geoserver.py b/raven/utilities/geoserver.py index 986a2caf..408f37a7 100644 --- a/raven/utilities/geoserver.py +++ b/raven/utilities/geoserver.py @@ -18,8 +18,9 @@ import os import urllib.request import warnings +from collections.abc import Iterable, Sequence from pathlib import Path -from typing import Iterable, Optional, Sequence, Tuple, Union +from typing import Optional, Tuple, Union from urllib.parse import urljoin from requests import Request @@ -62,7 +63,7 @@ def _get_location_wfs( bbox: Optional[ - Tuple[ + tuple[ Union[str, float, int], Union[str, float, int], Union[str, float, int], @@ -70,7 +71,7 @@ def _get_location_wfs( ] ] = None, point: Optional[ - Tuple[ + tuple[ Union[str, float, int], Union[str, float, int], ] @@ -409,11 +410,11 @@ def aggfunc(x): def select_hybas_domain( bbox: Optional[ - Tuple[ + tuple[ Union[int, float], Union[int, float], Union[int, float], Union[int, float] ] ] = None, - point: Optional[Tuple[Union[int, float], Union[int, float]]] = None, + point: Optional[tuple[Union[int, float], Union[int, float]]] = None, ) -> str: """Provided a given coordinate or boundary box, return the domain name of the geographic region the coordinate is located within. @@ -483,7 +484,7 @@ def filter_hydrobasins_attributes_wfs( def get_hydrobasins_location_wfs( - coordinates: Tuple[ + coordinates: tuple[ Union[str, float, int], Union[str, float, int], ], @@ -649,7 +650,7 @@ def filter_hydro_routing_attributes_wfs( def get_hydro_routing_location_wfs( - coordinates: Tuple[ + coordinates: tuple[ Union[int, float, str], Union[str, float, int], ], diff --git a/raven/utilities/io.py b/raven/utilities/io.py index 77a146a1..a1a23834 100644 --- a/raven/utilities/io.py +++ b/raven/utilities/io.py @@ -6,9 +6,10 @@ import tempfile import warnings import zipfile +from collections.abc import Iterable, Sequence from pathlib import Path from re import search -from typing import Iterable, List, Optional, Sequence, Tuple, Union +from typing import List, Optional, Tuple, Union from . import gis_import_error_message @@ -79,9 +80,9 @@ def address_append(address: Union[str, Path]) -> str: def generic_extract_archive( - resources: Union[str, Path, List[Union[bytes, str, Path]]], + resources: Union[str, Path, list[Union[bytes, str, Path]]], output_dir: Optional[Union[str, Path]] = None, -) -> List[str]: +) -> list[str]: """Extract archives (tar/zip) to a working directory. Parameters @@ -143,10 +144,10 @@ def generic_extract_archive( def archive_sniffer( - archives: Union[str, Path, List[Union[str, Path]]], + archives: Union[str, Path, list[Union[str, Path]]], working_dir: Optional[Union[str, Path]] = None, extensions: Optional[Sequence[str]] = None, -) -> List[Union[str, Path]]: +) -> list[Union[str, Path]]: """Return a list of locally unarchived files that match the desired extensions. Parameters @@ -177,7 +178,7 @@ def archive_sniffer( def crs_sniffer( *args: Union[str, Path, Sequence[Union[str, Path]]] -) -> Union[List[Union[str, int]], str, int]: +) -> Union[list[Union[str, int]], str, int]: """Return the list of CRS found in files. Parameters @@ -266,7 +267,7 @@ def raster_datatype_sniffer(file: Union[str, Path]) -> str: def get_bbox( vector: Union[str, Path], all_features: bool = True -) -> Tuple[float, float, float, float]: +) -> tuple[float, float, float, float]: """Return bounding box of all features or the first feature in file. Parameters diff --git a/raven/utilities/testdata.py b/raven/utilities/testdata.py index 4e03a472..d3a04c92 100644 --- a/raven/utilities/testdata.py +++ b/raven/utilities/testdata.py @@ -5,9 +5,10 @@ import os import re import warnings +from collections.abc import Sequence from pathlib import Path from shutil import copy -from typing import List, Optional, Sequence, Union +from typing import List, Optional, Union from urllib.error import HTTPError, URLError from urllib.parse import urljoin from urllib.request import urlretrieve @@ -40,7 +41,7 @@ def get_local_testdata( temp_folder: Union[str, os.PathLike], branch: str = "master", _local_cache: Union[str, os.PathLike] = _default_cache_dir, -) -> Union[Path, List[Path]]: +) -> Union[Path, list[Path]]: """Copy specific testdata from a default cache to a temporary folder. Return files matching `pattern` in the default cache dir and move to a local temp folder. @@ -175,7 +176,7 @@ def get_file( github_url: str = "https://github.com/Ouranosinc/raven-testdata", branch: str = "master", cache_dir: Path = _default_cache_dir, -) -> Union[Path, List[Path]]: +) -> Union[Path, list[Path]]: """ Return a file from an online GitHub-like repository. If a local copy is found then always use that to avoid network traffic. @@ -222,7 +223,7 @@ def query_folder( pattern: Optional[str] = None, github_url: str = "https://github.com/Ouranosinc/raven-testdata", branch: str = "master", -) -> List[str]: +) -> list[str]: """ Lists the files available for retrieval from a remote git repository with get_file. If provided a folder name, will perform a globbing-like filtering operation for parent folders. diff --git a/raven/utils.py b/raven/utils.py index b7c4e158..db0d2179 100644 --- a/raven/utils.py +++ b/raven/utils.py @@ -101,7 +101,7 @@ def gather_dem_tile( return Path(raster_file) -def parse_lonlat(lonlat: Union[str, Tuple[str, str]]) -> Tuple[float, float]: +def parse_lonlat(lonlat: Union[str, tuple[str, str]]) -> tuple[float, float]: """Return longitude and latitude from a string. Parameters @@ -133,7 +133,7 @@ def zonalstats_raster_file( data_type: str = None, crs: str = None, zip_archive: bool = False, -) -> Union[str, List[Path]]: +) -> Union[str, list[Path]]: """ Extract the zonalstats grid(s) to a zipped GeoTIFF file and ensure that it is projected with specified CRS.