Repository: planetlabs/planet-client-python

Issue:
revert name change for generator functions
Feedback on the name change for generator functions (`search() -> `search_aiter()`) indicates that a doc change is adequate and name change is not desired. Revert the name change for generator functions. Also, update the usage so that generator is used where it is created, aka `items = [i async for i in search()]`

Discussion:

Patch:
diff --git a/docs/get-started/upgrading.md b/docs/get-started/upgrading.md
index 0676651..7edf21f 100644
--- a/docs/get-started/upgrading.md
+++ b/docs/get-started/upgrading.md
@@ -35,7 +35,7 @@ For more information about Session, refer to the [Python SDK User Guide](../../p
 
 With the V1 client, all communication was synchronous. Asynchronous bulk support was provided with the `downloader` module. There was no built-in support for polling when an order was ready to download or tracking when an order was downloaded.
 
-In V2, all `*Client` methods (for example, `DataClient().search_aiter`, `OrderClient().create_order`) are asynchronous. Any functions that call such methods must include `async` in their definition. To invoke asynchronous methods from synchronous code, you can wrap the async method calls in `asyncio.run()`. The following is an example of using async with session.
+In V2, all `*Client` methods (for example, `DataClient().search`, `OrderClient().create_order`) are asynchronous. Any functions that call such methods must include `async` in their definition. To invoke asynchronous methods from synchronous code, you can wrap the async method calls in `asyncio.run()`. The following is an example of using async with session.
 
 ```python
 import asyncio
@@ -49,8 +49,7 @@ async def do_search():
         date_filter = filters.date_range_filter('acquired', gte=datetime.fromisoformat("2022-11-18"), lte=datetime.fromisoformat("2022-11-21"))
         cloud_filter = filters.range_filter('cloud_cover', lte=0.1)
         download_filter = filters.permission_filter()
-        search_results = await client.search(["PSScene"], filters.and_filter([date_filter, cloud_filter, download_filter]))
-    return [item async for item in search_results]
+    return [item async for item in client.search(["PSScene"], filters.and_filter([date_filter, cloud_filter, download_filter]))]
  
 items = asyncio.run(do_search())
 ```
@@ -74,8 +73,8 @@ planet.api.ClientV1().quick_search(filters.build_search_request(all_filters, ["P
 Is now
 
 ```python
-async with Session() as session: 
-    items_aiter = planet.DataClient(session).search_aiter(["PSScene"], all_filters)
+async with Session() as session:
+    items = [i async for i in planet.DataClient(session).search(["PSScene"], all_filters)]
 ```
 
 ## Orders API
diff --git a/docs/python/sdk-guide.md b/docs/python/sdk-guide.md
index fb1e7dc..4fbf6a9 100644
--- a/docs/python/sdk-guide.md
+++ b/docs/python/sdk-guide.md
@@ -268,8 +268,7 @@ from planet import collect, OrdersClient, Session
 async def main():
     async with Session() as sess:
         client = OrdersClient(sess)
-        orders_aiter = client.list_orders_aiter()
-        orders_list = collect(orders_aiter)
+        orders_list = collect(client.list_orders())
 
 asyncio.run(main())
 
@@ -278,7 +277,7 @@ asyncio.run(main())
 Alternatively, these results can be converted to a list directly with
 
 ```python
-        orders_list = [o async for o in client.list_orders_aiter()]
+orders_list = [o async for o in client.list_orders()]
 ```
 
 ## Query the data catalog
@@ -341,7 +340,7 @@ the context of a `Session` with the `DataClient`:
 async def main():
     async with Session() as sess:
         cl = DataClient(sess)
-        items = await cl.search(['PSScene'], sfilter)
+        items = [i async for i in cl.search(['PSScene'], sfilter)]
 
 asyncio.run(main())
 ```
diff --git a/planet/cli/data.py b/planet/cli/data.py
index 61688d6..7c33175 100644
--- a/planet/cli/data.py
+++ b/planet/cli/data.py
@@ -272,12 +272,12 @@ async def search(ctx, item_types, filter, limit, name, sort, pretty):
     parameter will be applied to the stored quick search.
     """
     async with data_client(ctx) as cl:
-        items_aiter = cl.search_aiter(item_types,
-                                      filter,
-                                      name=name,
-                                      sort=sort,
-                                      limit=limit)
-        async for item in items_aiter:
+
+        async for item in cl.search(item_types,
+                                    filter,
+                                    name=name,
+                                    sort=sort,
+                                    limit=limit):
             echo_json(item, pretty)
 
 
diff --git a/planet/cli/orders.py b/planet/cli/orders.py
index 998168e..d712f16 100644
--- a/planet/cli/orders.py
+++ b/planet/cli/orders.py
@@ -67,8 +67,7 @@ async def list(ctx, state, limit, pretty):
     optionally pretty-printed.
     '''
     async with orders_client(ctx) as cl:
-        orders_aiter = cl.list_orders_aiter(state=state, limit=limit)
-        async for o in orders_aiter:
+        async for o in cl.list_orders(state=state, limit=limit):
             echo_json(o, pretty)
 
 
diff --git a/planet/cli/subscriptions.py b/planet/cli/subscriptions.py
index 41d2283..23476a7 100644
--- a/planet/cli/subscriptions.py
+++ b/planet/cli/subscriptions.py
@@ -42,9 +42,7 @@ async def list_subscriptions_cmd(ctx, status, limit, pretty):
     """Prints a sequence of JSON-encoded Subscription descriptions."""
     async with CliSession(auth=ctx.obj['AUTH']) as session:
         client = SubscriptionsClient(session)
-        subs_aiter = client.list_subscriptions_aiter(status=status,
-                                                     limit=limit)
-        async for sub in subs_aiter:
+        async for sub in client.list_subscriptions(status=status, limit=limit):
             echo_json(sub, pretty)
 
 
@@ -158,8 +156,7 @@ async def list_subscription_results_cmd(ctx,
     """Gets results of a subscription and prints the API response."""
     async with CliSession(auth=ctx.obj['AUTH']) as session:
         client = SubscriptionsClient(session)
-        results_aiter = client.get_results_aiter(subscription_id,
-                                                 status=status,
-                                                 limit=limit)
-        async for result in results_aiter:
+        async for result in client.get_results(subscription_id,
+                                               status=status,
+                                               limit=limit):
             echo_json(result, pretty)
diff --git a/planet/clients/data.py b/planet/clients/data.py
index 5f8399b..2acd649 100644
--- a/planet/clients/data.py
+++ b/planet/clients/data.py
@@ -93,18 +93,23 @@ class DataClient:
     def _item_url(self, item_type, item_id):
         return f'{self._base_url}/item-types/{item_type}/items/{item_id}'
 
-    async def search_aiter(self,
-                           item_types: List[str],
-                           search_filter: dict,
-                           name: Optional[str] = None,
-                           sort: Optional[str] = None,
-                           limit: int = 100) -> AsyncIterator[dict]:
+    async def search(self,
+                     item_types: List[str],
+                     search_filter: dict,
+                     name: Optional[str] = None,
+                     sort: Optional[str] = None,
+                     limit: int = 100) -> AsyncIterator[dict]:
         """Iterate over results from a quick search.
 
         Quick searches are saved for a short period of time (~month). The
         `name` parameter of the search defaults to the id of the generated
         search id if `name` is not specified.
 
+        Note:
+            The name of this method is based on the API's method name. This
+            method provides iteration over results, it does not get a
+            single result description or return a list of descriptions.
+
         Parameters:
             item_types: The item types to include in the search.
             search_filter: Structured search criteria.
@@ -224,16 +229,16 @@ class DataClient:
                                                json=request)
         return response.json()
 
