Skip to content

imagery: Update i.eodag for EODAG v4 compatibility#1663

Open
polucifier wants to merge 4 commits into
OSGeo:grass8from
polucifier:grass8
Open

imagery: Update i.eodag for EODAG v4 compatibility#1663
polucifier wants to merge 4 commits into
OSGeo:grass8from
polucifier:grass8

Conversation

@polucifier
Copy link
Copy Markdown

@polucifier polucifier commented Mar 28, 2026

Description

This PR updates the i.eodag module to ensure compatibility with the latest EODAG v4 release while maintaining full backward compatibility for users on EODAG v3.

Solves #1659

Changes

  • EODAG v4 Support: Updated internal logic to handle Pydantic-based STAC objects, new property accessors, and the transition from productType to collection.
  • Backward Compatibility: Implemented a compatibility layer to detect EODAG version and switch between v3/v4 API calls dynamically.
  • Improved Testsuite: Updated test_eodag.py to adapt search parameters to the installed EODAG version.
  • Code Cleanup: Removed obsolete TODOs and improved inline documentation/comments.

Verification

  • Verified with EODAG v4.x.
  • Verified with EODAG v3.x.
  • All test_eodag.py tests passed in both environments.

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Remaining comments which cannot be posted as a review comment to avoid GitHub Rate Limit

pre-commit

[pre-commit] reported by reviewdog 🐶


[pre-commit] reported by reviewdog 🐶


[pre-commit] reported by reviewdog 🐶


[pre-commit] reported by reviewdog 🐶


[pre-commit] reported by reviewdog 🐶

Comment thread src/imagery/i.eodag/i.eodag.py Outdated
Comment thread src/imagery/i.eodag/i.eodag.py Outdated
Comment thread src/imagery/i.eodag/i.eodag.py Outdated
Comment thread src/imagery/i.eodag/i.eodag.py Outdated
Comment thread src/imagery/i.eodag/i.eodag.py Outdated
Comment thread src/imagery/i.eodag/i.eodag.py Outdated
Comment thread src/imagery/i.eodag/i.eodag.py Outdated
Comment thread src/imagery/i.eodag/i.eodag.py Outdated
Comment thread src/imagery/i.eodag/i.eodag.py Outdated
Comment thread src/imagery/i.eodag/i.eodag.py Outdated
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Remaining comments which cannot be posted as a review comment to avoid GitHub Rate Limit

ruff

[ruff] reported by reviewdog 🐶


[ruff] reported by reviewdog 🐶

p_id = p.get("ID") # This is fine for v3


[ruff] reported by reviewdog 🐶


[ruff] reported by reviewdog 🐶


[ruff] reported by reviewdog 🐶

def print_eodag_queryables(eodag_api, **kwargs) -> None:


[ruff] reported by reviewdog 🐶

The function extracts metadata (type, default value, and requirement)


[ruff] reported by reviewdog 🐶


[ruff] reported by reviewdog 🐶


[ruff] reported by reviewdog 🐶


[ruff] reported by reviewdog 🐶

if hasattr(potential_type, "__name__") and potential_type.__name__ == "Optional":


[ruff] reported by reviewdog 🐶

q_dict["type"] = getattr(potential_type, "__name__", str(potential_type))


[ruff] reported by reviewdog 🐶


[ruff] reported by reviewdog 🐶

q_dict["type"] = "int" # Fallback assumption for v3 ranges


[ruff] reported by reviewdog 🐶

if len(inner_args) > 0 and hasattr(inner_args[0], "__metadata__"):


[ruff] reported by reviewdog 🐶

getattr(range_meta[1], "lt", None)


[ruff] reported by reviewdog 🐶

def print_query(geometry, queryables, **kwargs) -> None:


[ruff] reported by reviewdog 🐶

p["ID"] if isinstance(p, dict) else getattr(p, "id", str(p))


[ruff] reported by reviewdog 🐶


