Skip to content

Commit 6475f28

Browse files
Merge pull request #1154 from planetlabs/main
catchup from main
2 parents 5e8c2d2 + 7d17324 commit 6475f28

File tree

10 files changed

+108
-132
lines changed

10 files changed

+108
-132
lines changed

planet/clients/base.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
from typing import Any, AsyncIterator, Coroutine, Iterator, TypeVar
2+
from planet.http import Session
3+
4+
T = TypeVar("T")
5+
6+
7+
class _BaseClient:
8+
9+
def __init__(self, session: Session, base_url: str):
10+
"""
11+
Parameters:
12+
session: Open session connected to server.
13+
base_url: The base URL to use. Defaults to production data API
14+
base url.
15+
"""
16+
self._session = session
17+
18+
self._base_url = base_url
19+
if self._base_url.endswith('/'):
20+
self._base_url = self._base_url[:-1]
21+
22+
def _call_sync(self, f: Coroutine[Any, Any, T]) -> T:
23+
"""block on an async function call, using the call_sync method of the session"""
24+
return self._session._call_sync(f)
25+
26+
def _aiter_to_iter(self, aiter: AsyncIterator[T]) -> Iterator[T]:
27+
return self._session._aiter_to_iter(aiter)

planet/clients/data.py

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@
1717
import logging
1818
from pathlib import Path
1919
import time
20-
from typing import Any, AsyncIterator, Awaitable, Callable, Dict, List, Optional, TypeVar, Union
20+
from typing import Any, AsyncIterator, Callable, Dict, List, Optional, TypeVar, Union
2121
import uuid
2222

23+
from planet.clients.base import _BaseClient
24+
2325
from ..data_filter import empty_filter
2426
from .. import exceptions
2527
from ..constants import PLANET_BASE_URL
@@ -67,7 +69,7 @@ class Searches(Paged):
6769
ITEMS_KEY = 'searches'
6870

6971

70-
class DataClient:
72+
class DataClient(_BaseClient):
7173
"""High-level asynchronous access to Planet's data API.
7274
7375
Example:
@@ -92,15 +94,7 @@ def __init__(self, session: Session, base_url: Optional[str] = None):
9294
base_url: The base URL to use. Defaults to production data API
9395
base url.
9496
"""
95-
self._session = session
96-
97-
self._base_url = base_url or BASE_URL
98-
if self._base_url.endswith('/'):
99-
self._base_url = self._base_url[:-1]
100-
101-
def _call_sync(self, f: Awaitable[T]) -> T:
102-
"""block on an async function call, using the call_sync method of the session"""
103-
return self._session._call_sync(f)
97+
super().__init__(session, base_url or BASE_URL)
10498

10599
@staticmethod
106100
def _check_search_id(sid):

planet/clients/features.py

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@
1313
# the License.
1414

1515
import logging
16-
from typing import Any, AsyncIterator, Awaitable, Optional, Union, TypeVar
16+
from typing import Any, AsyncIterator, Optional, Union, TypeVar
1717

18+
from planet.clients.base import _BaseClient
1819
from planet.http import Session
1920
from planet.models import Feature, GeoInterface, Paged
2021
from planet.constants import PLANET_BASE_URL
@@ -26,7 +27,7 @@
2627
LOGGER = logging.getLogger()
2728

2829

29-
class FeaturesClient:
30+
class FeaturesClient(_BaseClient):
3031
"""Asyncronous Features API client
3132
3233
For more information about the Features API, see the documentation at
@@ -55,15 +56,7 @@ def __init__(self,
5556
base_url: The base URL to use. Defaults to the Features
5657
API base url at api.planet.com.
5758
"""
58-
self._session = session
59-
60-
self._base_url = base_url or BASE_URL
61-
if self._base_url.endswith('/'):
62-
self._base_url = self._base_url[:-1]
63-
64-
def _call_sync(self, f: Awaitable[T]) -> T:
65-
"""block on an async function call, using the call_sync method of the session"""
66-
return self._session._call_sync(f)
59+
super().__init__(session, base_url or BASE_URL)
6760

