Skip to content

Modernize dependencies and CI, add Python 3.13 support, and fix NumPy 2 compatibility issues#372

Merged
clausmichele merged 26 commits into
Open-EO:mainfrom
Eurac-Research-Institute-for-EO:fix/dependencies_update
Jun 5, 2026
Merged

Modernize dependencies and CI, add Python 3.13 support, and fix NumPy 2 compatibility issues#372
clausmichele merged 26 commits into
Open-EO:mainfrom
Eurac-Research-Institute-for-EO:fix/dependencies_update

Conversation

@suriyahgit

@suriyahgit suriyahgit commented Apr 10, 2026

Copy link
Copy Markdown
Member

This PR modernises openeo-processes-dask dependencies and CI, extends Python support to 3.13, restores compatibility with current versions of NumPy, XGBoost, and pystac, and fixes a set of runtime and test issues uncovered during the upgrade work. Removes the xcube-eopf dependency and EOPF data support in load_stac.

The main goals are:

  • allow newer dependency versions without dropping existing supported lower bounds where possible
  • remove deprecated NumPy and Pydantic usage
  • make CI and release workflows more robust by replacing the UbuntuGIS PPA setup with conda-forge
  • fix correctness and compatibility issues in array_find, array_interpolate_linear, eq, resample_cube_temporal, and random_forest

Dependency and packaging changes

Python support

  • Supported Python range updated from >=3.10,<3.13 to >=3.10,<3.14
  • Added Python 3.13 and 3.14 classifiers

Package version

  • openeo-processes-dask: 2025.10.12026.06.2

Dependency policy

This PR follows a “widen, don’t shift” approach: upper bounds are removed where possible to allow newer versions, while existing lower bounds are largely preserved to avoid unnecessary disruption for older environments.

Updated constraints

Package Old New
numpy <2.0.0 >=1.26.3,<3
xarray >=2022.11.0,<2025.08.01 >=2022.11.0
dask[array,dataframe,distributed] >=2023.4.0,<2025.2.0 >=2023.4.0
xgboost >=1.5.1,<2.1.4 >=1.5.1
pystac <1.12.0 >=1.8.0
geopandas >=0.11.1,<1 >=0.11.1
dask-geopandas 0.4.3 >=0.4.3
pyarrow ^15.0.2 >=15.0.2
openeo-pg-parser-networkx >=2025.10 >=2025.10.0

New optional dependency

  • gdal >=3.9 added as an explicit optional dependency, installed from conda-forge in CI

CI and release workflow changes

The CI and release workflows were reworked to install GDAL via conda-forge instead of the UbuntuGIS PPA.

Main changes

  • switched GDAL installation from UbuntuGIS PPA to Miniforge/mamba + conda-forge
  • added .github/ci-environment.yml to define the CI conda environment
  • configured Poetry to install into the conda environment with virtualenvs.create false
  • expanded the test matrix from Python 3.10–3.12 to 3.10–3.13
  • upgraded Poetry from 1.5.1 to 2.3.3
  • updated GitHub Actions versions
  • changed shell from bash to bash -l {0} for conda activation
  • replaced the old .venv cache strategy with separate caches for conda packages and pip wheels
  • added the missing poetry install step in the release workflow
  • moved workflow commands to run directly inside the conda environment instead of through poetry run

This removes a fragile CI path that depended on matching the Python GDAL binding to the system GDAL version at runtime.

New file

.github/ci-environment.yml

name: ci
channels:
  - conda-forge
dependencies:
  - gdal

Code fixes

NumPy 2 compatibility

utils.pyget_scalar_type()

Replaced np.obj2sctype() with logic based on np.asarray(obj).dtype.type and np.dtype(obj.dtype).type, which works on both NumPy 1.x and 2.x.

Also added a str fast path so string inputs are handled consistently.

comparison.pynp.issubsctype() removal

Replaced all uses of np.issubsctype() with np.issubdtype().