[ruff] reported by reviewdog 🐶

gs.fatal(_("Product type <{}> not available.").format(options["producttype"]))


[ruff] reported by reviewdog 🐶

{pid.strip() for pid in options["id"].split(",")}, # product.id is consistent


[ruff] reported by reviewdog 🐶

gs.verbose(_("Reading stored search result from file <{}>").format(options["file"]))


[ruff] reported by reviewdog 🐶


[ruff] reported by reviewdog 🐶

ids = {pid.strip() for pid in id_file.read_text(encoding="UTF8").strip().split("\n")}


[ruff] reported by reviewdog 🐶

gs.fatal(_("Unable to read product IDs from file <{}>.").format(options["file"]))


[ruff] reported by reviewdog 🐶


[ruff] reported by reviewdog 🐶

dates_to_iso_format() # Validates date order and formats


[ruff] reported by reviewdog 🐶

search_parameters = {


[ruff] reported by reviewdog 🐶


[ruff] reported by reviewdog 🐶


[ruff] reported by reviewdog 🐶


[ruff] reported by reviewdog 🐶


[ruff] reported by reviewdog 🐶


[ruff] reported by reviewdog 🐶


[ruff] reported by reviewdog 🐶


[ruff] reported by reviewdog 🐶


[ruff] reported by reviewdog 🐶

gs.message(_("Please enter Creodias OTP (enter '-' to discard Creodias scenes): "))


[ruff] reported by reviewdog 🐶

search_result = SearchResult([s for s in search_result if s.provider != "creodias"])


[ruff] reported by reviewdog 🐶


[ruff] reported by reviewdog 🐶


[ruff] reported by reviewdog 🐶

flags="j", # Use -j to get JSON output for easier parsing


[ruff] reported by reviewdog 🐶

scene_id = scene["id"] # Get ID directly from JSON output


[ruff] reported by reviewdog 🐶

sensor_mode = props.get("instrumentMode") or \
props.get("platform:instrument:mode") or \
props.get("sat:instrument_mode")

Comment thread src/imagery/i.eodag/i.eodag.py
Comment thread src/imagery/i.eodag/i.eodag.py
Comment thread src/imagery/i.eodag/i.eodag.py
Comment thread src/imagery/i.eodag/i.eodag.py
Comment thread src/imagery/i.eodag/i.eodag.py
Comment thread src/imagery/i.eodag/i.eodag.py
Comment thread src/imagery/i.eodag/i.eodag.py Outdated
Comment thread src/imagery/i.eodag/i.eodag.py Outdated
Comment thread src/imagery/i.eodag/i.eodag.py
Comment thread src/imagery/i.eodag/i.eodag.py Outdated
@polucifier polucifier force-pushed the grass8 branch 4 times, most recently from 3b6728c to 69a3796 Compare March 29, 2026 09:25
@neteler neteler requested a review from landam March 31, 2026 19:30
@neteler neteler added the Python Related code is in Python label Mar 31, 2026
@landam landam requested a review from pesekon2 April 7, 2026 06:21
@landam landam linked an issue Apr 15, 2026 that may be closed by this pull request
@landam landam added the bug Something isn't working label Apr 15, 2026
Comment thread src/imagery/i.eodag/i.eodag.py Outdated
Copy link
Copy Markdown
Member

@ninsbl ninsbl left a comment

Choose a reason for hiding this comment

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

The code is not too easy to read with all the if-else-blocks.
Please consider using a mapping dict as suggested in my comment below...

Comment thread src/imagery/i.eodag/testsuite/test_eodag.py Outdated
@polucifier polucifier marked this pull request as draft April 19, 2026 23:32
polucifier added 2 commits May 2, 2026 03:33
- Add full support for EODAG v4 API (handling Pydantic STAC objects, .collection, and .providers).
- Maintain strict backward compatibility with EODAG v3 (fallback to .productType and dictionary properties).
- Implement automatic TOTP (2FA) generation for the Creodias provider using the 'pyotp' library.
- Fix testsuite (test_eodag.py) to dynamically switch between v3/v4 search parameters.
@polucifier polucifier marked this pull request as ready for review May 2, 2026 01:35
@polucifier
Copy link
Copy Markdown
Author

polucifier commented May 2, 2026

Addressed reviewers' feedbacks:

  • Replaced all if EODAG_VERSION >= 4 / else branches with a EODAG_TEST_MAP dictionary that maps version → constants, making version-specific handling explicit and centralized
  • Removed pyotp dependency and all unused imports (CalledModuleError, get_current_mapset, is_map_in_mapset)

Tested against both EODAG v3 and v4 — all 19 tests pass on both versions (excluding test_save_footprint which is intentionally skipped)

@polucifier polucifier requested a review from landam May 2, 2026 01:40
@landam landam changed the title imagery: Update i.eodag for EODAG v4 compatibility and add pyotp support imagery: Update i.eodag for EODAG v4 compatibility May 6, 2026
@landam
Copy link
Copy Markdown
Member

landam commented May 6, 2026

I have successfully used the tool to download a sample Sentinel-2 scene from Copernicus Data Space using both EODAG versions 3.10 and 4.2. The tests also ran successfully (3.10 and 4.2) with one exception:

FAIL: test_text_file_with_ids (__main__.TestEodag.test_text_file_with_ids)
Test searching for products from a text file.
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/lib/grass84/etc/python/grass/gunittest/case.py", line 1394, in assertModule
    module.run()
  File "/usr/lib/grass84/etc/python/grass/pygrass/modules/interface/module.py", line 838, in run
    self.wait()
  File "/usr/lib/grass84/etc/python/grass/pygrass/modules/interface/module.py", line 859, in wait
    raise CalledModuleError(
grass.exceptions.CalledModuleError: Module run `i.eodag producttype=S2_MSI_L2A limit=50 file=testsuite/data/ids_list.txt provider=cop_dataspace sort=cloudcover order=asc timeout=300 wait=2 -l --q` ended with an error.
The subprocess ended with a non-zero return code: 1. See the following errors:
b'peps: could not create provider from scratch using config\ntheia: could not create provider from scratch using config\nonda: could not create provider from scratch using config\nastraea_eod: could not create provider from scratch using config\nearth_search_cog: could not create provider from scratch using config\nwekeo: could not create provider from scratch using config\nERROR: Could not open file "testsuite/data/ids_list.txt"\n'

This test doesn't fail on the original code, it's seems to be cased by this PR. @polucifier Can you take a look on this issue, please?

By the way when using EODAG v4.2 I am getting:

DeprecationWarning: Call to deprecated eodag function/method as_geojson_object (Please use 'SearchResult.as_dict' instead) -- Deprecated since v4.1.0

Would be nice to address this issue.

Use `SearchResult.as_dict()` instead of the deprecated `as_geojson_object()` for EODAG versions >= 4.1.0, while maintaining backward compatibility for older versions.
@polucifier polucifier marked this pull request as draft May 6, 2026 17:51
Reverted the file path in `test_text_file_with_ids` back to `data/ids_list.txt`. The previous path caused tests to fail in the GitHub Actions CI environment due to relative path resolution issues.
@polucifier
Copy link
Copy Markdown
Author

This test doesn't fail on the original code, it's seems to be cased by this PR. @polucifier Can you take a look on this issue, please?

By the way when using EODAG v4.2 I am getting:

DeprecationWarning: Call to deprecated eodag function/method as_geojson_object (Please use 'SearchResult.as_dict' instead) -- Deprecated since v4.1.0

Would be nice to address this issue.

Hi @landam, thank you for the review. Both the test failure and the deprecation warning should be fixed now.

@polucifier polucifier marked this pull request as ready for review May 6, 2026 23:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working Python Related code is in Python

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] i.eodag do not support EODAG v4

4 participants