6861
async def list_collections(self, limit: int = 0) -> AsyncIterator[dict]:
6962
"""

planet/clients/orders.py

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,14 @@
1616
import asyncio
1717
import logging
1818
import time
19-
from typing import AsyncIterator, Awaitable, Callable, Dict, List, Optional, Sequence, TypeVar, Union
19+
from typing import AsyncIterator, Callable, Dict, List, Optional, Sequence, TypeVar, Union
2020
import uuid
2121
import json
2222
import hashlib
2323

2424
from pathlib import Path
25+
26+
from planet.clients.base import _BaseClient
2527
from .. import exceptions
2628
from ..constants import PLANET_BASE_URL
2729
from ..http import Session
@@ -68,7 +70,7 @@ def is_final(cls, test):
6870
return cls.passed('running', test)
6971

7072

71-
class OrdersClient:
73+
class OrdersClient(_BaseClient):
7274
"""High-level asynchronous access to Planet's orders API.
7375
7476
Example:
@@ -93,15 +95,7 @@ def __init__(self, session: Session, base_url: Optional[str] = None):
9395
base_url: The base URL to use. Defaults to production orders API
9496
base url.
9597
"""
96-
self._session = session
97-
98-
self._base_url = base_url or BASE_URL
99-
if self._base_url.endswith('/'):
100-
self._base_url = self._base_url[:-1]
101-
102-
def _call_sync(self, f: Awaitable[T]) -> T:
103-
"""block on an async function call, using the call_sync method of the session"""
104-
return self._session._call_sync(f)
98+
super().__init__(session, base_url or BASE_URL)
10599

106100
@staticmethod
107101
def _check_order_id(oid):

planet/clients/subscriptions.py

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
"""Planet Subscriptions API Python client."""
22

33
import logging
4-
from typing import Any, AsyncIterator, Awaitable, Dict, Optional, Sequence, TypeVar, List
4+
from typing import Any, AsyncIterator, Dict, Optional, Sequence, TypeVar, List
55

66
from typing_extensions import Literal
77

8+
from planet.clients.base import _BaseClient
89
from planet.exceptions import APIError, ClientError
910
from planet.http import Session
1011
from planet.models import Paged
@@ -17,7 +18,7 @@
1718
T = TypeVar("T")
1819

1920

20-
class SubscriptionsClient:
21+
class SubscriptionsClient(_BaseClient):
2122
"""A Planet Subscriptions Service API 1.0.0 client.
2223
2324
The methods of this class forward request parameters to the
@@ -55,15 +56,7 @@ def __init__(self,
5556
base_url: The base URL to use. Defaults to production subscriptions
5657
API base url.
5758
"""
58-
self._session = session
59-
60-
self._base_url = base_url or BASE_URL
61-
if self._base_url.endswith('/'):
62-
self._base_url = self._base_url[:-1]
63-
64-
def _call_sync(self, f: Awaitable[T]) -> T:
65-
"""block on an async function call, using the call_sync method of the session"""
66-
return self._session._call_sync(f)
59+
super().__init__(session, base_url or BASE_URL)
6760

6861
async def list_subscriptions(self,
6962
status: Optional[Sequence[str]] = None,

planet/http.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
import random
2323
import threading
2424
import time
25-
from typing import AsyncGenerator, Awaitable, Optional, TypeVar
25+
from typing import Any, AsyncGenerator, AsyncIterator, Awaitable, Coroutine, Iterator, Optional, TypeVar
2626

2727
import httpx
2828
from typing_extensions import Literal
@@ -293,10 +293,26 @@ def _start_background_loop(loop):
293293
daemon=True)
294294
self._loop_thread.start()
295295

296-
def _call_sync(self, f: Awaitable[T]) -> T:
296+
def _call_sync(self, f: Coroutine[Any, Any, T]) -> T:
297297
self._init_loop()
298298
return asyncio.run_coroutine_threadsafe(f, self._loop).result()
299299

300+
def _aiter_to_iter(self, aiter: AsyncIterator[T]) -> Iterator[T]:
301+
self._init_loop()
302+
303+
# this turns an awaitable into a coroutine - works around typing
304+
# check on run_coroutine_threadsafe (which actually does check if
305+
# the argument is a coroutine)
306+
async def coro(a: Awaitable[T]) -> T:
307+
return await a
308+
309+
try:
310+
while True:
311+
yield asyncio.run_coroutine_threadsafe(coro(aiter.__anext__()),
312+
self._loop).result()
313+
except StopAsyncIteration:
314+
pass
315+
300316
@classmethod
301317
async def _raise_for_status(cls, response):
302318
if response.is_error:

planet/sync/data.py

Lines changed: 14 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
# the License.
1414
"""Functionality for interacting with the data api"""
1515
from pathlib import Path
16-
from typing import Any, Callable, Dict, Iterator, List, Optional, Union
16+
from typing import Any, Callable, Dict, Iterator, List, Optional, TypeVar, Union
1717

1818
from planet.models import GeojsonLike
1919

@@ -27,6 +27,8 @@
2727
WAIT_DELAY = 5
2828
WAIT_MAX_ATTEMPTS = 200
2929

30+
T = TypeVar("T")
31+
3032

3133
class DataAPI:
3234
"""Data API client"""
@@ -75,18 +77,13 @@ def search(
7577
references
7678
"""
7779

78-
results = self._client.search(item_types,
79-
search_filter,
80-
name,
81-
sort,
82-
limit,
83-
geometry)
84-
85-
try:
86-
while True:
87-
yield self._client._call_sync(results.__anext__())
88-
except StopAsyncIteration:
89-
pass
80+
return self._client._aiter_to_iter(
81+
self._client.search(item_types,
82+
search_filter,
83+
name,
84+
sort,
85+
limit,
86+
geometry))
9087

9188
def create_search(
9289
self,
@@ -183,13 +180,9 @@ def list_searches(self,
183180
planet.exceptions.ClientError: If sort or search_type are not
184181
valid.
185182
"""
186-
results = self._client.list_searches(sort, search_type, limit)
187183

188-
try:
189-
while True:
190-
yield self._client._call_sync(results.__anext__())
191-
except StopAsyncIteration:
192-
pass
184+
return self._client._aiter_to_iter(
185+
self._client.list_searches(sort, search_type, limit))
193186

194187
def delete_search(self, search_id: str):
195188
"""Delete an existing saved search.
@@ -242,13 +235,8 @@ def run_search(self,
242235
planet.exceptions.ClientError: If search_id or sort is not valid.
243236
"""
244237

245-
results = self._client.run_search(search_id, sort, limit)
246-
247-
try:
248-
while True:
249-
yield self._client._call_sync(results.__anext__())
250-
except StopAsyncIteration:
251-
pass
238+
return self._client._aiter_to_iter(
239+
self._client.run_search(search_id, sort, limit))
252240

253241
def get_stats(self,
254242
item_types: List[str],

planet/sync/features.py

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,8 @@ def list_collections(self, limit: int = 0) -> Iterator[dict]:
4444
print(collection)
4545
```
4646
"""
47-
collections = self._client.list_collections(limit=limit)
48-
49-
try:
50-
while True:
51-
yield self._client._call_sync(collections.__anext__())
52-
except StopAsyncIteration:
53-
pass
47+
return self._client._aiter_to_iter(
48+
self._client.list_collections(limit=limit))
5449

5550
def get_collection(self, collection_id: str) -> dict:
5651
"""
@@ -109,13 +104,8 @@ def list_items(self,
109104
results = pl.data.search(["PSScene"], geometry=feature])
110105
```
111106
"""
112-
results = self._client.list_items(collection_id, limit=limit)
113-
114-
try:
115-
while True:
116-
yield self._client._call_sync(results.__anext__())
117-
except StopAsyncIteration:
118-
pass
107+
return self._client._aiter_to_iter(
108+
self._client.list_items(collection_id, limit=limit))
119109

120110
def get_item(self, collection_id: str, feature_id: str) -> Feature:
121111
"""

planet/sync/orders.py

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -307,18 +307,13 @@ def list_orders(self,
307307
planet.exceptions.APIError: On API error.
308308
planet.exceptions.ClientError: If state is not valid.
309309
"""
310-
results = self._client.list_orders(state,
311-
limit,
312-
source_type,
313-
name,
314-
name__contains,
315-
created_on,
316-
last_modified,
317-
hosting,
318-
sort_by)
319-
320-
try:
321-
while True:
322-
yield self._client._call_sync(results.__anext__())
323-
except StopAsyncIteration:
324-
pass
310+
return self._client._aiter_to_iter(
311+
self._client.list_orders(state,
312+
limit,
313+
source_type,
314+
name,
315+
name__contains,
316+
created_on,
317+
last_modified,
318+
hosting,
319+
sort_by))

0 commit comments

Comments
 (0)