Skip to content

Commit 45bedc9

Browse files
committed
add sync client and tests, minor fixups
1 parent 915ec13 commit 45bedc9

File tree

5 files changed

+745
-33
lines changed

5 files changed

+745
-33
lines changed

planet/cli/mosaics.py

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,6 @@ def _strip_links(resource):
6060
async def _output(result, pretty, include_links=False):
6161
if asyncio.iscoroutine(result):
6262
result = await result
63-
if result is None:
64-
raise click.ClickException("not found")
6563
if not include_links:
6664
_strip_links(result)
6765
echo_json(result, pretty)
@@ -236,11 +234,8 @@ async def list_quads(ctx, name_or_id, bbox, geometry, summary, pretty, links):
236234
planet mosaics search global_monthly_2025_04_mosaic --bbox -100,40,-100,41
237235
"""
238236
async with client(ctx) as cl:
239-
mosaic = await cl.get_mosaic(name_or_id)
240-
if mosaic is None:
241-
raise click.ClickException("No mosaic named " + name_or_id)
242237
await _output(
243-
cl.list_quads(mosaic,
238+
cl.list_quads(name_or_id,
244239
minimal=False,
245240
bbox=bbox,
246241
geometry=geometry,
@@ -268,10 +263,7 @@ async def download(ctx, name_or_id, output_dir, bbox, geometry, **kwargs):
268263
"""
269264
quiet = ctx.obj['QUIET']
270265
async with client(ctx) as cl:
271-
mosaic = await cl.get_mosaic(name_or_id)
272-
if mosaic is None:
273-
raise click.ClickException("No mosaic named " + name_or_id)
274-
await cl.download_quads(mosaic,
266+
await cl.download_quads(name_or_id,
275267
bbox=bbox,
276268
geometry=geometry,
277269
directory=output_dir,

planet/clients/mosaics.py

Lines changed: 43 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,24 @@
1+
# Copyright 2025 Planet Labs PBC.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
4+
# use this file except in compliance with the License. You may obtain a copy of
5+
# the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12+
# License for the specific language governing permissions and limitations under
13+
# the License.
14+
115
import asyncio
216
from pathlib import Path
317
from typing import AsyncIterator, Awaitable, Optional, Tuple, Type, TypeVar, Union, cast
418
from planet.constants import PLANET_BASE_URL
519
from planet.exceptions import MissingResource
620
from planet.http import Session
7-
from planet.models import Mosaic, Paged, Quad, Response, Series, StreamingBody
21+
from planet.models import GeoInterface, Mosaic, Paged, Quad, Response, Series, StreamingBody
822
from uuid import UUID
923

1024
BASE_URL = f'{PLANET_BASE_URL}/basemaps/v1'
@@ -49,7 +63,7 @@ class MosaicsClient:
4963
>>>
5064
>>> async def main():
5165
... async with Session() as sess:
52-
... cl = sess.client('data')
66+
... cl = sess.client('mosaics')
5367
... # use client here
5468
...
5569
>>> asyncio.run(main())
@@ -74,7 +88,7 @@ def _call_sync(self, f: Awaitable[T]) -> T:
7488
return self._session._call_sync(f)
7589

7690
def _url(self, path: str) -> str:
77-
return f"{BASE_URL}/{path}"
91+
return f"{self._base_url}/{path}"
7892

7993
async def _get_by_name(self, path: str, pager: Type[Paged],
8094
name: str) -> dict:
@@ -88,7 +102,12 @@ async def _get_by_name(self, path: str, pager: Type[Paged],
88102
listing = response.json()[pager.ITEMS_KEY]
89103
if len(listing):
90104
return listing[0]
91-
raise MissingResource(f"{name} not found")
105+
# mimic the response for 404 when search is empty
106+
resource = "Mosaic"
107+
if path == "series":
108+
resource = "Series"
109+
raise MissingResource('{"message":"%s Not Found: %s"}' %
110+
(resource, name))
92111

93112
async def _get_by_id(self, path: str, id: str) -> dict:
94113
response = await self._session.request(method="GET",
@@ -130,7 +149,7 @@ async def list_series(
130149
name_contains: Optional[str] = None,
131150
interval: Optional[str] = None,
132151
acquired_gt: Optional[str] = None,
133-
acquired_lt: Optional[str] = None) -> AsyncIterator[dict]:
152+
acquired_lt: Optional[str] = None) -> AsyncIterator[Series]:
134153
"""
135154
List the series you have access to.
136155
@@ -157,7 +176,7 @@ async def list_series(
157176
params=params,
158177
)
159178
async for item in _SeriesPage(resp, self._session.request):
160-
yield item
179+
yield Series(item)
161180

162181
async def list_mosaics(
163182
self,
@@ -166,8 +185,7 @@ async def list_mosaics(
166185
interval: Optional[str] = None,
167186
acquired_gt: Optional[str] = None,
168187
acquired_lt: Optional[str] = None,
169-
latest: bool = False,
170-
) -> AsyncIterator[dict]:
188+
) -> AsyncIterator[Mosaic]:
171189
"""
172190
List the mosaics you have access to.
173191
@@ -188,15 +206,13 @@ async def list_mosaics(
188206
params["acquired__gt"] = acquired_gt
189207
if acquired_lt:
190208
params["acquired__lt"] = acquired_lt
191-
if latest:
192-
params["latest"] = "yes"
193209
resp = await self._session.request(
194210
method='GET',
195211
url=self._url("mosaics"),
196212
params=params,
197213
)
198214
async for item in _MosaicsPage(resp, self._session.request):
199-
yield item
215+
yield Mosaic(item)
200216

201217
async def list_series_mosaics(
202218
self,
@@ -206,7 +222,7 @@ async def list_series_mosaics(
206222
acquired_gt: Optional[str] = None,
207223
acquired_lt: Optional[str] = None,
208224
latest: bool = False,
209-
) -> AsyncIterator[dict]:
225+
) -> AsyncIterator[Mosaic]:
210226
"""
211227
List the mosaics in a series.
212228
@@ -218,6 +234,7 @@ async def list_series_mosaics(
218234
print(m)
219235
```
220236
"""
237+
series_id = series
221238
if isinstance(series, Series):
222239
series_id = series["id"]
223240
elif not _is_uuid(series):
@@ -238,15 +255,15 @@ async def list_series_mosaics(
238255
params=params,
239256
)
240257
async for item in _MosaicsPage(resp, self._session.request):
241-
yield item
258+
yield Mosaic(item)
242259

243260
async def list_quads(self,
244261
/,
245262
mosaic: Union[Mosaic, str],
246263
*,
247264
minimal: bool = False,
248265
bbox: Optional[BBox] = None,
249-
geometry: Optional[dict] = None,
266+
geometry: Optional[Union[dict, GeoInterface]] = None,
250267
summary: bool = False) -> AsyncIterator[Quad]:
251268
"""
252269
List the a mosaic's quads.
@@ -262,6 +279,8 @@ async def list_quads(self,
262279
"""
263280
mosaic = await self._resolve_mosaic(mosaic)
264281
if geometry:
282+
if isinstance(geometry, GeoInterface):
283+
geometry = geometry.__geo_interface__
265284
resp = await self._quads_geometry(mosaic,
266285
geometry,
267286
minimal,
@@ -364,14 +383,6 @@ async def download_quad(self,
364383
directory: str = ".",
365384
overwrite: bool = False,
366385
progress_bar: bool = False):
367-
url = quad["_links"]["download"]
368-
Path(directory).mkdir(exist_ok=True, parents=True)
369-
async with self._session.stream(method='GET', url=url) as resp:
370-
body = StreamingBody(resp)
371-
dest = Path(directory, body.name)
372-
await body.write(dest,
373-
overwrite=overwrite,
374-
progress_bar=progress_bar)
375386
"""
376387
Download a quad to a directory.
377388
@@ -382,6 +393,14 @@ async def download_quad(self,
382393
await client.download_quad(quad)
383394
```
384395
"""
396+
url = quad["_links"]["download"]
397+
Path(directory).mkdir(exist_ok=True, parents=True)
398+
async with self._session.stream(method='GET', url=url) as resp:
399+
body = StreamingBody(resp)
400+
dest = Path(directory, body.name)
401+
await body.write(dest,
402+
overwrite=overwrite,
403+
progress_bar=progress_bar)
385404

386405
async def download_quads(self,
387406
/,
@@ -390,7 +409,8 @@ async def download_quads(self,
390409
directory: Optional[str] = None,
391410
overwrite: bool = False,
392411
bbox: Optional[BBox] = None,
393-
geometry: Optional[dict] = None,
412+
geometry: Optional[Union[dict,
413+
GeoInterface]] = None,
394414
progress_bar: bool = False,
395415
concurrency: int = 4):
396416
"""

0 commit comments

Comments
 (0)