-    async def list_searches_aiter(self,
-                                  sort: str = 'created desc',
-                                  search_type: str = 'any',
-                                  limit: int = 100) -> AsyncIterator[dict]:
+    async def list_searches(self,
+                            sort: str = 'created desc',
+                            search_type: str = 'any',
+                            limit: int = 100) -> AsyncIterator[dict]:
         """Iterate through list of searches available to the user.
 
-        NOTE: the term 'saved' is overloaded here. We want to list saved
-        searches that are 'quick' or list saved searches that are 'saved'? Do
-        we want to introduce a new term, 'stored' that encompasses 'saved' and
-        'quick' searches?
+        Note:
+            The name of this method is based on the API's method name. This
+            method provides iteration over results, it does not get a
+            single result description or return a list of descriptions.
 
         Parameters:
             sort: Field and direction to order results by.
@@ -295,11 +300,16 @@ class DataClient:
         response = await self._session.request(method='GET', url=url)
         return response.json()
 
-    async def run_search_aiter(self,
-                               search_id: str,
-                               limit: int = 100) -> AsyncIterator[dict]:
+    async def run_search(self,
+                         search_id: str,
+                         limit: int = 100) -> AsyncIterator[dict]:
         """Iterate over results from a saved search.
 
+        Note:
+            The name of this method is based on the API's method name. This
+            method provides iteration over results, it does not get a
+            single result description or return a list of descriptions.
+
         Parameters:
             search_id: Stored search identifier.
             limit: Maximum number of results to return. When set to 0, no