In eq(), the incompatible-type branch now returns a boolean array matching input shape instead of a scalar False, avoiding shape-related issues for array inputs.


arrays.py fixes

array_find()

Reworked array_find() to fix three issues:

  • np.isnan(value) could fail on non-numeric values
  • masked-array behaviour was unreliable for Dask-backed results under NumPy 2.x because masks were lost when coerced
  • the function emitted a warning on every call about masked arrays

The new behaviour is:

  • handle non-numeric search values safely
  • return filled arrays instead of masked arrays
  • keep the Dask path lazy
  • remove the per-call warning

For not-found positions, the function now returns filled values (999999) instead of masked entries.

array_interpolate_linear()

Fixed the Dask 1D path so it stays lazy by using da.map_blocks(...).

Also:

  • converts block inputs to NumPy arrays inside the interpolation helper
  • avoids mutating the caller input in place by copying before assignment
  • simplifies valid.all() logic

random_forest.py

Modernised XGBoost Dask usage by switching from xgb.dask.* access through import xgboost as xgb to:

from xgboost import dask as dxgb

Also added ImportError guards with clearer error messages.


cubes/resample.py

resample_cube_temporal()

Simplified tie handling when multiple timestamps are equally close to the target.

The old implementation only handled specific argwhere() output shapes. The new implementation flattens the index result and always takes the first match, which consistently selects the earliest timestamp and avoids crashes on other tie configurations.


cubes/apply_neighborhood_intertwin.py

Added an explicit zero-size dimension check and now raises a clear ValueError when an input dimension has size 0.


Test and compatibility updates

Pydantic v2

Replaced all parse_obj() uses with model_validate() across fixtures and tests.

Test fixes

  • fixed a Dask assertion that previously passed without actually evaluating values by adding .compute()
  • corrected the array_find() string not-found expectation from 99999 to 999999

The previous string sentinel test was not validating the actual returned value because masked positions were effectively being ignored.


Behavioural changes to note

These are the main observable behaviour changes introduced by the fixes:

  1. numpy >=1.26.3 is now required, and NumPy 2.x is supported.

  2. pystac >=1.8.0 is now required.

  3. array_find() now returns filled np.ndarray / da.Array results instead of masked arrays. Not-found positions are represented by 999999.

  4. array_interpolate_linear() now preserves laziness for 1D Dask inputs instead of forcing eager behaviour.

  5. eq() now returns a shape-preserving boolean array for incompatible array/type comparisons instead of a scalar False.

  6. CI now assumes Poetry 2.x compatibility.


Testing notes

  • CI now runs on Python 3.10, 3.11, 3.12 and 3.13
  • GDAL is installed from conda-forge
  • corrected tests now validate previously untested behaviour in eq() and array_find()

@codecov

codecov Bot commented Apr 10, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.29630% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 83.36%. Comparing base (010c214) to head (e055dac).
⚠️ Report is 40 commits behind head on main.

Files with missing lines Patch % Lines
...ocesses_dask/process_implementations/comparison.py 90.90% 1 Missing ⚠️
...s_dask/process_implementations/ml/random_forest.py 90.90% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #372      +/-   ##
==========================================
+ Coverage   82.82%   83.36%   +0.54%     
==========================================
  Files          34       39       +5     
  Lines        2241     2567     +326     
==========================================
+ Hits         1856     2140     +284     
- Misses        385      427      +42     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@suriyahgit

Copy link
Copy Markdown
Member Author

also a spiritual continuity from these PRs: #358 (numpy v2 support); #354 and #345 (GDAL related CI stuffs which this PR solves)

@suriyahgit

Copy link
Copy Markdown
Member Author

codecov is comparing against an outdated base commit (010c214) that has no coverage uploads, so the reported coverage change is not reliable. This likely stems from missing/invalid coverage reports on recent main commits, not from the changes in this PR. We should trigger a fresh CI run on main (or merge this PR) to ensure coverage is uploaded with the updated workflow, so Codecov can establish a valid baseline for future comparisons.

