From 92991968a4a0c8f8fb2f549db7f9607fb8448585 Mon Sep 17 00:00:00 2001 From: BRAUN REMI Date: Mon, 29 Jul 2024 10:02:48 +0200 Subject: [PATCH] FIX: Don't make `pystac` a mandatory requirement --- CHANGES.md | 1 + eoreader/products/optical/landsat_product.py | 11 +---- eoreader/products/optical/s2_e84_product.py | 12 +----- eoreader/products/optical/s2_mpc_product.py | 12 +----- eoreader/products/optical/s2_product.py | 11 +---- eoreader/products/sar/s1_rtc_mpc_product.py | 11 +---- eoreader/products/stac_product.py | 33 +++++++++++++++ eoreader/reader.py | 42 +++++++++++++++----- requirements.txt | 1 + 9 files changed, 73 insertions(+), 61 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index f9d2e426..5146525e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -14,6 +14,7 @@ - FIX: Handle wrongly recognized Planet products because of the recursive nested mtd in the Reader ([#169](https://github.com/sertit/eoreader/issues/169)) - FIX: Force the loading of `DimapV1` bands in `float32` - FIX: Handle the case where fiona isn't installed anymore (with `geopandas 1.0`) +- FIX: Don't make `pystac` a mandatory requirement - OPTIM: Search correctly nested metadata in the Reader (without accidentally using a recursive glob) - CI: Fix S3 endpoint management with `sertit>=1.37` diff --git a/eoreader/products/optical/landsat_product.py b/eoreader/products/optical/landsat_product.py index 17a9da84..d1733ac7 100644 --- a/eoreader/products/optical/landsat_product.py +++ b/eoreader/products/optical/landsat_product.py @@ -28,7 +28,6 @@ import xarray as xr from lxml import etree from lxml.builder import E -from pystac import Item from rasterio.enums import Resampling from sertit import AnyPath, path, rasters, rasters_rio from sertit.misc import ListEnum @@ -1865,16 +1864,8 @@ def __init__( super_kwargs = kwargs.copy() # Get STAC Item - self.item = None + self.item = self._set_item(product_path, **super_kwargs) """ STAC Item of the product """ - self.item = super_kwargs.pop("item", None) - if self.item is None: - try: - self.item = Item.from_file(product_path) - except TypeError: - raise InvalidProductError( - "You should either fill 'product_path' or 'item'." - ) if not self._is_mpc(): self.default_clients = [ diff --git a/eoreader/products/optical/s2_e84_product.py b/eoreader/products/optical/s2_e84_product.py index 679cf536..aeaba10b 100644 --- a/eoreader/products/optical/s2_e84_product.py +++ b/eoreader/products/optical/s2_e84_product.py @@ -25,7 +25,6 @@ import numpy as np import xarray as xr from lxml import etree -from pystac import Item from rasterio.enums import Resampling from sertit import AnyPath, files, path, rasters_rio from sertit.files import CustomDecoder @@ -795,16 +794,7 @@ def __init__( super_kwargs = kwargs.copy() # Get STAC Item - self.item = None - """ STAC Item of the product """ - self.item = super_kwargs.pop("item", None) - if self.item is None: - try: - self.item = Item.from_file(product_path) - except TypeError: - raise InvalidProductError( - "You should either fill 'product_path' or 'item'." - ) + self.item = self._set_item(product_path, **super_kwargs) if not self._is_mpc(): self.default_clients = [self.get_e84_client(), self.get_sinergise_client()] diff --git a/eoreader/products/optical/s2_mpc_product.py b/eoreader/products/optical/s2_mpc_product.py index 98ff67b4..8f26d82b 100644 --- a/eoreader/products/optical/s2_mpc_product.py +++ b/eoreader/products/optical/s2_mpc_product.py @@ -21,13 +21,11 @@ import numpy as np import xarray as xr from lxml import etree -from pystac import Item from sertit import AnyPath from sertit.types import AnyPathStrType, AnyPathType from eoreader import EOREADER_NAME from eoreader.bands import BandNames -from eoreader.exceptions import InvalidProductError from eoreader.products import S2E84Product from eoreader.products.optical.optical_product import RawUnits from eoreader.products.stac_product import StacProduct @@ -54,16 +52,8 @@ def __init__( super_kwargs = kwargs.copy() # Get STAC Item - self.item = None + self.item = self._set_item(product_path, **super_kwargs) """ STAC Item of the product """ - self.item = super_kwargs.pop("item", None) - if self.item is None: - try: - self.item = Item.from_file(product_path) - except TypeError: - raise InvalidProductError( - "You should either fill 'product_path' or 'item'." - ) self.default_clients = [] self.clients = super_kwargs.pop("client", self.default_clients) diff --git a/eoreader/products/optical/s2_product.py b/eoreader/products/optical/s2_product.py index 755780ec..0998ef56 100644 --- a/eoreader/products/optical/s2_product.py +++ b/eoreader/products/optical/s2_product.py @@ -33,7 +33,6 @@ import xarray as xr from affine import Affine from lxml import etree -from pystac import Item from rasterio import errors, features, transform from rasterio.crs import CRS from rasterio.enums import Resampling @@ -1725,16 +1724,8 @@ def __init__( super_kwargs = kwargs.copy() # Get STAC Item - self.item = None + self.item = self._set_item(product_path, **super_kwargs) """ STAC Item of the product """ - self.item = super_kwargs.pop("item", None) - if self.item is None: - try: - self.item = Item.from_file(product_path) - except TypeError: - raise InvalidProductError( - "You should either fill 'product_path' or 'item'." - ) if not self._is_mpc(): self.default_clients = [ diff --git a/eoreader/products/sar/s1_rtc_mpc_product.py b/eoreader/products/sar/s1_rtc_mpc_product.py index 9b8c354c..d7c9cc1b 100644 --- a/eoreader/products/sar/s1_rtc_mpc_product.py +++ b/eoreader/products/sar/s1_rtc_mpc_product.py @@ -24,7 +24,6 @@ from typing import Union from lxml import etree -from pystac import Item from sertit import AnyPath, path, xml from sertit.types import AnyPathStrType @@ -61,16 +60,8 @@ def __init__( super_kwargs = kwargs.copy() # Get STAC Item - self.item = None + self.item = self._set_item(product_path, **super_kwargs) """ STAC Item of the product """ - self.item = super_kwargs.pop("item", None) - if self.item is None: - try: - self.item = Item.from_file(product_path) - except TypeError: - raise InvalidProductError( - "You should either fill 'product_path' or 'item'." - ) # Nothing here works for MPC self.default_clients = [] diff --git a/eoreader/products/stac_product.py b/eoreader/products/stac_product.py index 13b8d47f..3382c7ff 100644 --- a/eoreader/products/stac_product.py +++ b/eoreader/products/stac_product.py @@ -24,12 +24,19 @@ from lxml import etree from rasterio import crs from sertit import geometry, path, rasters, vectors +from sertit.types import AnyPathStrType from eoreader import EOREADER_NAME, cache +from eoreader.exceptions import InvalidProductError from eoreader.products.product import Product from eoreader.stac import PROJ_EPSG from eoreader.utils import simplify +try: + from pystac import Item +except ModuleNotFoundError: + from typing import Any as Item + LOGGER = logging.getLogger(EOREADER_NAME) @@ -45,6 +52,32 @@ class StacProduct(Product): clients = None default_clients = None + def _set_item(self, product_path: AnyPathStrType, **kwargs) -> Item: + """ + Set the STAC Item as member + + Args: + product_path (AnyPathStrType): Product path + **kwargs: Other argumlents + """ + item = kwargs.pop("item", None) + if item is None: + try: + import pystac + + item = pystac.Item.from_file(product_path) + except ModuleNotFoundError: + raise InvalidProductError( + "You should install 'pystac' to use STAC Products." + ) + + except TypeError: + raise InvalidProductError( + "You should either fill 'product_path' or 'item'." + ) + + return item + @cache def extent(self) -> gpd.GeoDataFrame: """ diff --git a/eoreader/reader.py b/eoreader/reader.py index 888ec37a..03b4a457 100644 --- a/eoreader/reader.py +++ b/eoreader/reader.py @@ -25,9 +25,7 @@ from typing import Union from zipfile import BadZipFile -import pystac import validators -from pystac import Item from sertit import AnyPath, path, strings from sertit.misc import ListEnum from sertit.types import AnyPathStrType @@ -35,6 +33,16 @@ from eoreader import EOREADER_NAME from eoreader.exceptions import InvalidProductError +try: + import pystac + from pystac import Item + + PYSTAC_INSTALLED = True +except ModuleNotFoundError: + from typing import Any as Item + + PYSTAC_INSTALLED = False + LOGGER = logging.getLogger(EOREADER_NAME) @@ -512,16 +520,32 @@ def open( Returns: Product: EOReader's product """ - # If an URL is given, it must point to an URL translatable to a STAC Item + prod = None + # If a URL is given, it must point to a URL translatable to a STAC Item if validators.url(product_path): - try: - product_path = pystac.Item.from_file(product_path) - except Exception: - raise InvalidProductError( - f"Cannot convert your URL ({product_path}) to a STAC Item." + if PYSTAC_INSTALLED: + try: + product_path = Item.from_file(product_path) + is_stac = True + except Exception: + raise InvalidProductError( + f"Cannot convert your URL ({product_path}) to a STAC Item." + ) + else: + raise ModuleNotFoundError( + "You should install 'pystac' to use STAC Products." ) + # Check path (first check URL as they are also strings) + elif path.is_path(product_path): + is_stac = False + else: + # Check STAC Item + if PYSTAC_INSTALLED: + is_stac = isinstance(product_path, pystac.Item) + else: + is_stac = False - if isinstance(product_path, Item): + if is_stac: prod = self._open_stac_item(product_path, output_path, remove_tmp, **kwargs) else: # If not an Item, it should be a path to somewhere diff --git a/requirements.txt b/requirements.txt index fdebdc9b..8c14a839 100644 --- a/requirements.txt +++ b/requirements.txt @@ -47,6 +47,7 @@ methodtools matplotlib # MPC, AWS and STAC +# /!\ Should not be mandatory requirements! pystac[validation] stac-asset planetary_computer