Skip to content

Commit 744f63a

Browse files
authored
Removed all "autoregions" and other guesses in the DB/DBAdmin spawn paths. Also better general exceptions (#337)
* replaced ValueErrors with appropriate exceptions where applicable * custom domain supported in admin's get_database; removed autoregion/autokeyspace in DBspawners * Completed removal of impur HTTP calls in any db/dbadmin spawn path
1 parent c5e627b commit 744f63a

File tree

9 files changed

+156
-296
lines changed

9 files changed

+156
-296
lines changed

CHANGES

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,27 @@
11
main
22
====
3-
Support for Astra DB "custom domain" endpoints for database (then: `.id`, `.region`, `.get_database_admin()`, `.info()` and `.name()` aren't available)
4-
Support for the `indexType` field to describe table indexes (as not mandatory for compatibility)
5-
DataAPITime: support for "hh:mm" no-seconds format
6-
DataAPIDuration: improved parse performance by caching regexpes
7-
DataAPIDuration: support for "P4W"-type strings and for zeroes such as "P", "-PR"
8-
Collection and Table `insert_many` methods employ returnDocumentResponses under the hood
9-
maintenance: switch to DSE6.9 for local non-Astra testing
3+
Spawner methods for databases/admins standardized; they don't issue DevOps API calls.
4+
- removed `normalize_region_for_id` utility method, not used anymore.
5+
- `AstraDBAdmin.get_[async]_database()`:
6+
- does not run DevOps API calls anymore (for missing keyspace/region);
7+
- defers defaults to the [Async]Database class consistently;
8+
- removed `database_admin_timeout_ms`, `request_timeout_ms`, `timeout_ms` parameters;
9+
- `region` now required if `id` passed instead of endpoint.
10+
- `AstraDBDatabaseAdmin.get[_async]_database()`:
11+
- removed `database_admin_timeout_ms`, `request_timeout_ms`, `timeout_ms` parameters.
12+
- `AstraDBDatabaseAdmin.get_database_admin()` standardized and simplified:
13+
- does not run DevOps API calls anymore (for missing keyspace/region);
14+
- removed `database_admin_timeout_ms`, `request_timeout_ms`, `timeout_ms` parameters;
15+
- `region` now required if `id` passed instead of endpoint.
16+
Support for Astra DB "custom domain" endpoints for database
17+
- in which case: `.id`, `.region`, `.get_database_admin()`, `.info()` and `.name()` aren't available.
18+
Support for the `indexType` field to describe table indexes (for compatibility, said field is not mandatory).
19+
DataAPITime: support for "hh:mm" no-seconds format.
20+
DataAPIDuration: improved parse performance by caching regexpes.
21+
DataAPIDuration: support for "P4W"-type strings and for zeroes such as "P", "-PR".
22+
Replaced the ValueErrors not directly coming from function calls/constructors with more appropriate exceptions.
23+
Collection and Table `insert_many` methods employ returnDocumentResponses under the hood.
24+
maintenance: switch to DSE6.9 for local non-Astra testing.
1025

1126
v 2.0.0rc1
1227
==========

astrapy/admin/admin.py

Lines changed: 97 additions & 219 deletions
Large diffs are not rendered by default.

astrapy/data/collection.py

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
from __future__ import annotations
1616

1717
import asyncio
18-
import json
1918
import logging
2019
from concurrent.futures import ThreadPoolExecutor
2120
from types import TracebackType
@@ -476,7 +475,7 @@ def options(
476475
if self_descriptors:
477476
return self_descriptors[0].definition
478477
else:
479-
raise ValueError(
478+
raise RuntimeError(
480479
f"Collection {self.keyspace}.{self.name} not found.",
481480
)
482481

@@ -552,7 +551,7 @@ def keyspace(self) -> str:
552551

553552
_keyspace = self.database.keyspace
554553
if _keyspace is None:
555-
raise ValueError("The collection's DB is set with keyspace=None")
554+
raise RuntimeError("The collection's DB is set with keyspace=None")
556555
return _keyspace
557556

558557
@property
@@ -3009,7 +3008,7 @@ async def options(
30093008
if self_descriptors:
30103009
return self_descriptors[0].definition
30113010
else:
3012-
raise ValueError(
3011+
raise RuntimeError(
30133012
f"Collection {self.keyspace}.{self.name} not found.",
30143013
)
30153014

@@ -3088,7 +3087,7 @@ def keyspace(self) -> str:
30883087

30893088
_keyspace = self.database.keyspace
30903089
if _keyspace is None:
3091-
raise ValueError("The collection's DB is set with keyspace=None")
3090+
raise RuntimeError("The collection's DB is set with keyspace=None")
30923091
return _keyspace
30933092

30943093
@property
@@ -3195,14 +3194,14 @@ async def insert_one(
31953194
inserted_id=inserted_id,
31963195
)
31973196
else:
3198-
raise ValueError(
3199-
"Could not complete a insert_one operation. "
3200-
f"(gotten '${json.dumps(io_response)}')"
3197+
raise UnexpectedDataAPIResponseException(
3198+
text="Faulty response from insert_one API command.",
3199+
raw_response=io_response,
32013200
)
32023201
else:
3203-
raise ValueError(
3204-
"Could not complete a insert_one operation. "
3205-
f"(gotten '${json.dumps(io_response)}')"
3202+
raise UnexpectedDataAPIResponseException(
3203+
text="Faulty response from insert_one API command.",
3204+
raw_response=io_response,
32063205
)
32073206

32083207
async def insert_many(

astrapy/data/cursor.py

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,7 @@ def _fetch_page(
375375
timeout_context: _TimeoutContext,
376376
) -> tuple[list[TRAW], str | None, dict[str, Any] | None]:
377377
if self.collection is None:
378-
raise ValueError("Query engine has no sync collection.")
378+
raise RuntimeError("Query engine has no sync collection.")
379379
f_payload = {
380380
"find": {
381381
**self.f_r_subpayload,
@@ -419,7 +419,7 @@ async def _async_fetch_page(
419419
timeout_context: _TimeoutContext,
420420
) -> tuple[list[TRAW], str | None, dict[str, Any] | None]:
421421
if self.async_collection is None:
422-
raise ValueError("Query engine has no async collection.")
422+
raise RuntimeError("Query engine has no async collection.")
423423
f_payload = {
424424
"find": {
425425
**self.f_r_subpayload,
@@ -512,7 +512,7 @@ def _fetch_page(
512512
timeout_context: _TimeoutContext,
513513
) -> tuple[list[TRAW], str | None, dict[str, Any] | None]:
514514
if self.table is None:
515-
raise ValueError("Query engine has no sync table.")
515+
raise RuntimeError("Query engine has no sync table.")
516516
f_payload = self.table._converter_agent.preprocess_payload(
517517
{
518518
"find": {
@@ -561,7 +561,7 @@ async def _async_fetch_page(
561561
timeout_context: _TimeoutContext,
562562
) -> tuple[list[TRAW], str | None, dict[str, Any] | None]:
563563
if self.async_table is None:
564-
raise ValueError("Query engine has no async table.")
564+
raise RuntimeError("Query engine has no async table.")
565565
f_payload = self.async_table._converter_agent.preprocess_payload(
566566
{
567567
"find": {
@@ -714,7 +714,7 @@ def _copy(
714714
skip: int | None | UnsetType = _UNSET,
715715
) -> CollectionFindCursor[TRAW, T]:
716716
if self._query_engine.collection is None:
717-
raise ValueError("Query engine has no collection.")
717+
raise RuntimeError("Query engine has no collection.")
718718
return CollectionFindCursor(
719719
collection=self._query_engine.collection,
720720
request_timeout_ms=self._request_timeout_ms
@@ -805,7 +805,7 @@ def data_source(self) -> Collection[TRAW]:
805805
"""
806806

807807
if self._query_engine.collection is None:
808-
raise ValueError("Query engine has no collection.")
808+
raise RuntimeError("Query engine has no collection.")
809809
return self._query_engine.collection
810810

811811
def clone(self) -> CollectionFindCursor[TRAW, TRAW]:
@@ -839,7 +839,7 @@ def clone(self) -> CollectionFindCursor[TRAW, TRAW]:
839839
"""
840840

841841
if self._query_engine.collection is None:
842-
raise ValueError("Query engine has no collection.")
842+
raise RuntimeError("Query engine has no collection.")
843843
return CollectionFindCursor(
844844
collection=self._query_engine.collection,
845845
request_timeout_ms=self._request_timeout_ms,
@@ -1060,7 +1060,7 @@ def map(self, mapper: Callable[[T], TNEW]) -> CollectionFindCursor[TRAW, TNEW]:
10601060
"""
10611061
self._ensure_idle()
10621062
if self._query_engine.collection is None:
1063-
raise ValueError("Query engine has no collection.")
1063+
raise RuntimeError("Query engine has no collection.")
10641064
composite_mapper: Callable[[TRAW], TNEW]
10651065
if self._mapper is not None:
10661066

@@ -1392,7 +1392,7 @@ def _copy(
13921392
skip: int | None | UnsetType = _UNSET,
13931393
) -> AsyncCollectionFindCursor[TRAW, T]:
13941394
if self._query_engine.async_collection is None:
1395-
raise ValueError("Query engine has no async collection.")
1395+
raise RuntimeError("Query engine has no async collection.")
13961396
return AsyncCollectionFindCursor(
13971397
collection=self._query_engine.async_collection,
13981398
request_timeout_ms=self._request_timeout_ms
@@ -1488,7 +1488,7 @@ def data_source(self) -> AsyncCollection[TRAW]:
14881488
"""
14891489

14901490
if self._query_engine.async_collection is None:
1491-
raise ValueError("Query engine has no async collection.")
1491+
raise RuntimeError("Query engine has no async collection.")
14921492
return self._query_engine.async_collection
14931493

14941494
def clone(self) -> AsyncCollectionFindCursor[TRAW, TRAW]:
@@ -1508,7 +1508,7 @@ def clone(self) -> AsyncCollectionFindCursor[TRAW, TRAW]:
15081508
"""
15091509

15101510
if self._query_engine.async_collection is None:
1511-
raise ValueError("Query engine has no async collection.")
1511+
raise RuntimeError("Query engine has no async collection.")
15121512
return AsyncCollectionFindCursor(
15131513
collection=self._query_engine.async_collection,
15141514
request_timeout_ms=self._request_timeout_ms,
@@ -1701,7 +1701,7 @@ def map(self, mapper: Callable[[T], TNEW]) -> AsyncCollectionFindCursor[TRAW, TN
17011701

17021702
self._ensure_idle()
17031703
if self._query_engine.async_collection is None:
1704-
raise ValueError("Query engine has no async collection.")
1704+
raise RuntimeError("Query engine has no async collection.")
17051705
composite_mapper: Callable[[TRAW], TNEW]
17061706
if self._mapper is not None:
17071707

@@ -1994,7 +1994,7 @@ def _copy(
19941994
skip: int | None | UnsetType = _UNSET,
19951995
) -> TableFindCursor[TRAW, T]:
19961996
if self._query_engine.table is None:
1997-
raise ValueError("Query engine has no table.")
1997+
raise RuntimeError("Query engine has no table.")
19981998
return TableFindCursor(
19991999
table=self._query_engine.table,
20002000
request_timeout_ms=self._request_timeout_ms
@@ -2085,7 +2085,7 @@ def data_source(self) -> Table[TRAW]:
20852085
"""
20862086

20872087
if self._query_engine.table is None:
2088-
raise ValueError("Query engine has no table.")
2088+
raise RuntimeError("Query engine has no table.")
20892089
return self._query_engine.table
20902090

20912091
def clone(self) -> TableFindCursor[TRAW, TRAW]:
@@ -2119,7 +2119,7 @@ def clone(self) -> TableFindCursor[TRAW, TRAW]:
21192119
"""
21202120

21212121
if self._query_engine.table is None:
2122-
raise ValueError("Query engine has no table.")
2122+
raise RuntimeError("Query engine has no table.")
21232123
return TableFindCursor(
21242124
table=self._query_engine.table,
21252125
request_timeout_ms=self._request_timeout_ms,
@@ -2338,7 +2338,7 @@ def map(self, mapper: Callable[[T], TNEW]) -> TableFindCursor[TRAW, TNEW]:
23382338

23392339
self._ensure_idle()
23402340
if self._query_engine.table is None:
2341-
raise ValueError("Query engine has no table.")
2341+
raise RuntimeError("Query engine has no table.")
23422342
composite_mapper: Callable[[TRAW], TNEW]
23432343
if self._mapper is not None:
23442344

@@ -2670,7 +2670,7 @@ def _copy(
26702670
skip: int | None | UnsetType = _UNSET,
26712671
) -> AsyncTableFindCursor[TRAW, T]:
26722672
if self._query_engine.async_table is None:
2673-
raise ValueError("Query engine has no async table.")
2673+
raise RuntimeError("Query engine has no async table.")
26742674
return AsyncTableFindCursor(
26752675
table=self._query_engine.async_table,
26762676
request_timeout_ms=self._request_timeout_ms
@@ -2765,7 +2765,7 @@ def data_source(self) -> AsyncTable[TRAW]:
27652765
"""
27662766

27672767
if self._query_engine.async_table is None:
2768-
raise ValueError("Query engine has no async table.")
2768+
raise RuntimeError("Query engine has no async table.")
27692769
return self._query_engine.async_table
27702770

27712771
def clone(self) -> AsyncTableFindCursor[TRAW, TRAW]:
@@ -2785,7 +2785,7 @@ def clone(self) -> AsyncTableFindCursor[TRAW, TRAW]:
27852785
"""
27862786

27872787
if self._query_engine.async_table is None:
2788-
raise ValueError("Query engine has no async table.")
2788+
raise RuntimeError("Query engine has no async table.")
27892789
return AsyncTableFindCursor(
27902790
table=self._query_engine.async_table,
27912791
request_timeout_ms=self._request_timeout_ms,
@@ -2978,7 +2978,7 @@ def map(self, mapper: Callable[[T], TNEW]) -> AsyncTableFindCursor[TRAW, TNEW]:
29782978

29792979
self._ensure_idle()
29802980
if self._query_engine.async_table is None:
2981-
raise ValueError("Query engine has no async table.")
2981+
raise RuntimeError("Query engine has no async table.")
29822982
composite_mapper: Callable[[TRAW], TNEW]
29832983
if self._mapper is not None:
29842984

astrapy/data/table.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,7 @@ def definition(
451451
if self_descriptors:
452452
return self_descriptors[0].definition
453453
else:
454-
raise ValueError(
454+
raise RuntimeError(
455455
f"Table {self.keyspace}.{self.name} not found.",
456456
)
457457

@@ -524,7 +524,7 @@ def keyspace(self) -> str:
524524

525525
_keyspace = self.database.keyspace
526526
if _keyspace is None:
527-
raise ValueError("The table's DB is set with keyspace=None")
527+
raise RuntimeError("The table's DB is set with keyspace=None")
528528
return _keyspace
529529

530530
@property
@@ -3162,7 +3162,7 @@ async def definition(
31623162
if self_descriptors:
31633163
return self_descriptors[0].definition
31643164
else:
3165-
raise ValueError(
3165+
raise RuntimeError(
31663166
f"Table {self.keyspace}.{self.name} not found.",
31673167
)
31683168

@@ -3238,7 +3238,7 @@ def keyspace(self) -> str:
32383238

32393239
_keyspace = self.database.keyspace
32403240
if _keyspace is None:
3241-
raise ValueError("The table's DB is set with keyspace=None")
3241+
raise RuntimeError("The table's DB is set with keyspace=None")
32423242
return _keyspace
32433243

32443244
@property

astrapy/utils/date_utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ def _unix_timestamp_ms_to_timetuple(
129129
milliseconds=y_remainder_ms
130130
)
131131
if ref_dt.year != ref_year:
132-
raise ValueError(f"Could not map timestamp ({timestamp_ms} ms) to a date.")
132+
raise RuntimeError(f"Could not map timestamp ({timestamp_ms} ms) to a date.")
133133
month = ref_dt.month
134134
day = ref_dt.day
135135
hour = ref_dt.hour

tests/base/integration/collections/test_collection_ddl_sync.py

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import pytest
2020

2121
from astrapy import DataAPIClient, Database
22-
from astrapy.admin import parse_api_endpoint
2322
from astrapy.constants import DefaultIdType, VectorMetric
2423
from astrapy.ids import UUID, ObjectId
2524
from astrapy.info import (
@@ -422,37 +421,6 @@ def test_tokenless_client_sync(
422421
)
423422
assert isinstance(database.list_collection_names(), list)
424423

425-
@pytest.mark.skipif(not IS_ASTRA_DB, reason="Not supported outside of Astra DB")
426-
@pytest.mark.describe(
427-
"test of autoregion through DevOps API for get_database(_admin), sync"
428-
)
429-
def test_autoregion_getdatabase_sync(
430-
self,
431-
data_api_credentials_kwargs: DataAPICredentials,
432-
data_api_credentials_info: DataAPICredentialsInfo,
433-
) -> None:
434-
client = DataAPIClient(environment=data_api_credentials_info["environment"])
435-
parsed_api_endpoint = parse_api_endpoint(
436-
data_api_credentials_kwargs["api_endpoint"]
437-
)
438-
if parsed_api_endpoint is None:
439-
raise ValueError(
440-
f"Unparseable API endpoint: {data_api_credentials_kwargs['api_endpoint']}"
441-
)
442-
adm = client.get_admin(token=data_api_credentials_kwargs["token"])
443-
# auto-region through the DebvOps "db info" call
444-
assert adm.get_database_admin(
445-
parsed_api_endpoint.database_id
446-
) == adm.get_database_admin(data_api_credentials_kwargs["api_endpoint"])
447-
448-
# auto-region for get_database
449-
assert adm.get_database(
450-
parsed_api_endpoint.database_id,
451-
keyspace="the_ks",
452-
) == adm.get_database(
453-
data_api_credentials_kwargs["api_endpoint"], keyspace="the_ks"
454-
)
455-
456424
@pytest.mark.skipif(not IS_ASTRA_DB, reason="Not supported outside of Astra DB")
457425
@pytest.mark.describe(
458426
"test database-from-admin default keyspace per environment, sync"

tests/base/integration/collections/test_collection_exceptions_async.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ async def test_collection_options_failures_async(
144144
) -> None:
145145
acol = async_empty_collection._copy()
146146
acol._name += "_hacked"
147-
with pytest.raises(ValueError, match="not found"):
147+
with pytest.raises(RuntimeError, match="not found"):
148148
await acol.options()
149149

150150
@pytest.mark.describe("test of collection count_documents failure modes, async")

tests/base/integration/collections/test_collection_exceptions_sync.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ def test_collection_options_failures_sync(
134134
) -> None:
135135
col = sync_empty_collection._copy()
136136
col._name += "_hacked"
137-
with pytest.raises(ValueError, match="not found"):
137+
with pytest.raises(RuntimeError, match="not found"):
138138
col.options()
139139

140140
@pytest.mark.describe("test of collection count_documents failure modes, sync")

0 commit comments

Comments
 (0)