@suriyahgit suriyahgit requested a review from rtmiz April 22, 2026 14:13
return np.str_
if np.isscalar(obj):
return np.obj2sctype(type(obj))
return np.asarray(obj).dtype.type

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://numpy.org/doc/2.3/numpy_2_0_migration_guide.html says use np.dtype(obj).type instead of obj2sctype, and NumPy 1.25 seem to contain np.dtype (https://numpy.org/doc/1.25/reference/arrays.dtypes.html). Why is all the extra logic required?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @pjonsson, it's a good point. I simplified this while keeping separate scalar and dtype-bearing paths. np.dtype(obj).type is not safe for all current callers: it raises for values like 1, 1.0, and ndarray inputs, and string values can be interpreted as dtype aliases. Scalars now use np.dtype(type(obj)).type, while dtype-bearing objects use np.dtype(obj.dtype).type.

idxs = eq.argmax(axis=axis)
mask = ~np.array(eq.any(axis=axis))

# Fix: np.isnan crashes on strings, None, lists, etc.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find it difficult to sort out what fixes that are done because of Python 3.13/3.14 incompatibilities, NumPy 2 incompatibilities, or CI incompatibilities. Is it a NumPy 2 problem that np.isnan crashes on anything that isn't array_like?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed! that this needed clearer separation, this is not specifically a NumPy 2 issue; np.isnan is only defined for numeric-like values, so the old unconditional check was too broad. I updated the comment and kept non-numeric search values on the normal equality/not-found path.

@pjonsson

pjonsson commented May 6, 2026

Copy link
Copy Markdown
Contributor

This likely stems from missing/invalid coverage reports on recent main commits, not from the changes in this PR. We should trigger a fresh CI run on main (or merge this PR) to ensure coverage is uploaded with the updated workflow, so Codecov can establish a valid baseline for future comparisons.

Does code coverage work for main, but CodeCov was down when the last commit was merged to main? If so, merging #358 and rebasing this on top of latest main should get the right coverage information. And if coverage doesn't work on main, perhaps the commit that fixes coverage in this PR can be cherry-picked into a separate branch and submitted as a separate PR that can be merged first to establish the baseline coverage?

Comment thread .github/workflows/main.yml Outdated
shell: bash -l {0}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v5

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason to not use version 6?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there was no reason to avoid version 6, so actions/checkout is now updated to v6.

Comment thread .github/workflows/main.yml Outdated
POETRY_VERSION: 1.5.1

POETRY_VERSION: 2.3.3
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true # fixes the Node.js 20 warning

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's nice if the comment contains a note about which action that triggers "the Node.js 20 warning" so this variable can be removed when that action is updated in the future.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that makes sense. addressed this by removing the override instead of documenting it. The workflow now uses actions/checkout@v6 and setup-miniconda@v4, so FORCE_JAVASCRIPT_ACTIONS_TO_NODE24 is no longer needed.

Comment thread .pre-commit-config.yaml Outdated

- repo: https://github.com/python-poetry/poetry
- repo: https://github.com/python-poetry/poetry
rev: 1.5.1

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The POETRY_VERSION in the CI is set to 2.3.3 by this PR, should the version here be the same?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice catch, yes, it should match, addressed this. The poetry-check pre-commit hook now uses 2.3.3 to align with POETRY_VERSION in CI.

return data
if len(x[valid]) < 2:
return data
data = data.copy() # avoid mutating the caller's array

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like it changes both behavior of the interp function and how expensive it is to call it.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed, it indeed makes things expensive at this point. addressed in the latest commit: e91ab28. The previous lazy Dask path could process chunks independently and change interpolation results at chunk boundaries. I changed the 1D Dask path to rechunk to one block before map_blocks, preserving the eager/global interpolation behavior while still delaying execution until compute time. I also added a multi-chunk regression test for this.

