diff --git a/CHANGELOG.md b/CHANGELOG.md
index f9deb1d..1e11f35 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
+### Added
+
+`tilebox-storage`: Added a `LocalFileSystemStorageClient` to access data on a local file system, a mounted network file
+system or a syncified directory with a remote file system (e.g. Dropbox, Google Drive, etc.).
+
+### Changed
+
+`tilebox-storage`: Renamed the existing `StorageClient` base class in `tilebox.storage.aio` to `CachingStorageClient`
+to accomodate the new `StorageClient` base class that does not provide caching, since `LocalFileSystemStorageClient` is
+the first client that does not cache data (since it's already on the local file system).
+
## [0.47.0] - 2026-01-28
### Added
diff --git a/tilebox-storage/tilebox/storage/__init__.py b/tilebox-storage/tilebox/storage/__init__.py
index 28975f7..e7785be 100644
--- a/tilebox-storage/tilebox/storage/__init__.py
+++ b/tilebox-storage/tilebox/storage/__init__.py
@@ -2,6 +2,7 @@
from tilebox.storage.aio import ASFStorageClient as _ASFStorageClient
from tilebox.storage.aio import CopernicusStorageClient as _CopernicusStorageClient
+from tilebox.storage.aio import LocalFileSystemStorageClient as _LocalFileSystemStorageClient
from tilebox.storage.aio import UmbraStorageClient as _UmbraStorageClient
from tilebox.storage.aio import USGSLandsatStorageClient as _USGSLandsatStorageClient
@@ -66,3 +67,14 @@ def __init__(self, cache_directory: Path | None = Path.home() / ".cache" / "tile
"""
super().__init__(cache_directory)
self._syncify()
+
+
+class LocalFileSystemStorageClient(_LocalFileSystemStorageClient):
+ def __init__(self, root: Path) -> None:
+ """A tilebox storage client for accessing data on a local file system, or a mounted network file system.
+
+ Args:
+ root: The root directory of the file system to access.
+ """
+ super().__init__(root)
+ self._syncify()
diff --git a/tilebox-storage/tilebox/storage/aio.py b/tilebox-storage/tilebox/storage/aio.py
index 1ad4a9d..ce5bda6 100644
--- a/tilebox-storage/tilebox/storage/aio.py
+++ b/tilebox-storage/tilebox/storage/aio.py
@@ -24,6 +24,7 @@
from tilebox.storage.granule import (
ASFStorageGranule,
CopernicusStorageGranule,
+ LocationStorageGranule,
UmbraStorageGranule,
USGSLandsatStorageGranule,
)
@@ -241,6 +242,10 @@ def _display_quicklook(image_data: bytes | Path, width: int, height: int, image_
class StorageClient(Syncifiable):
+ """Base class for all storage clients."""
+
+
+class CachingStorageClient(StorageClient):
def __init__(self, cache_directory: Path | None) -> None:
self._cache = cache_directory
@@ -323,7 +328,7 @@ async def _download_object(
return output_path
-class ASFStorageClient(StorageClient):
+class ASFStorageClient(CachingStorageClient):
def __init__(self, user: str, password: str, cache_directory: Path = Path.home() / ".cache" / "tilebox") -> None:
"""A tilebox storage client that downloads data from the Alaska Satellite Facility.
@@ -415,7 +420,7 @@ async def quicklook(self, datapoint: xr.Dataset | ASFStorageGranule, width: int
"""
granule = ASFStorageGranule.from_data(datapoint)
if Image is None:
- raise ImportError("IPython is not available, please use download_preview instead.")
+ raise ImportError("IPython is not available, please use download_quicklook instead.")
quicklook = await self._download_quicklook(datapoint)
_display_quicklook(quicklook, width, height, f"Image {quicklook.name} © ASF {granule.time.year}")
@@ -439,7 +444,7 @@ def _umbra_s3_prefix(datapoint: xr.Dataset | UmbraStorageGranule) -> str:
return f"sar-data/tasks/{granule.location}/"
-class UmbraStorageClient(StorageClient):
+class UmbraStorageClient(CachingStorageClient):
_STORAGE_PROVIDER = "Umbra"
_BUCKET = "umbra-open-data-catalog"
_REGION = "us-west-2"
@@ -539,7 +544,7 @@ def _copernicus_s3_prefix(datapoint: xr.Dataset | CopernicusStorageGranule) -> s
return granule.location.removeprefix("/eodata/")
-class CopernicusStorageClient(StorageClient):
+class CopernicusStorageClient(CachingStorageClient):
_STORAGE_PROVIDER = "CopernicusDataspace"
_BUCKET = "eodata"
_ENDPOINT_URL = "https://eodata.dataspace.copernicus.eu"
@@ -724,7 +729,7 @@ async def quicklook(
ValueError: If no quicklook is available for the given datapoint.
"""
if Image is None:
- raise ImportError("IPython is not available, please use download_preview instead.")
+ raise ImportError("IPython is not available, please use download_quicklook instead.")
granule = CopernicusStorageGranule.from_data(datapoint)
quicklook = await self._download_quicklook(granule)
_display_quicklook(quicklook, width, height, f"{granule.granule_name} © ESA {granule.time.year}")
@@ -750,7 +755,7 @@ def _landsat_s3_prefix(datapoint: xr.Dataset | USGSLandsatStorageGranule) -> str
return granule.location.removeprefix("s3://usgs-landsat/")
-class USGSLandsatStorageClient(StorageClient):
+class USGSLandsatStorageClient(CachingStorageClient):
"""
A client for downloading USGS Landsat data from the usgs-landsat and usgs-landsat-ard S3 bucket.
@@ -883,7 +888,7 @@ async def quicklook(
ValueError: If no quicklook is available for the given datapoint.
"""
if Image is None:
- raise ImportError("IPython is not available, please use download_preview instead.")
+ raise ImportError("IPython is not available, please use download_quicklook instead.")
quicklook = await self._download_quicklook(datapoint)
_display_quicklook(quicklook, width, height, f"Image {quicklook.name} © USGS")
@@ -901,3 +906,77 @@ async def _download_quicklook(self, datapoint: xr.Dataset | USGSLandsatStorageGr
await download_objects(self._store, prefix, [granule.thumbnail], output_folder, show_progress=False)
return output_folder / granule.thumbnail
+
+
+class LocalFileSystemStorageClient(StorageClient):
+ def __init__(self, root: Path) -> None:
+ """A tilebox storage client for accessing data on a local file system, or a mounted network file system.
+
+ Args:
+ root: The root directory of the file system to access.
+ """
+ super().__init__()
+ self._root = Path(root)
+
+ async def list_objects(self, datapoint: xr.Dataset | LocationStorageGranule) -> list[str]:
+ """List all available objects for a given datapoint."""
+ granule = LocationStorageGranule.from_data(datapoint)
+ granule_path = self._root / granule.location
+ return [p.relative_to(granule_path).as_posix() for p in granule_path.rglob("**/*") if p.is_file()]
+
+ async def download(
+ self,
+ datapoint: xr.Dataset | LocationStorageGranule,
+ ) -> Path:
+ """No-op download method, as the data is already on the local file system.
+
+ Args:
+ datapoint: The datapoint to locate the data for in the local file system.
+
+ Returns:
+ The path to the data on the local file system.
+ """
+ granule = LocationStorageGranule.from_data(datapoint)
+ granule_path = self._root / granule.location
+ if not granule_path.exists():
+ raise ValueError(f"Data not found on the local file system: {granule_path}")
+ return granule_path
+
+ async def _download_quicklook(self, datapoint: xr.Dataset | LocationStorageGranule) -> Path:
+ granule = LocationStorageGranule.from_data(datapoint)
+ if granule.thumbnail is None:
+ raise ValueError(f"No quicklook available for {granule.location}")
+ quicklook_path = self._root / granule.thumbnail
+ if not quicklook_path.exists():
+ raise ValueError(f"Quicklook not found on the local file system: {quicklook_path}")
+ return quicklook_path
+
+ async def download_quicklook(self, datapoint: xr.Dataset | LocationStorageGranule) -> Path:
+ """No-op download_quicklook method, as the quicklook image is already on the local file system.
+
+ Args:
+ datapoint: The datapoint to locate the quicklook image for in the local file system.
+
+ Returns:
+ The path to the data on the local file system.
+
+ Raises:
+ ValueError: If no quicklook image is available for the given datapoint, or if the quicklook image is not
+ found on the local file system.
+ """
+ return await self._download_quicklook(datapoint)
+
+ async def quicklook(
+ self, datapoint: xr.Dataset | LocationStorageGranule, width: int = 600, height: int = 600
+ ) -> None:
+ """Display the quicklook image for a given datapoint.
+
+ Args:
+ datapoint: The datapoint to display the quicklook for.
+ width: Display width of the image in pixels. Defaults to 600.
+ height: Display height of the image in pixels. Defaults to 600.
+ """
+ quicklook_path = await self._download_quicklook(datapoint)
+ if Image is None:
+ raise ImportError("IPython is not available, please use download_quicklook instead.")
+ _display_quicklook(quicklook_path, width, height, None)
diff --git a/tilebox-storage/tilebox/storage/granule.py b/tilebox-storage/tilebox/storage/granule.py
index 88c135d..f214bba 100644
--- a/tilebox-storage/tilebox/storage/granule.py
+++ b/tilebox-storage/tilebox/storage/granule.py
@@ -183,3 +183,28 @@ def from_data(cls, dataset: "xr.Dataset | USGSLandsatStorageGranule") -> "USGSLa
dataset.location.item().replace("s3://usgs-landsat-ard/", "s3://usgs-landsat/"),
thumbnail,
)
+
+
+@dataclass
+class LocationStorageGranule:
+ location: str
+ thumbnail: str | None = None
+
+ @classmethod
+ def from_data(cls, dataset: "xr.Dataset | LocationStorageGranule") -> "LocationStorageGranule":
+ """Extract the granule information from a datapoint given as xarray dataset."""
+ if isinstance(dataset, LocationStorageGranule):
+ return dataset
+
+ if "location" not in dataset:
+ raise ValueError("The given dataset has no location information.")
+
+ thumbnail = None
+ if "thumbnail" in dataset:
+ thumbnail = dataset.thumbnail.item()
+ elif "overview" in dataset:
+ thumbnail = dataset.overview.item()
+ elif "quicklook" in dataset:
+ thumbnail = dataset.quicklook.item()
+
+ return cls(dataset.location.item(), thumbnail)
diff --git a/uv.lock b/uv.lock
index 8315f26..5d7617e 100644
--- a/uv.lock
+++ b/uv.lock
@@ -69,30 +69,30 @@ wheels = [
[[package]]
name = "boto3"
-version = "1.42.36"
+version = "1.42.37"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "botocore" },
{ name = "jmespath" },
{ name = "s3transfer" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/54/06/50ff808cf4f40efada8edc20f9d563ab287864423c874dfb94f755a60c52/boto3-1.42.36.tar.gz", hash = "sha256:a4eb51105c8c5d7b2bc2a9e2316e69baf69a55611275b9f189c0cf59f1aae171", size = 112839, upload-time = "2026-01-27T20:38:26.992Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/a9/ef/0d6ceb88ae2b3638b956190a431e4a8a3697d5769d4bbbede8efcccacaea/boto3-1.42.37.tar.gz", hash = "sha256:d8b6c52c86f3bf04f71a5a53e7fb4d1527592afebffa5170cf3ef7d70966e610", size = 112830, upload-time = "2026-01-28T20:38:43.339Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/8d/d1/35d12f04a7792e2f5e9ddff3c7b60493a32027761380dee7f24ee8ae80cc/boto3-1.42.36-py3-none-any.whl", hash = "sha256:e0ff6f2747bfdec63405b35ea185a7aea35239c3f4fe99e4d29368a6de9c4a84", size = 140604, upload-time = "2026-01-27T20:38:25.349Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/a4/cd334f74498acc6ad42a69c48e8c495f6f721d8abe13f8ef0d4b862fb1c0/boto3-1.42.37-py3-none-any.whl", hash = "sha256:e1e38fd178ffc66cfbe9cb6838b8c460000c3eb741e5f40f57eb730780ef0ed4", size = 140604, upload-time = "2026-01-28T20:38:42.135Z" },
]
[[package]]
name = "boto3-stubs"
-version = "1.42.36"
+version = "1.42.37"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "botocore-stubs" },
{ name = "types-s3transfer" },
{ name = "typing-extensions", marker = "python_full_version < '3.12'" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/f7/bc/1d6fc5e84cc89c670a6c8c224deee2cdc8f05d0db1723912566dec26915c/boto3_stubs-1.42.36.tar.gz", hash = "sha256:0c367fbe305d9c76983989eea81e4ee2f43b992f553fdb8a0de67908877c9b00", size = 100861, upload-time = "2026-01-27T20:59:11.859Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/7f/b7/15995a1261cb3dccaee7e53e1e27d14fb542c56b95883598b53190e7d979/boto3_stubs-1.42.37.tar.gz", hash = "sha256:1620519a55bbb26cebed95b6d8f26ba96b8ea91dadd05eafc3b8f17a587e2108", size = 100870, upload-time = "2026-01-28T20:56:37.32Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/c2/4f/900008ba354fbf3861de324a685a04c352e8292a2af5fb32153378797f67/boto3_stubs-1.42.36-py3-none-any.whl", hash = "sha256:02904609e0d0aefc7f9c330b4312a5a2bff3789b03a1ba30e7cb8acb11c616a5", size = 69783, upload-time = "2026-01-27T20:59:07.746Z" },
+ { url = "https://files.pythonhosted.org/packages/12/10/ad4f3ffcdc46df83df4ba06d7692ea0869700537163cd867dd66f966835b/boto3_stubs-1.42.37-py3-none-any.whl", hash = "sha256:07b9ac27196b233b802f8fadff2fa9c01d656927943c618dc862ff00fd592b24", size = 69785, upload-time = "2026-01-28T20:56:29.211Z" },
]
[package.optional-dependencies]
@@ -108,28 +108,28 @@ essential = [
[[package]]
name = "botocore"
-version = "1.42.36"
+version = "1.42.37"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "jmespath" },
{ name = "python-dateutil" },
{ name = "urllib3" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/66/4e/b24089cf7a77d38886ac4fbae300a3c4c6d68c1b9ccb66af03cb07b6c35c/botocore-1.42.36.tar.gz", hash = "sha256:2ebd89cc75927944e2cee51b7adce749f38e0cb269a758a6464a27f8bcca65fb", size = 14909073, upload-time = "2026-01-27T20:38:16.621Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/d5/4d/94292e7686e64d2ede8dae7102bbb11a1474e407c830de4192f2518e6cff/botocore-1.42.37.tar.gz", hash = "sha256:3ec58eb98b0857f67a2ae6aa3ded51597e7335f7640be654e0e86da4f173b5b2", size = 14914621, upload-time = "2026-01-28T20:38:34.586Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/e6/e8/f14d25bd768187424b385bc6a44e2ed5e96964e461ba019add03e48713c7/botocore-1.42.36-py3-none-any.whl", hash = "sha256:2cfae4c482e5e87bd835ab4289b711490c161ba57e852c06b65a03e7c25e08eb", size = 14583066, upload-time = "2026-01-27T20:38:14.02Z" },
+ { url = "https://files.pythonhosted.org/packages/72/30/54042dd3ad8161964f8f47aa418785079bd8d2f17053c40d65bafb9f6eed/botocore-1.42.37-py3-none-any.whl", hash = "sha256:f13bb8b560a10714d96fb7b0c7f17828dfa6e6606a1ead8c01c6ebb8765acbd8", size = 14589390, upload-time = "2026-01-28T20:38:31.306Z" },
]
[[package]]
name = "botocore-stubs"
-version = "1.42.36"
+version = "1.42.37"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "types-awscrt" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/6c/9b/0fe6bdda315e43cb01719eb5b1c19224310fadac696c5b2914b6a4a686e4/botocore_stubs-1.42.36.tar.gz", hash = "sha256:b3e7505f411b26b303ed24835ac29a7aa7ec2aedcead92e4665d1b4566c396bd", size = 42410, upload-time = "2026-01-27T21:25:28.077Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/e2/d4/0348543289c57b766622958ebf0b9cc2d9ebd36e803f25e0e55455bbb165/botocore_stubs-1.42.37.tar.gz", hash = "sha256:7357d1876ae198757dbe0a73f887449ffdda18eb075d7d3cc2e22d3580dcb17c", size = 42399, upload-time = "2026-01-28T21:35:52.863Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/a3/57/a3ad25c101f8839addc754900b8ef890efb5ca350f0cd284e732d526773f/botocore_stubs-1.42.36-py3-none-any.whl", hash = "sha256:15bed118ffc4d413a75822c7182af9a9e9989ccdf5d9528bc7b98bb03edf2971", size = 66761, upload-time = "2026-01-27T21:25:26.92Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/c8/41d4e54f92865aac2afcae22c6e892659f5172b264d7dec28cf1bb36de7a/botocore_stubs-1.42.37-py3-none-any.whl", hash = "sha256:5a9b2a4062f7cc19e0648508f67d3f1a1fd8d3e0d6f5a0d3244cc9656e54cc67", size = 66761, upload-time = "2026-01-28T21:35:51.749Z" },
]
[[package]]
@@ -911,15 +911,15 @@ wheels = [
[[package]]
name = "hypothesis"
-version = "6.151.3"
+version = "6.151.4"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "exceptiongroup", marker = "python_full_version < '3.11'" },
{ name = "sortedcontainers" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/e9/aa/0e1aff2de954a1b11acf643519599c3da97d461cf62e312cab266aa9f855/hypothesis-6.151.3.tar.gz", hash = "sha256:31bcd884b0be8f478aa2e52f0f73be03e8f6a9a2c66706fb93410ebaa5c27bcc", size = 475822, upload-time = "2026-01-28T05:37:32.494Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/47/03/9fd03d5db09029250e69745c1600edab16fe90947636f77a12ba92d79939/hypothesis-6.151.4.tar.gz", hash = "sha256:658a62da1c3ccb36746ac2f7dc4bb1a6e76bd314e0dc54c4e1aaba2503d5545c", size = 475706, upload-time = "2026-01-29T01:30:14.985Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/b4/9a/e39da18745c889c7959e0ef4b53b71522062cc94714f99ac9d5b86cb222e/hypothesis-6.151.3-py3-none-any.whl", hash = "sha256:879a4b3dec37678fd1bd3a060aaaf3eedc2aab5543d9a85e5f2a04a6445683c6", size = 543275, upload-time = "2026-01-28T05:37:30.312Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/6d/01ad1b6c3b8cb2bb47eeaa9765dabc27cbe68e3b59f6cff83d5668f57780/hypothesis-6.151.4-py3-none-any.whl", hash = "sha256:a1cf7e0fdaa296d697a68ff3c0b3912c0050f07aa37e7d2ff33a966749d1d9b4", size = 543146, upload-time = "2026-01-29T01:30:12.805Z" },
]
[[package]]
@@ -1302,26 +1302,26 @@ wheels = [
[[package]]
name = "mypy-boto3-ec2"
-version = "1.42.36"
+version = "1.42.37"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions", marker = "python_full_version < '3.12'" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/40/ac/ca563f74c25937a624f797a13f90eaac51591d2c3aaba768fdea36547dee/mypy_boto3_ec2-1.42.36.tar.gz", hash = "sha256:0468a5fe39eb7d45c6e0af4aeacf25663f251acfe0b6c227effdbc2e630beec6", size = 433827, upload-time = "2026-01-27T20:54:18.76Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/06/c6/864d5fd7f0e763f5ac537905de40ffedc693ec85cc5429d02bda57ae404d/mypy_boto3_ec2-1.42.37.tar.gz", hash = "sha256:b0e169def4c7a983c8e1813c1b27b9afef331b8972df1b63d141e7732b6aa1bc", size = 434122, upload-time = "2026-01-28T20:51:36.719Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/2a/3f/fd1dae13beb721f1889140b8090321d5cc5befe13c223df04099cc296110/mypy_boto3_ec2-1.42.36-py3-none-any.whl", hash = "sha256:7ea0cd827ed77d60c0c6355b1752ac683898de6f19c2db942c7a87b5b1c483bb", size = 423968, upload-time = "2026-01-27T20:54:14.151Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/11/1c93fcd8f824c76c360ead47e83e1eb4d23b3d5c38d131482e2cdf616235/mypy_boto3_ec2-1.42.37-py3-none-any.whl", hash = "sha256:5c3c3e6fc207cc83ba31b925f4c13b4f8444791bf5c9e9a822e95d2e9c9050a2", size = 424244, upload-time = "2026-01-28T20:51:29.15Z" },
]
[[package]]
name = "mypy-boto3-lambda"
-version = "1.42.8"
+version = "1.42.37"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions", marker = "python_full_version < '3.12'" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/85/b1/d80eed0b9ec23f6995610e215928e62db130bde9a3b4bba336714b1eae83/mypy_boto3_lambda-1.42.8.tar.gz", hash = "sha256:55deadbfaf0e5f118237831a84d35f48dc7164ce2bf7efdcb54f54aef4025602", size = 51742, upload-time = "2025-12-11T22:12:46.909Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/f6/a8/caec8f9a196143f991c603b9364b0a1300b5c54d0c833c6c1dc792b3050c/mypy_boto3_lambda-1.42.37.tar.gz", hash = "sha256:94f7f0708f9b5ffa5b8b3eb6d564be1ef402ebb8b8cd96045332b7a3bc1ea0e0", size = 51821, upload-time = "2026-01-28T20:51:44.74Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/ca/16/0cdb7af03c752146e603ea5cb2bac531f5099f3850bfee89f0806671d968/mypy_boto3_lambda-1.42.8-py3-none-any.whl", hash = "sha256:fbb6646138520c675a4c4adff334e830b010d5c077dee8d5187346809ebb6f72", size = 60081, upload-time = "2025-12-11T22:12:38.099Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/4c/d172fac61aa689c96fe286e7adb9774e5094f5c0a52fa1a6888699b47d7f/mypy_boto3_lambda-1.42.37-py3-none-any.whl", hash = "sha256:9614518cbe3c300d3d1e2d9c3d857c3829c44a8544c4cd4ca393d35181b22619", size = 60175, upload-time = "2026-01-28T20:51:39.865Z" },
]
[[package]]
@@ -1338,14 +1338,14 @@ wheels = [
[[package]]
name = "mypy-boto3-s3"
-version = "1.42.21"
+version = "1.42.37"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions", marker = "python_full_version < '3.12'" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/a0/32/aa7208348dc8db8bd4ea357e5e6e1e8bcba44419033d03456c3b767a6c98/mypy_boto3_s3-1.42.21.tar.gz", hash = "sha256:cab71c918aac7d98c4d742544c722e37d8e7178acb8bc88a0aead7b1035026d2", size = 76024, upload-time = "2026-01-03T02:46:35.161Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/e6/41/44066f4cd3421bacb6aad4ec7b1da8d0f8858560e526166db64d95fa7ad7/mypy_boto3_s3-1.42.37.tar.gz", hash = "sha256:628a4652f727870a07e1c3854d6f30dc545a7dd5a4b719a2c59c32a95d92e4c1", size = 76317, upload-time = "2026-01-28T20:51:52.971Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/f1/c0/01babfa8cef5f992a2a0f3d52fc1123fbbc336ab6decfdfc8f702e88a8af/mypy_boto3_s3-1.42.21-py3-none-any.whl", hash = "sha256:f5b7d1ed718ba5b00f67e95a9a38c6a021159d3071ea235e6cf496e584115ded", size = 83169, upload-time = "2026-01-03T02:46:33.356Z" },
+ { url = "https://files.pythonhosted.org/packages/94/06/cb6050ecd72f5fa449bac80ad1a4711719367c4f545201317f36e3999784/mypy_boto3_s3-1.42.37-py3-none-any.whl", hash = "sha256:7c118665f3f583dbfde1013ce47908749f9d2a760f28f59ec65732306ee9cec9", size = 83439, upload-time = "2026-01-28T20:51:49.99Z" },
]
[[package]]
@@ -2418,7 +2418,7 @@ dependencies = [
{ name = "tilebox-grpc" },
{ name = "tqdm" },
{ name = "xarray", version = "2025.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
- { name = "xarray", version = "2025.12.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
+ { name = "xarray", version = "2026.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
]
[package.dev-dependencies]
@@ -2833,7 +2833,7 @@ wheels = [
[[package]]
name = "xarray"
-version = "2025.12.0"
+version = "2026.1.0"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version >= '3.14' and sys_platform == 'win32'",
@@ -2851,9 +2851,9 @@ dependencies = [
{ name = "packaging", marker = "python_full_version >= '3.11'" },
{ name = "pandas", version = "3.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/d3/af/7b945f331ba8911fdfff2fdfa092763156119f124be1ba4144615c540222/xarray-2025.12.0.tar.gz", hash = "sha256:73f6a6fadccc69c4d45bdd70821a47c72de078a8a0313ff8b1e97cd54ac59fed", size = 3082244, upload-time = "2025-12-05T21:51:22.432Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/f5/85/113ff1e2cde9e8a5b13c2f0ef4e9f5cd6ca3a036b6452f4dd523419289b5/xarray-2026.1.0.tar.gz", hash = "sha256:0c9814761f9d9a9545df37292d3fda89f83201f3e02ae0f09f03313d9cfdd5e2", size = 3107024, upload-time = "2026-01-28T17:49:03.822Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/d5/e4/62a677feefde05b12a70a4fc9bdc8558010182a801fbcab68cb56c2b0986/xarray-2025.12.0-py3-none-any.whl", hash = "sha256:9e77e820474dbbe4c6c2954d0da6342aa484e33adaa96ab916b15a786181e970", size = 1381742, upload-time = "2025-12-05T21:51:20.841Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/8e/952a351c10df395d9bab850f611f4368834ae9104d6449049f5a49e00925/xarray-2026.1.0-py3-none-any.whl", hash = "sha256:5fcc03d3ed8dfb662aa254efe6cd65efc70014182bbc2126e4b90d291d970d41", size = 1403009, upload-time = "2026-01-28T17:49:01.538Z" },
]
[[package]]