diff --git a/planet/clients/orders.py b/planet/clients/orders.py
index b5c1cc5..d0918c9 100644
--- a/planet/clients/orders.py
+++ b/planet/clients/orders.py
@@ -463,18 +463,23 @@ class OrdersClient:
 
         return current_state
 
-    async def list_orders_aiter(self,
-                                state: Optional[str] = None,
-                                limit: int = 100) -> AsyncIterator[dict]:
+    async def list_orders(self,
+                          state: Optional[str] = None,
+                          limit: int = 100) -> AsyncIterator[dict]:
         """Iterate over the list of stored order requests.
 
+        Note:
+            The name of this method is based on the API's method name. This
+            method provides iteration over results, it does not get a
+            single result description or return a list of descriptions.
+
         Parameters:
             state: Filter orders to given state.
             limit: Maximum number of results to return. When set to 0, no
                 maximum is applied.
 
-        Returns:
-            Iterator over user orders that match the query
+        Yields:
+            Description of an order.
 
         Raises:
             planet.exceptions.APIError: On API error.
diff --git a/planet/clients/subscriptions.py b/planet/clients/subscriptions.py
index 5aba2d4..dd884a1 100644
--- a/planet/clients/subscriptions.py
+++ b/planet/clients/subscriptions.py
@@ -31,10 +31,9 @@ class SubscriptionsClient:
     def __init__(self, session: Session) -> None:
         self._session = session
 
-    async def list_subscriptions_aiter(
-            self,
-            status: Optional[Set[str]] = None,
-            limit: int = 100) -> AsyncIterator[dict]:
+    async def list_subscriptions(self,
+                                 status: Optional[Set[str]] = None,
+                                 limit: int = 100) -> AsyncIterator[dict]:
         """Iterate over list of account subscriptions with optional filtering.
 
         Note:
@@ -193,10 +192,10 @@ class SubscriptionsClient:
             sub = resp.json()
             return sub
 
-    async def get_results_aiter(self,
-                                subscription_id: str,
-                                status: Optional[Set[str]] = None,
-                                limit: int = 100) -> AsyncIterator[dict]:
+    async def get_results(self,
+                          subscription_id: str,
+                          status: Optional[Set[str]] = None,
+                          limit: int = 100) -> AsyncIterator[dict]:
         """Iterate over results of a Subscription.
 
         Note:


Test patch:
diff --git a/tests/integration/test_data_api.py b/tests/integration/test_data_api.py
index aba2bcd..8283d8b 100644
--- a/tests/integration/test_data_api.py
+++ b/tests/integration/test_data_api.py
@@ -67,10 +67,10 @@ def search_response(item_descriptions):
 
 @respx.mock
 @pytest.mark.asyncio
-async def test_search_aiter_basic(item_descriptions,
-                                  search_filter,
-                                  search_response,
-                                  session):
+async def test_search_basic(item_descriptions,
+                            search_filter,
+                            search_response,
+                            session):
 
     quick_search_url = f'{TEST_URL}/quick-search'
     next_page_url = f'{TEST_URL}/blob/?page_marker=IAmATest'
@@ -89,10 +89,10 @@ async def test_search_aiter_basic(item_descriptions,
     respx.get(next_page_url).return_value = mock_resp2
 
     cl = DataClient(session, base_url=TEST_URL)
-    item_aiter = cl.search_aiter(['PSScene'],
-                                 search_filter,
-                                 name='quick_search')
-    items_list = [i async for i in item_aiter]
+    items_list = [
+        i async for i in cl.search(
+            ['PSScene'], search_filter, name='quick_search')
+    ]
 
     # check that request is correct
     expected_request = {
@@ -109,10 +109,10 @@ async def test_search_aiter_basic(item_descriptions,
 
 @respx.mock
 @pytest.mark.asyncio
-async def test_search_aiter_sort(item_descriptions,
-                                 search_filter,
-                                 search_response,
-                                 session):
+async def test_search_sort(item_descriptions,
+                           search_filter,
+                           search_response,
+                           session):
 
     sort = 'acquired asc'
     quick_search_url = f'{TEST_URL}/quick-search?_sort={sort}'
@@ -125,18 +125,17 @@ async def test_search_aiter_sort(item_descriptions,
     # if the sort parameter is not used correctly, the client will not send
     # the request to the mocked endpoint and this test will fail
     cl = DataClient(session, base_url=TEST_URL)
-    item_aiter = cl.search_aiter(['PSScene'], search_filter, sort=sort)
 
     # run through the iterator to actually initiate the call
-    [i async for i in item_aiter]
+    [i async for i in cl.search(['PSScene'], search_filter, sort=sort)]
 
 
 @respx.mock
 @pytest.mark.asyncio
-async def test_search_aiter_limit(item_descriptions,
-                                  search_filter,
-                                  search_response,
-                                  session):
+async def test_search_limit(item_descriptions,
+                            search_filter,
+                            search_response,
+                            session):
 
     quick_search_url = f'{TEST_URL}/quick-search'
 
@@ -148,8 +147,9 @@ async def test_search_aiter_limit(item_descriptions,
     respx.post(quick_search_url).return_value = mock_resp
 
     cl = DataClient(session, base_url=TEST_URL)
-    item_aiter = cl.search_aiter(['PSScene'], search_filter, limit=2)
-    items_list = [i async for i in item_aiter]
+    items_list = [
+        i async for i in cl.search(['PSScene'], search_filter, limit=2)
+    ]
 
     # check only the first two results were returned
     assert items_list == item_descriptions[:2]
@@ -297,19 +297,18 @@ async def test_update_search_basic(search_filter, session):
 @respx.mock
 @pytest.mark.asyncio
 @pytest.mark.parametrize("limit, expected_list_length", [(None, 4), (3, 3)])
-async def test_list_searches_aiter_success(limit,
-                                           expected_list_length,
-                                           search_result,
-                                           session):
+async def test_list_searches_success(limit,
+                                     expected_list_length,
+                                     search_result,
+                                     session):
     page1_response = {"_links": {}, "searches": [search_result] * 4}
     route = respx.get(TEST_SEARCHES_URL)
     route.return_value = httpx.Response(200, json=page1_response)
 
     cl = DataClient(session, base_url=TEST_URL)
 
-    search_aiter = cl.list_searches_aiter(limit=limit)
-    searches_list_length = len([s async for s in search_aiter])
-    assert searches_list_length == expected_list_length
+    assert len([s async for s in cl.list_searches(limit=limit)
+                ]) == expected_list_length
 
     assert route.called
 
@@ -320,19 +319,17 @@ async def test_list_searches_aiter_success(limit,
     "sort, search_type, expectation",
     [('DOESNOTEXIST', 'ANY', pytest.raises(exceptions.ClientError)),
      ('CREATED DESC', 'DOESNOTEXIST', pytest.raises(exceptions.ClientError))])
-async def test_list_searches_aiter_args_do_not_match(sort,
-                                                     search_type,
-                                                     expectation,
-                                                     session):
+async def test_list_searches_args_do_not_match(sort,
+                                               search_type,
+                                               expectation,
+                                               session):
     route = respx.get(TEST_SEARCHES_URL)
     route.return_value = httpx.Response(200, json={})
 
     cl = DataClient(session, base_url=TEST_URL)
 
     with expectation:
-        searches_aiter = cl.list_searches_aiter(sort=sort,
-                                                search_type=search_type)
-        [s async for s in searches_aiter]
+        [s async for s in cl.list_searches(sort=sort, search_type=search_type)]
 
     assert not route.called
 
@@ -357,7 +354,7 @@ async def test_delete_search(retcode, expectation, session):
 
 @respx.mock
 @pytest.mark.asyncio
-async def test_run_search_aiter_success(item_descriptions, session):
+async def test_run_search_success(item_descriptions, session):
     sid = 'search_id'
     route = respx.get(f'{TEST_SEARCHES_URL}/{sid}/results')
 
@@ -376,8 +373,7 @@ async def test_run_search_aiter_success(item_descriptions, session):
     respx.get(next_page_url).return_value = mock_resp2
 
     cl = DataClient(session, base_url=TEST_URL)
-    item_aiter = cl.run_search_aiter(sid)
-    items_list = [i async for i in item_aiter]
+    items_list = [i async for i in cl.run_search(sid)]
 
     assert route.called
 
@@ -387,17 +383,14 @@ async def test_run_search_aiter_success(item_descriptions, session):
 
 @respx.mock
 @pytest.mark.asyncio
-async def test_run_search_aiter_doesnotexist(session):
+async def test_run_search_doesnotexist(session):
     sid = 'search_id'
     route = respx.get(f'{TEST_SEARCHES_URL}/{sid}/results')
     route.return_value = httpx.Response(404)
 
     cl = DataClient(session, base_url=TEST_URL)
     with pytest.raises(exceptions.APIError):
-        item_aiter = cl.run_search_aiter(sid)
-        # this won't throw the error until the iterator is processed
-        # issue 476
-        [i async for i in item_aiter]
+        [i async for i in cl.run_search(sid)]
 
     assert route.called
 
diff --git a/tests/integration/test_orders_api.py b/tests/integration/test_orders_api.py
index 8ef8caf..d11292a 100644
--- a/tests/integration/test_orders_api.py
+++ b/tests/integration/test_orders_api.py
@@ -97,7 +97,7 @@ def test_OrderStates_passed():
 
 @respx.mock
 @pytest.mark.asyncio
-async def test_list_orders_aiter_basic(order_descriptions, session):
+async def test_list_orders_basic(order_descriptions, session):
     next_page_url = TEST_ORDERS_URL + 'blob/?page_marker=IAmATest'
 
     order1, order2, order3 = order_descriptions
@@ -116,13 +116,12 @@ async def test_list_orders_aiter_basic(order_descriptions, session):
     respx.get(next_page_url).return_value = mock_resp2
 
     cl = OrdersClient(session, base_url=TEST_URL)
-    order_aiter = cl.list_orders_aiter()
-    assert order_descriptions == [o async for o in order_aiter]
+    assert order_descriptions == [o async for o in cl.list_orders()]
 
 
 @respx.mock
 @pytest.mark.asyncio
-async def test_list_orders_aiter_state_success(order_descriptions, session):
+async def test_list_orders_state_success(order_descriptions, session):
     list_url = TEST_ORDERS_URL + '?source_type=all&state=failed'
 
     order1, order2, _ = order_descriptions
@@ -139,27 +138,26 @@ async def test_list_orders_aiter_state_success(order_descriptions, session):
 
     # if the value of state doesn't get sent as a url parameter,
     # the mock will fail and this test will fail
-    order_aiter = cl.list_orders_aiter(state='failed')
-    assert [order1, order2] == [o async for o in order_aiter]
+    assert [order1,
+            order2] == [o async for o in cl.list_orders(state='failed')]
 
 
 @pytest.mark.asyncio
-async def test_list_orders_aiter_state_invalid(session):
+async def test_list_orders_state_invalid(session):
     cl = OrdersClient(session, base_url=TEST_URL)
 
     with pytest.raises(exceptions.ClientError):
-        order_aiter = cl.list_orders_aiter(state='invalidstate')
-        [o async for o in order_aiter]
+        [o async for o in cl.list_orders(state='invalidstate')]
 
 
 @respx.mock
 @pytest.mark.asyncio
 @pytest.mark.parametrize("limit,limited_list_length", [(None, 100), (0, 102),
                                                        (1, 1)])
-async def test_list_orders_aiter_limit(order_descriptions,
-                                       session,
-                                       limit,
-                                       limited_list_length):
+async def test_list_orders_limit(order_descriptions,
+                                 session,
+                                 limit,
+                                 limited_list_length):
     nono_page_url = None
 
     # Creating 102 (3x34) order descriptions
@@ -183,8 +181,8 @@ async def test_list_orders_aiter_limit(order_descriptions,
 
     cl = OrdersClient(session, base_url=TEST_URL)
 
-    order_aiter = cl.list_orders_aiter(limit=limit)
-    assert len([o async for o in order_aiter]) == limited_list_length
+    assert len([o async for o in cl.list_orders(limit=limit)
+                ]) == limited_list_length
 
 
 @respx.mock
diff --git a/tests/integration/test_subscriptions_api.py b/tests/integration/test_subscriptions_api.py
index 65d84df..4658660 100644
--- a/tests/integration/test_subscriptions_api.py
+++ b/tests/integration/test_subscriptions_api.py
@@ -156,19 +156,19 @@ res_api_mock.route(
 
 @pytest.mark.asyncio
 @failing_api_mock
-async def test_list_subscriptions_aiter_failure():
+async def test_list_subscriptions_failure():
     """ServerError is raised if there is an internal server error (500)."""
     with pytest.raises(ServerError):
         async with Session() as session:
             client = SubscriptionsClient(session)
-            _ = [sub async for sub in client.list_subscriptions_aiter()]
+            _ = [sub async for sub in client.list_subscriptions()]
 
 
 @pytest.mark.parametrize("status, count", [({"created"}, 100), ({"failed"}, 0),
                                            (None, 100)])
 @pytest.mark.asyncio
 @api_mock
-async def test_list_subscriptions_aiter_success(
+async def test_list_subscriptions_success(
     status,
     count,
 ):
@@ -176,7 +176,7 @@ async def test_list_subscriptions_aiter_success(
     async with Session() as session:
         client = SubscriptionsClient(session)
         assert len([
-            sub async for sub in client.list_subscriptions_aiter(status=status)
+            sub async for sub in client.list_subscriptions(status=status)
         ]) == count
 
 
@@ -266,21 +266,21 @@ async def test_get_subscription_success(monkeypatch):
 
 @pytest.mark.asyncio
 @failing_api_mock
-async def test_get_results_aiter_failure():
+async def test_get_results_failure():
     """APIError is raised if there is a server error."""
     with pytest.raises(APIError):
         async with Session() as session:
             client = SubscriptionsClient(session)
-            _ = [res async for res in client.get_results_aiter("lolwut")]
+            _ = [res async for res in client.get_results("lolwut")]
 
 
 @pytest.mark.asyncio
 @res_api_mock
-async def test_get_results_aiter_success():
+async def test_get_results_success():
     """Subscription description fetched, has the expected items."""
     async with Session() as session:
         client = SubscriptionsClient(session)
-        results = [res async for res in client.get_results_aiter("42")]
+        results = [res async for res in client.get_results("42")]
         assert len(results) == 100
 
 
@@ -313,9 +313,9 @@ paging_cycle_api_mock.route(
 
 @pytest.mark.asyncio
 @paging_cycle_api_mock
-async def test_list_subscriptions_aiter_cycle_break():
+async def test_list_subscriptions_cycle_break():
     """PagingError is raised if there is a paging cycle."""
     with pytest.raises(PagingError):
         async with Session() as session:
             client = SubscriptionsClient(session)
-            _ = [sub async for sub in client.list_subscriptions_aiter()]
+            _ = [sub async for sub in client.list_subscriptions()]