try:
from xgboost import dask as dxgb
except ImportError as e:
raise ImportError("xgboost[dask] is required for predict_random_forest.") from e

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like the intention is to replace the import error generated by the system, shouldn't this raise from None instead to avoid showing a bunch of extra stuff in the backtrace?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks, agreed. addressed in e91ab28. This now raises the clearer ImportError with from None, so the original import traceback is suppressed.

try:
from xgboost import dask as dxgb
except ImportError as e:
raise ImportError(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like the intention is to replace the import error generated by the system, shouldn't this raise from None instead to avoid showing a bunch of extra stuff in the backtrace?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this now also raises with from None, matching the intended replacement error behavior.

@suriyahgit

Copy link
Copy Markdown
Member Author

This likely stems from missing/invalid coverage reports on recent main commits, not from the changes in this PR. We should trigger a fresh CI run on main (or merge this PR) to ensure coverage is uploaded with the updated workflow, so Codecov can establish a valid baseline for future comparisons.

Does code coverage work for main, but CodeCov was down when the last commit was merged to main? If so, merging #358 and rebasing this on top of latest main should get the right coverage information. And if coverage doesn't work on main, perhaps the commit that fixes coverage in this PR can be cherry-picked into a separate branch and submitted as a separate PR that can be merged first to establish the baseline coverage?

@pjonsson I did not make a branch-level code change for this because it appears to be a baseline/main coverage upload issue rather than something this PR branch can fully fix. After e91ab28, Codecov is comparing against the pushed head and patch coverage improved, but it still reports the base as 010c214 and 38 commits behind main. Rebasing onto a main commit with a valid coverage upload should make the comparison meaningful.

@pjonsson thanks again for your time and efforts for reviewing this PR

I pushed e91ab28 to address the review feedback from @pjonsson .
Main changes: updated the workflow action versions, removed the Node24 override, aligned the Poetry pre-commit hook with CI, simplified get_scalar_type, clarified array_find NaN handling, preserved global behavior for lazy 1D Dask interpolation, and switched the XGBoost import guards to raise from None.

suriyahgit added a commit to Eurac-Research-Institute-for-EO/openeo-processes-dask-slim that referenced this pull request Jun 4, 2026
…ompat

Backport changes from openeo-processes-dask PR Open-EO#372:

- Bump python to >=3.10,<3.14, add py3.13 classifier
- Allow numpy >=1.26.3,<3; pystac >=1.8.0
- Remove upper bounds from xarray, dask, geopandas, dask-geopandas, xgboost
- Add gdal >=3.9 optional dep from conda-forge
- Switch CI to conda-incubator/setup-miniconda, Poetry 2.3.3, actions/checkout@v6
- Replace np.obj2sctype and np.issubsctype for NumPy 2 compat
- Fix array_find (filled arrays, no warning), array_interpolate_linear (lazy dask)
- Fix random_forest xgboost import (dxgb), resample tie handling, zero-size dim check
- Update tests: model_validate, new array_find/interpolate/ml tests
@clausmichele clausmichele changed the title Modernize dependencies and CI, add Python 3.13–3.14 support, and fix NumPy 2 compatibility issues Modernize dependencies and CI, add Python 3.13 support, and fix NumPy 2 compatibility issues Jun 5, 2026
- Restructure README installation: conda first (mirrors CI), then system packages
- Replace auto-detecting PyPI badge with explicit Python 3.10-3.13 badge
- Fix codecov badge to point to correct org and branch
- Remove non-existent 'experimental' extra from extras list
- Clarify which extras depend on GDAL (implementations only)
- Add install-methods.yml CI workflow to test conda+pip, micromamba+pip,
  and system packages+pip installation paths weekly
…puts

- conda-pip: mamba not available in PATH, use conda install instead
- micromamba-pip: remove invalid inputs (channels, channel-priority,
  conda-remove-defaults) not accepted by setup-micromamba@v2, add
  required environment-name
- Cannot merge into a single matrix job because uses: does not
  support dynamic expressions
@clausmichele clausmichele merged commit c653e78 into Open-EO:main Jun 5, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants