From a8349a5cdd295bdb04b965e3e91c3a8959e044e9 Mon Sep 17 00:00:00 2001 From: nguyenv Date: Thu, 12 Dec 2024 07:30:40 -0600 Subject: [PATCH] [python, c++] Fix misleading `DoesNotExistErrors` in `open` factory (#3402) --- apis/python/src/tiledbsoma/_collection.py | 23 +----- apis/python/src/tiledbsoma/_exception.py | 10 ++- apis/python/src/tiledbsoma/_soma_array.py | 27 +------ apis/python/src/tiledbsoma/_soma_object.py | 6 +- apis/python/src/tiledbsoma/_tdb_handles.py | 75 ++++++++++-------- apis/python/src/tiledbsoma/soma_collection.cc | 44 ++++++++++- apis/python/src/tiledbsoma/soma_object.cc | 77 ++++++++----------- apis/python/tests/test_collection.py | 25 ++++++ apis/python/tests/test_dataframe.py | 25 ++++++ apis/python/tests/test_dense_nd_array.py | 25 ++++++ apis/python/tests/test_experiment_basic.py | 48 ++++++++++++ apis/python/tests/test_multiscale_image.py | 29 ++++++- apis/python/tests/test_platform_config.py | 2 +- .../tests/test_point_cloud_dataframe.py | 25 ++++++ apis/python/tests/test_scene.py | 25 ++++++ apis/python/tests/test_sparse_nd_array.py | 27 ++++++- apis/r/src/rinterface.cpp | 2 +- libtiledbsoma/src/soma/soma_array.cc | 30 +++++--- libtiledbsoma/src/soma/soma_array.h | 4 +- libtiledbsoma/src/soma/soma_collection.cc | 10 ++- libtiledbsoma/src/soma/soma_dataframe.cc | 19 ++++- libtiledbsoma/src/soma/soma_dense_ndarray.cc | 9 ++- libtiledbsoma/src/soma/soma_experiment.cc | 10 ++- libtiledbsoma/src/soma/soma_measurement.cc | 10 ++- .../src/soma/soma_multiscale_image.cc | 11 ++- libtiledbsoma/src/soma/soma_object.cc | 39 ++++++++-- libtiledbsoma/src/soma/soma_object.h | 2 + .../src/soma/soma_point_cloud_dataframe.cc | 10 ++- libtiledbsoma/src/soma/soma_scene.cc | 9 ++- libtiledbsoma/src/soma/soma_sparse_ndarray.cc | 9 ++- libtiledbsoma/test/unit_soma_dataframe.cc | 15 ++-- libtiledbsoma/test/unit_soma_dense_ndarray.cc | 17 ++-- .../test/unit_soma_sparse_ndarray.cc | 17 ++-- 33 files changed, 526 insertions(+), 190 deletions(-) diff --git a/apis/python/src/tiledbsoma/_collection.py b/apis/python/src/tiledbsoma/_collection.py index 1e486ab002..c7eac3e6f3 100644 --- a/apis/python/src/tiledbsoma/_collection.py +++ b/apis/python/src/tiledbsoma/_collection.py @@ -110,7 +110,7 @@ def create( timestamp_ms = context._open_timestamp_ms(tiledb_timestamp) clib.SOMAGroup.create( uri=uri, - soma_type=wrapper._GROUP_WRAPPED_TYPE.__name__, + soma_type=wrapper._WRAPPED_TYPE.__name__, ctx=context.native_context, timestamp=(0, timestamp_ms), ) @@ -122,27 +122,6 @@ def create( except SOMAError as e: raise map_exception_for_create(e, uri) from None - @classmethod - def open( - cls, - uri: str, - mode: options.OpenMode = "r", - *, - tiledb_timestamp: Optional[OpenTimestamp] = None, - context: Optional[SOMATileDBContext] = None, - platform_config: Optional[options.PlatformConfig] = None, - clib_type: Optional[str] = None, - ) -> Self: - """Opens this specific type of SOMA object.""" - return super().open( - uri, - mode, - tiledb_timestamp=tiledb_timestamp, - context=context, - platform_config=platform_config, - clib_type="SOMAGroup", - ) - # Subclass protocol to constrain which SOMA objects types may be set on a # particular collection key. Used by Experiment and Measurement. _subclass_constrained_soma_types: ClassVar[Dict[str, Tuple[str, ...]]] = {} diff --git a/apis/python/src/tiledbsoma/_exception.py b/apis/python/src/tiledbsoma/_exception.py index 82b4a14016..b2b6591f7e 100644 --- a/apis/python/src/tiledbsoma/_exception.py +++ b/apis/python/src/tiledbsoma/_exception.py @@ -6,6 +6,8 @@ """Exceptions. """ +from typing import Union + class SOMAError(Exception): """Base error type for SOMA-specific exceptions. @@ -25,8 +27,9 @@ class DoesNotExistError(SOMAError): pass -def is_does_not_exist_error(e: RuntimeError) -> bool: - """Given a RuntimeError, return true if it indicates the object does not exist +def is_does_not_exist_error(e: Union[RuntimeError, SOMAError]) -> bool: + """Given a RuntimeError or SOMAError, return true if it indicates the object + does not exist Lifecycle: Maturing. @@ -34,7 +37,7 @@ def is_does_not_exist_error(e: RuntimeError) -> bool: try: with tiledbsoma.open(uri): ... - except RuntimeError as e: + except (RuntimeError, SOMAError) as e: if is_does_not_exist_error(e): ... raise e @@ -47,6 +50,7 @@ def is_does_not_exist_error(e: RuntimeError) -> bool: or "Unrecognized array" in stre or "HTTP code 401" in stre or "HTTP code 404" in stre + or "[SOMAObject::open] " in stre ): return True diff --git a/apis/python/src/tiledbsoma/_soma_array.py b/apis/python/src/tiledbsoma/_soma_array.py index 674ba7004a..1e0ed63cc9 100644 --- a/apis/python/src/tiledbsoma/_soma_array.py +++ b/apis/python/src/tiledbsoma/_soma_array.py @@ -3,19 +3,15 @@ # # Licensed under the MIT License. -from typing import Any, Optional, Tuple +from typing import Any, Tuple import pyarrow as pa -from somacore import options -from typing_extensions import Self from . import _tdb_handles # This package's pybind11 code from . import pytiledbsoma as clib # noqa: E402 from ._soma_object import SOMAObject -from ._types import OpenTimestamp -from .options._soma_tiledb_context import SOMATileDBContext class SOMAArray(SOMAObject[_tdb_handles.SOMAArrayWrapper[Any]]): @@ -27,27 +23,6 @@ class SOMAArray(SOMAObject[_tdb_handles.SOMAArrayWrapper[Any]]): __slots__ = () - @classmethod - def open( - cls, - uri: str, - mode: options.OpenMode = "r", - *, - tiledb_timestamp: Optional[OpenTimestamp] = None, - context: Optional[SOMATileDBContext] = None, - platform_config: Optional[options.PlatformConfig] = None, - clib_type: Optional[str] = None, - ) -> Self: - """Opens this specific type of SOMA object.""" - return super().open( - uri, - mode, - tiledb_timestamp=tiledb_timestamp, - context=context, - platform_config=platform_config, - clib_type="SOMAArray", - ) - @property def schema(self) -> pa.Schema: """Returns data schema, in the form of an diff --git a/apis/python/src/tiledbsoma/_soma_object.py b/apis/python/src/tiledbsoma/_soma_object.py index 71fae027ee..3c2776036f 100644 --- a/apis/python/src/tiledbsoma/_soma_object.py +++ b/apis/python/src/tiledbsoma/_soma_object.py @@ -97,7 +97,11 @@ def open( del platform_config # unused context = _validate_soma_tiledb_context(context) handle = _tdb_handles.open( - uri, mode, context, tiledb_timestamp, clib_type=clib_type + uri, + mode, + context, + tiledb_timestamp, + clib_type=cls._wrapper_type._WRAPPED_TYPE.__name__, ) if not isinstance(handle, cls._wrapper_type): handle = cls._wrapper_type.open(uri, mode, context, tiledb_timestamp) diff --git a/apis/python/src/tiledbsoma/_tdb_handles.py b/apis/python/src/tiledbsoma/_tdb_handles.py index 012a8a6a8c..f7b00bf654 100644 --- a/apis/python/src/tiledbsoma/_tdb_handles.py +++ b/apis/python/src/tiledbsoma/_tdb_handles.py @@ -57,6 +57,8 @@ _RawHdl_co = TypeVar("_RawHdl_co", bound=RawHandle, covariant=True) """A raw TileDB object. Covariant because Handles are immutable enough.""" +_SOMAObjectType = TypeVar("_SOMAObjectType", bound=clib.SOMAObject) + def open( uri: str, @@ -70,16 +72,35 @@ def open( timestamp_ms = context._open_timestamp_ms(timestamp) - soma_object = clib.SOMAObject.open( - uri=uri, - mode=open_mode, - context=context.native_context, - timestamp=(0, timestamp_ms), - clib_type=clib_type, - ) + _type_to_open = { + "somaarray": clib.SOMAArray.open, + "somagroup": clib.SOMAGroup.open, + "somadataframe": clib.SOMADataFrame.open, + "somapointclouddataframe": clib.SOMAPointCloudDataFrame.open, + "somadensendarray": clib.SOMADenseNDArray.open, + "somasparsendarray": clib.SOMASparseNDArray.open, + "somacollection": clib.SOMACollection.open, + "somaexperiment": clib.SOMAExperiment.open, + "somameasurement": clib.SOMAMeasurement.open, + "somascene": clib.SOMAScene.open, + "somamultiscaleimage": clib.SOMAMultiscaleImage.open, + None: clib.SOMAObject.open, + } - if not soma_object: - raise DoesNotExistError(f"{uri!r} does not exist") + if clib_type is not None: + clib_type = clib_type.lower() + + try: + soma_object = _type_to_open[clib_type]( + uri=uri, + mode=open_mode, + context=context.native_context, + timestamp=(0, timestamp_ms), + ) + except (RuntimeError, SOMAError) as tdbe: + if is_does_not_exist_error(tdbe): + raise DoesNotExistError(tdbe) from tdbe + raise SOMAError(tdbe) from tdbe _type_to_class = { "somadataframe": DataFrameWrapper, @@ -265,13 +286,10 @@ def from_soma_group_entry(cls, obj: Tuple[str, str]) -> "GroupEntry": raise SOMAError(f"internal error: unknown object type {uri}") -_GrpType = TypeVar("_GrpType", bound=clib.SOMAGroup) - - -class SOMAGroupWrapper(Wrapper[_GrpType]): +class SOMAGroupWrapper(Wrapper[_SOMAObjectType]): """Base class for Pybind11 SOMAGroupWrapper handles.""" - _GROUP_WRAPPED_TYPE: Type[_GrpType] + _WRAPPED_TYPE: Type[_SOMAObjectType] clib_type = "SOMAGroup" @@ -284,7 +302,7 @@ def _opener( timestamp: int, ) -> clib.SOMAGroup: open_mode = clib.OpenMode.read if mode == "r" else clib.OpenMode.write - return cls._GROUP_WRAPPED_TYPE.open( + return cls._WRAPPED_TYPE.open( uri, mode=open_mode, context=context.native_context, @@ -310,40 +328,37 @@ def members(self) -> Dict[str, Tuple[str, str]]: class CollectionWrapper(SOMAGroupWrapper[clib.SOMACollection]): """Wrapper around a Pybind11 CollectionWrapper handle.""" - _GROUP_WRAPPED_TYPE = clib.SOMACollection + _WRAPPED_TYPE = clib.SOMACollection class ExperimentWrapper(SOMAGroupWrapper[clib.SOMAExperiment]): """Wrapper around a Pybind11 ExperimentWrapper handle.""" - _GROUP_WRAPPED_TYPE = clib.SOMAExperiment + _WRAPPED_TYPE = clib.SOMAExperiment class MeasurementWrapper(SOMAGroupWrapper[clib.SOMAMeasurement]): """Wrapper around a Pybind11 MeasurementWrapper handle.""" - _GROUP_WRAPPED_TYPE = clib.SOMAMeasurement + _WRAPPED_TYPE = clib.SOMAMeasurement class MultiscaleImageWrapper(SOMAGroupWrapper[clib.SOMAMultiscaleImage]): """Wrapper around a Pybind11 MultiscaleImage handle.""" - _GROUP_WRAPPED_TYPE = clib.SOMAMultiscaleImage + _WRAPPED_TYPE = clib.SOMAMultiscaleImage class SceneWrapper(SOMAGroupWrapper[clib.SOMAScene]): """Wrapper around a Pybind11 SceneWrapper handle.""" - _GROUP_WRAPPED_TYPE = clib.SOMAScene - - -_ArrType = TypeVar("_ArrType", bound=clib.SOMAArray) + _WRAPPED_TYPE = clib.SOMAScene -class SOMAArrayWrapper(Wrapper[_ArrType]): +class SOMAArrayWrapper(Wrapper[_SOMAObjectType]): """Base class for Pybind11 SOMAArrayWrapper handles.""" - _ARRAY_WRAPPED_TYPE: Type[_ArrType] + _WRAPPED_TYPE: Type[_SOMAObjectType] clib_type = "SOMAArray" @@ -357,7 +372,7 @@ def _opener( ) -> clib.SOMAArray: open_mode = clib.OpenMode.read if mode == "r" else clib.OpenMode.write - return cls._ARRAY_WRAPPED_TYPE.open( + return cls._WRAPPED_TYPE.open( uri, mode=open_mode, context=context.native_context, @@ -517,7 +532,7 @@ def can_change_domain( class DataFrameWrapper(SOMAArrayWrapper[clib.SOMADataFrame]): """Wrapper around a Pybind11 SOMADataFrame handle.""" - _ARRAY_WRAPPED_TYPE = clib.SOMADataFrame + _WRAPPED_TYPE = clib.SOMADataFrame @property def count(self) -> int: @@ -607,7 +622,7 @@ def can_change_domain( class PointCloudDataFrameWrapper(SOMAArrayWrapper[clib.SOMAPointCloudDataFrame]): """Wrapper around a Pybind11 SOMAPointCloudDataFrame handle.""" - _ARRAY_WRAPPED_TYPE = clib.SOMAPointCloudDataFrame + _WRAPPED_TYPE = clib.SOMAPointCloudDataFrame @property def count(self) -> int: @@ -620,7 +635,7 @@ def write(self, values: pa.RecordBatch) -> None: class DenseNDArrayWrapper(SOMAArrayWrapper[clib.SOMADenseNDArray]): """Wrapper around a Pybind11 DenseNDArrayWrapper handle.""" - _ARRAY_WRAPPED_TYPE = clib.SOMADenseNDArray + _WRAPPED_TYPE = clib.SOMADenseNDArray @property def tiledbsoma_has_upgraded_shape(self) -> bool: @@ -653,7 +668,7 @@ def tiledbsoma_can_upgrade_shape( class SparseNDArrayWrapper(SOMAArrayWrapper[clib.SOMASparseNDArray]): """Wrapper around a Pybind11 SparseNDArrayWrapper handle.""" - _ARRAY_WRAPPED_TYPE = clib.SOMASparseNDArray + _WRAPPED_TYPE = clib.SOMASparseNDArray @property def nnz(self) -> int: diff --git a/apis/python/src/tiledbsoma/soma_collection.cc b/apis/python/src/tiledbsoma/soma_collection.cc index 5836ca54bb..1078a23f70 100644 --- a/apis/python/src/tiledbsoma/soma_collection.cc +++ b/apis/python/src/tiledbsoma/soma_collection.cc @@ -71,10 +71,28 @@ void load_soma_collection(py::module& m) { .def("get", &SOMACollection::get); py::class_( - m, "SOMAExperiment"); + m, "SOMAExperiment") + .def_static( + "open", + &SOMAExperiment::open, + "uri"_a, + py::kw_only(), + "mode"_a, + "context"_a, + "timestamp"_a = py::none(), + py::call_guard()); py::class_( - m, "SOMAMeasurement"); + m, "SOMAMeasurement") + .def_static( + "open", + &SOMAMeasurement::open, + "uri"_a, + py::kw_only(), + "mode"_a, + "context"_a, + "timestamp"_a = py::none(), + py::call_guard()); py::class_(m, "SOMAScene") .def_static( @@ -91,9 +109,27 @@ void load_soma_collection(py::module& m) { py::kw_only(), "ctx"_a, "uri"_a, - "timestamp"_a = py::none()); + "timestamp"_a = py::none()) + .def_static( + "open", + &SOMAScene::open, + "uri"_a, + py::kw_only(), + "mode"_a, + "context"_a, + "timestamp"_a = py::none(), + py::call_guard()); py::class_( - m, "SOMAMultiscaleImage"); + m, "SOMAMultiscaleImage") + .def_static( + "open", + &SOMAMultiscaleImage::open, + "uri"_a, + py::kw_only(), + "mode"_a, + "context"_a, + "timestamp"_a = py::none(), + py::call_guard()); } } // namespace libtiledbsomacpp diff --git a/apis/python/src/tiledbsoma/soma_object.cc b/apis/python/src/tiledbsoma/soma_object.cc index 8e5750dc3a..b0bfef38c9 100644 --- a/apis/python/src/tiledbsoma/soma_object.cc +++ b/apis/python/src/tiledbsoma/soma_object.cc @@ -57,52 +57,41 @@ void load_soma_object(py::module& m) { std::shared_ptr context, std::optional> timestamp, std::optional clib_type) -> py::object { - try { - auto soma_obj = ([&]() { - py::gil_scoped_release release; - return SOMAObject::open( - uri, mode, context, timestamp, clib_type); - })(); - auto soma_obj_type = soma_obj->type(); + auto soma_obj = ([&]() { + py::gil_scoped_release release; + return SOMAObject::open( + uri, mode, context, timestamp, clib_type); + })(); - if (soma_obj_type.has_value()) { - std::transform( - soma_obj_type->begin(), - soma_obj_type->end(), - soma_obj_type->begin(), - [](unsigned char c) { return std::tolower(c); }); - } + auto soma_obj_type = soma_obj->type(); - if (soma_obj_type == "somadataframe") - return py::cast( - dynamic_cast(*soma_obj)); - if (soma_obj_type == "somapointclouddataframe") - return py::cast( - dynamic_cast(*soma_obj)); - else if (soma_obj_type == "somasparsendarray") - return py::cast( - dynamic_cast(*soma_obj)); - else if (soma_obj_type == "somadensendarray") - return py::cast( - dynamic_cast(*soma_obj)); - else if (soma_obj_type == "somacollection") - return py::cast( - dynamic_cast(*soma_obj)); - else if (soma_obj_type == "somaexperiment") - return py::cast( - dynamic_cast(*soma_obj)); - else if (soma_obj_type == "somameasurement") - return py::cast( - dynamic_cast(*soma_obj)); - else if (soma_obj_type == "somascene") - return py::cast(dynamic_cast(*soma_obj)); - else if (soma_obj_type == "somamultiscaleimage") - return py::cast( - dynamic_cast(*soma_obj)); - return py::none(); - } catch (...) { - return py::none(); - } + std::transform( + soma_obj_type->begin(), + soma_obj_type->end(), + soma_obj_type->begin(), + [](unsigned char c) { return std::tolower(c); }); + + if (soma_obj_type == "somadataframe") + return py::cast(dynamic_cast(*soma_obj)); + if (soma_obj_type == "somapointclouddataframe") + return py::cast( + dynamic_cast(*soma_obj)); + else if (soma_obj_type == "somasparsendarray") + return py::cast( + dynamic_cast(*soma_obj)); + else if (soma_obj_type == "somadensendarray") + return py::cast(dynamic_cast(*soma_obj)); + else if (soma_obj_type == "somacollection") + return py::cast(dynamic_cast(*soma_obj)); + else if (soma_obj_type == "somaexperiment") + return py::cast(dynamic_cast(*soma_obj)); + else if (soma_obj_type == "somameasurement") + return py::cast(dynamic_cast(*soma_obj)); + else if (soma_obj_type == "somascene") + return py::cast(dynamic_cast(*soma_obj)); + else if (soma_obj_type == "somamultiscaleimage") + return py::cast( + dynamic_cast(*soma_obj)); }, "uri"_a, "mode"_a, diff --git a/apis/python/tests/test_collection.py b/apis/python/tests/test_collection.py index adadddbcb7..3f0b182765 100644 --- a/apis/python/tests/test_collection.py +++ b/apis/python/tests/test_collection.py @@ -87,6 +87,31 @@ def test_collection_basic(tmp_path): with readback_collection["snda"] as snda: assert len(snda.read().tables().concat()) == 3 + # Ensure it cannot be opened by another type + with pytest.raises(soma.SOMAError): + soma.DataFrame.open(collection.uri) + + with pytest.raises(soma.SOMAError): + soma.SparseNDArray.open(collection.uri) + + with pytest.raises(soma.SOMAError): + soma.DenseNDArray.open(collection.uri) + + with pytest.raises(soma.SOMAError): + soma.PointCloudDataFrame.open(collection.uri) + + with pytest.raises(soma.SOMAError): + soma.Experiment.open(collection.uri) + + with pytest.raises(soma.SOMAError): + soma.Measurement.open(collection.uri) + + with pytest.raises(soma.SOMAError): + soma.Scene.open(collection.uri) + + with pytest.raises(soma.SOMAError): + soma.MultiscaleImage.open(collection.uri) + @pytest.fixture( scope="function", diff --git a/apis/python/tests/test_dataframe.py b/apis/python/tests/test_dataframe.py index c607cfcf7f..c7b956a583 100644 --- a/apis/python/tests/test_dataframe.py +++ b/apis/python/tests/test_dataframe.py @@ -160,6 +160,31 @@ def test_dataframe(tmp_path, arrow_schema): with soma.DataFrame.open(tmp_path.as_posix(), "w") as A: assert isinstance(A._handle._handle, soma.pytiledbsoma.SOMADataFrame) + # Ensure it cannot be opened by another type + with pytest.raises(soma.SOMAError): + soma.SparseNDArray.open(tmp_path.as_posix()) + + with pytest.raises(soma.SOMAError): + soma.DenseNDArray.open(tmp_path.as_posix()) + + with pytest.raises(soma.SOMAError): + soma.PointCloudDataFrame.open(tmp_path.as_posix()) + + with pytest.raises(soma.SOMAError): + soma.Collection.open(tmp_path.as_posix()) + + with pytest.raises(soma.SOMAError): + soma.Experiment.open(tmp_path.as_posix()) + + with pytest.raises(soma.SOMAError): + soma.Measurement.open(tmp_path.as_posix()) + + with pytest.raises(soma.SOMAError): + soma.Scene.open(tmp_path.as_posix()) + + with pytest.raises(soma.SOMAError): + soma.MultiscaleImage.open(tmp_path.as_posix()) + def test_dataframe_reopen(tmp_path, arrow_schema): soma.DataFrame.create( diff --git a/apis/python/tests/test_dense_nd_array.py b/apis/python/tests/test_dense_nd_array.py index 693b96da4a..dbe7bf846c 100644 --- a/apis/python/tests/test_dense_nd_array.py +++ b/apis/python/tests/test_dense_nd_array.py @@ -57,6 +57,31 @@ def test_dense_nd_array_create_ok( with soma.DenseNDArray.open(tmp_path.as_posix(), "w") as A: assert isinstance(A._handle._handle, soma.pytiledbsoma.SOMADenseNDArray) + # Ensure it cannot be opened by another type + with pytest.raises(soma.SOMAError): + soma.DataFrame.open(tmp_path.as_posix()) + + with pytest.raises(soma.SOMAError): + soma.SparseNDArray.open(tmp_path.as_posix()) + + with pytest.raises(soma.SOMAError): + soma.PointCloudDataFrame.open(tmp_path.as_posix()) + + with pytest.raises(soma.SOMAError): + soma.Collection.open(tmp_path.as_posix()) + + with pytest.raises(soma.SOMAError): + soma.Experiment.open(tmp_path.as_posix()) + + with pytest.raises(soma.SOMAError): + soma.Measurement.open(tmp_path.as_posix()) + + with pytest.raises(soma.SOMAError): + soma.Scene.open(tmp_path.as_posix()) + + with pytest.raises(soma.SOMAError): + soma.MultiscaleImage.open(tmp_path.as_posix()) + def test_dense_nd_array_reopen(tmp_path): soma.DenseNDArray.create( diff --git a/apis/python/tests/test_experiment_basic.py b/apis/python/tests/test_experiment_basic.py index 9c4886373f..685f67cdc8 100644 --- a/apis/python/tests/test_experiment_basic.py +++ b/apis/python/tests/test_experiment_basic.py @@ -162,6 +162,54 @@ def test_experiment_basic(tmp_path): with pytest.raises(soma.DoesNotExistError): soma.DataFrame.open("/nonesuch/no/nope/nope/never") + # Ensure it cannot be opened by another type + with pytest.raises(soma.SOMAError): + soma.DataFrame.open(experiment.uri) + + with pytest.raises(soma.SOMAError): + soma.SparseNDArray.open(experiment.uri) + + with pytest.raises(soma.SOMAError): + soma.DenseNDArray.open(experiment.uri) + + with pytest.raises(soma.SOMAError): + soma.PointCloudDataFrame.open(experiment.uri) + + with pytest.raises(soma.SOMAError): + soma.Collection.open(experiment.uri) + + with pytest.raises(soma.SOMAError): + soma.Measurement.open(experiment.uri) + + with pytest.raises(soma.SOMAError): + soma.Scene.open(experiment.uri) + + with pytest.raises(soma.SOMAError): + soma.MultiscaleImage.open(experiment.uri) + + with pytest.raises(soma.SOMAError): + soma.DataFrame.open(measurement.uri) + + with pytest.raises(soma.SOMAError): + soma.SparseNDArray.open(measurement.uri) + + with pytest.raises(soma.SOMAError): + soma.DenseNDArray.open(measurement.uri) + + with pytest.raises(soma.SOMAError): + soma.PointCloudDataFrame.open(measurement.uri) + + with pytest.raises(soma.SOMAError): + soma.Collection.open(measurement.uri) + + with pytest.raises(soma.SOMAError): + soma.Experiment.open(measurement.uri) + + with pytest.raises(soma.SOMAError): + soma.Scene.open(measurement.uri) + + with pytest.raises(soma.SOMAError): + soma.MultiscaleImage.open(measurement.uri) # ---------------------------------------------------------------- # TODO: check more things diff --git a/apis/python/tests/test_multiscale_image.py b/apis/python/tests/test_multiscale_image.py index 9cb4a9be69..dff9c19f79 100644 --- a/apis/python/tests/test_multiscale_image.py +++ b/apis/python/tests/test_multiscale_image.py @@ -159,6 +159,31 @@ def test_multiscale_basic(tmp_path): assert np.array_equal(from_level(1).scale_factors, [2, 2]) assert np.array_equal(from_level(2).scale_factors, [16, 16]) + # Ensure it cannot be opened by another type + with pytest.raises(soma.SOMAError): + soma.DataFrame.open(image_uri) + + with pytest.raises(soma.SOMAError): + soma.SparseNDArray.open(image_uri) + + with pytest.raises(soma.SOMAError): + soma.DenseNDArray.open(image_uri) + + with pytest.raises(soma.SOMAError): + soma.PointCloudDataFrame.open(image_uri) + + with pytest.raises(soma.SOMAError): + soma.Collection.open(image_uri) + + with pytest.raises(soma.SOMAError): + soma.Experiment.open(image_uri) + + with pytest.raises(soma.SOMAError): + soma.Measurement.open(image_uri) + + with pytest.raises(soma.SOMAError): + soma.Scene.open(image_uri) + class TestSimpleMultiscale2D: @@ -461,7 +486,7 @@ def test_multiscale_2d_read_region_with_channel( baseuri = urljoin(f"{tmp_path.as_uri()}/", "test_multiscale_read_region") image_uri = create_multiscale(baseuri, ("x", "y"), data_axis_order, True, shapes) - with soma.Collection.open(image_uri, mode="w") as image: + with soma.MultiscaleImage.open(image_uri, mode="w") as image: for i, shape in enumerate(shapes): data = np.arange(shape[0] * shape[1] * shape[2], dtype=np.uint8).reshape( shape @@ -526,7 +551,7 @@ def test_multiscale_2d_read_region_no_channel(tmp_path, shapes, region, scale_fa baseuri = urljoin(f"{tmp_path.as_uri()}/", "test_multiscale_read_region") image_uri = create_multiscale(baseuri, ("x", "y"), ("y", "x"), False, shapes) - with soma.Collection.open(image_uri, mode="w") as image: + with soma.MultiscaleImage.open(image_uri, mode="w") as image: for i, shape in enumerate(shapes): data = np.arange(shape[0] * shape[1], dtype=np.uint8).reshape(shape) image[f"level{i}"].write( diff --git a/apis/python/tests/test_platform_config.py b/apis/python/tests/test_platform_config.py index b36151863f..4653d6f32f 100644 --- a/apis/python/tests/test_platform_config.py +++ b/apis/python/tests/test_platform_config.py @@ -69,7 +69,7 @@ def test_platform_config(conftest_pbmc_small): ] var_arr_uri = str(Path(output_path) / "ms" / "RNA" / "var") - with tiledbsoma.SparseNDArray.open(var_arr_uri) as var_arr: + with tiledbsoma.DataFrame.open(var_arr_uri) as var_arr: cfg = var_arr.config_options_from_schema() assert json.loads(cfg.dims)["soma_joinid"]["filters"] == [ {"COMPRESSION_LEVEL": 1, "name": "ZSTD"} diff --git a/apis/python/tests/test_point_cloud_dataframe.py b/apis/python/tests/test_point_cloud_dataframe.py index 9d8f9d21b1..a5611781e4 100644 --- a/apis/python/tests/test_point_cloud_dataframe.py +++ b/apis/python/tests/test_point_cloud_dataframe.py @@ -88,6 +88,31 @@ def test_point_cloud_basic_read(tmp_path): assert [e.as_py() for e in table["x"]] == pydict["x"] assert [e.as_py() for e in table["y"]] == pydict["y"] + # Ensure it cannot be opened by another type + with pytest.raises(soma.SOMAError): + soma.DataFrame.open(urljoin(baseuri, "default")) + + with pytest.raises(soma.SOMAError): + soma.SparseNDArray.open(urljoin(baseuri, "default")) + + with pytest.raises(soma.SOMAError): + soma.DenseNDArray.open(urljoin(baseuri, "default")) + + with pytest.raises(soma.SOMAError): + soma.Collection.open(urljoin(baseuri, "default")) + + with pytest.raises(soma.SOMAError): + soma.Experiment.open(urljoin(baseuri, "default")) + + with pytest.raises(soma.SOMAError): + soma.Measurement.open(urljoin(baseuri, "default")) + + with pytest.raises(soma.SOMAError): + soma.Scene.open(urljoin(baseuri, "default")) + + with pytest.raises(soma.SOMAError): + soma.MultiscaleImage.open(urljoin(baseuri, "default")) + def test_point_cloud_coordinate_space(tmp_path): uri = tmp_path.as_uri() diff --git a/apis/python/tests/test_scene.py b/apis/python/tests/test_scene.py index f8e6ad4b1c..4e488e4fe1 100644 --- a/apis/python/tests/test_scene.py +++ b/apis/python/tests/test_scene.py @@ -97,6 +97,31 @@ def test_scene_basic(tmp_path): with pytest.raises(soma.DoesNotExistError): soma.Scene.open("bad uri") + # Ensure it cannot be opened by another type + with pytest.raises(soma.SOMAError): + soma.DataFrame.open(baseuri) + + with pytest.raises(soma.SOMAError): + soma.SparseNDArray.open(baseuri) + + with pytest.raises(soma.SOMAError): + soma.DenseNDArray.open(baseuri) + + with pytest.raises(soma.SOMAError): + soma.PointCloudDataFrame.open(baseuri) + + with pytest.raises(soma.SOMAError): + soma.Collection.open(baseuri) + + with pytest.raises(soma.SOMAError): + soma.Experiment.open(baseuri) + + with pytest.raises(soma.SOMAError): + soma.Measurement.open(baseuri) + + with pytest.raises(soma.SOMAError): + soma.MultiscaleImage.open(baseuri) + def test_measurement_with_var_scene(tmp_path): baseuri = tmp_path.as_uri() diff --git a/apis/python/tests/test_sparse_nd_array.py b/apis/python/tests/test_sparse_nd_array.py index cedd88c68e..9480ee8598 100644 --- a/apis/python/tests/test_sparse_nd_array.py +++ b/apis/python/tests/test_sparse_nd_array.py @@ -83,10 +83,35 @@ def test_sparse_nd_array_create_ok( with soma.SparseNDArray.open(tmp_path.as_posix(), "r") as A: assert isinstance(A._handle._handle, soma.pytiledbsoma.SOMASparseNDArray) - # Ensure write mode uses Python object + # Ensure write mode uses clib object with soma.SparseNDArray.open(tmp_path.as_posix(), "w") as A: assert isinstance(A._handle._handle, soma.pytiledbsoma.SOMASparseNDArray) + # Ensure it cannot be opened by another type + with pytest.raises(soma.SOMAError): + soma.DataFrame.open(tmp_path.as_posix()) + + with pytest.raises(soma.SOMAError): + soma.DenseNDArray.open(tmp_path.as_posix()) + + with pytest.raises(soma.SOMAError): + soma.PointCloudDataFrame.open(tmp_path.as_posix()) + + with pytest.raises(soma.SOMAError): + soma.Collection.open(tmp_path.as_posix()) + + with pytest.raises(soma.SOMAError): + soma.Experiment.open(tmp_path.as_posix()) + + with pytest.raises(soma.SOMAError): + soma.Measurement.open(tmp_path.as_posix()) + + with pytest.raises(soma.SOMAError): + soma.Scene.open(tmp_path.as_posix()) + + with pytest.raises(soma.SOMAError): + soma.MultiscaleImage.open(tmp_path.as_posix()) + def test_sparse_nd_array_reopen(tmp_path): soma.SparseNDArray.create( diff --git a/apis/r/src/rinterface.cpp b/apis/r/src/rinterface.cpp index eeda0f9b22..70b9c1eff1 100644 --- a/apis/r/src/rinterface.cpp +++ b/apis/r/src/rinterface.cpp @@ -281,7 +281,7 @@ Rcpp::NumericVector maxshape( // [[Rcpp::export]] SEXP non_empty_domain( const std::string& uri, Rcpp::XPtr ctxxp) { - auto sdf = tdbs::SOMADataFrame::open(uri, OpenMode::read, ctxxp->ctxptr); + auto sdf = tdbs::SOMAArray::open(OpenMode::read, uri, ctxxp->ctxptr); tdbs::ArrowTable arrow_table = sdf->get_non_empty_domain(); SEXP retval = convert_domainish(arrow_table); sdf->close(); diff --git a/libtiledbsoma/src/soma/soma_array.cc b/libtiledbsoma/src/soma/soma_array.cc index 337b1c86da..51e6646fe5 100644 --- a/libtiledbsoma/src/soma/soma_array.cc +++ b/libtiledbsoma/src/soma/soma_array.cc @@ -142,7 +142,7 @@ SOMAArray::SOMAArray( ctx_ = std::make_shared(platform_config); validate(mode, name, timestamp); reset(column_names, batch_size, result_order); - fill_metadata_cache(); + fill_metadata_cache(timestamp); } SOMAArray::SOMAArray( @@ -160,7 +160,7 @@ SOMAArray::SOMAArray( , timestamp_(timestamp) { validate(mode, name, timestamp); reset(column_names, batch_size, result_order); - fill_metadata_cache(); + fill_metadata_cache(timestamp); } SOMAArray::SOMAArray( @@ -176,17 +176,22 @@ SOMAArray::SOMAArray( , arr_(arr) , schema_(std::make_shared(arr->schema())) { reset({}, batch_size_, result_order_); - fill_metadata_cache(); + fill_metadata_cache(timestamp); } -void SOMAArray::fill_metadata_cache() { +void SOMAArray::fill_metadata_cache(std::optional timestamp) { if (arr_->query_type() == TILEDB_WRITE) { - meta_cache_arr_ = std::make_shared( - *ctx_->tiledb_ctx(), - uri_, - TILEDB_READ, - TemporalPolicy( - TimestampStartEnd, timestamp()->first, timestamp()->second)); + if (timestamp) { + meta_cache_arr_ = std::make_shared( + *ctx_->tiledb_ctx(), + uri_, + TILEDB_READ, + TemporalPolicy( + TimestampStartEnd, timestamp->first, timestamp->second)); + } else { + meta_cache_arr_ = std::make_shared( + *ctx_->tiledb_ctx(), uri_, TILEDB_READ); + } } else { meta_cache_arr_ = arr_; } @@ -219,7 +224,7 @@ void SOMAArray::open(OpenMode mode, std::optional timestamp) { validate(mode, name_, timestamp); reset(column_names(), batch_size_, result_order_); - fill_metadata_cache(); + fill_metadata_cache(timestamp); } std::unique_ptr SOMAArray::reopen( @@ -236,8 +241,9 @@ std::unique_ptr SOMAArray::reopen( } void SOMAArray::close() { - if (arr_->query_type() == TILEDB_WRITE) + if (arr_->query_type() == TILEDB_WRITE) { meta_cache_arr_->close(); + } // Close the array through the managed query to ensure any pending queries // are completed. diff --git a/libtiledbsoma/src/soma/soma_array.h b/libtiledbsoma/src/soma/soma_array.h index eff4ffbd77..021c4fa477 100644 --- a/libtiledbsoma/src/soma/soma_array.h +++ b/libtiledbsoma/src/soma/soma_array.h @@ -234,7 +234,7 @@ class SOMAArray : public SOMAObject { , meta_cache_arr_(other.meta_cache_arr_) , first_read_next_(other.first_read_next_) , submitted_(other.submitted_) { - fill_metadata_cache(); + fill_metadata_cache(timestamp_); } SOMAArray( @@ -1533,7 +1533,7 @@ class SOMAArray : public SOMAObject { std::optional _maybe_soma_joinid_tiledb_current_domain(); std::optional _maybe_soma_joinid_tiledb_domain(); - void fill_metadata_cache(); + void fill_metadata_cache(std::optional timestamp); // SOMAArray URI std::string uri_; diff --git a/libtiledbsoma/src/soma/soma_collection.cc b/libtiledbsoma/src/soma/soma_collection.cc index d5dd15098f..5696b3935a 100644 --- a/libtiledbsoma/src/soma/soma_collection.cc +++ b/libtiledbsoma/src/soma/soma_collection.cc @@ -58,7 +58,15 @@ std::unique_ptr SOMACollection::open( std::shared_ptr ctx, std::optional timestamp) { try { - return std::make_unique(mode, uri, ctx, timestamp); + auto group = std::make_unique( + mode, uri, ctx, timestamp); + + if (!group->check_type("SOMACollection")) { + throw TileDBSOMAError( + "[SOMACollection::open] Object is not a SOMACollection"); + } + + return group; } catch (TileDBError& e) { throw TileDBSOMAError(e.what()); } diff --git a/libtiledbsoma/src/soma/soma_dataframe.cc b/libtiledbsoma/src/soma/soma_dataframe.cc index 92c042a7d3..b13c8621e9 100644 --- a/libtiledbsoma/src/soma/soma_dataframe.cc +++ b/libtiledbsoma/src/soma/soma_dataframe.cc @@ -65,8 +65,15 @@ std::unique_ptr SOMADataFrame::open( std::vector column_names, ResultOrder result_order, std::optional timestamp) { - return std::make_unique( + auto array = std::make_unique( mode, uri, ctx, column_names, result_order, timestamp); + + if (!array->check_type("SOMADataFrame")) { + throw TileDBSOMAError( + "[SOMADataFrame::open] Object is not a SOMADataFrame"); + } + + return array; } std::unique_ptr SOMADataFrame::open( @@ -74,7 +81,15 @@ std::unique_ptr SOMADataFrame::open( OpenMode mode, std::string_view name, std::map platform_config) { - return std::make_unique(mode, uri, name, platform_config); + auto array = std::make_unique( + mode, uri, name, platform_config); + + if (!array->check_type("SOMADataFrame")) { + throw TileDBSOMAError( + "[SOMADataFrame::open] Object is not a SOMADataFrame"); + } + + return array; } bool SOMADataFrame::exists( diff --git a/libtiledbsoma/src/soma/soma_dense_ndarray.cc b/libtiledbsoma/src/soma/soma_dense_ndarray.cc index 13d3f2ee18..2865720996 100644 --- a/libtiledbsoma/src/soma/soma_dense_ndarray.cc +++ b/libtiledbsoma/src/soma/soma_dense_ndarray.cc @@ -99,8 +99,15 @@ std::unique_ptr SOMADenseNDArray::open( std::vector column_names, ResultOrder result_order, std::optional timestamp) { - return std::make_unique( + auto array = std::make_unique( mode, uri, ctx, column_names, result_order, timestamp); + + if (!array->check_type("SOMADenseNDArray")) { + throw TileDBSOMAError( + "[SOMADenseNDArray::open] Object is not a SOMADenseNDArray"); + } + + return array; } bool SOMADenseNDArray::exists( diff --git a/libtiledbsoma/src/soma/soma_experiment.cc b/libtiledbsoma/src/soma/soma_experiment.cc index f6b2f3e3e2..02f4a6fde5 100644 --- a/libtiledbsoma/src/soma/soma_experiment.cc +++ b/libtiledbsoma/src/soma/soma_experiment.cc @@ -90,7 +90,15 @@ std::unique_ptr SOMAExperiment::open( std::shared_ptr ctx, std::optional timestamp) { try { - return std::make_unique(mode, uri, ctx, timestamp); + auto group = std::make_unique( + mode, uri, ctx, timestamp); + + if (!group->check_type("SOMAExperiment")) { + throw TileDBSOMAError( + "[SOMAExperiment::open] Object is not a SOMAExperiment"); + } + + return group; } catch (TileDBError& e) { throw TileDBSOMAError(e.what()); } diff --git a/libtiledbsoma/src/soma/soma_measurement.cc b/libtiledbsoma/src/soma/soma_measurement.cc index c6f1be6c48..7617822829 100644 --- a/libtiledbsoma/src/soma/soma_measurement.cc +++ b/libtiledbsoma/src/soma/soma_measurement.cc @@ -118,7 +118,15 @@ std::unique_ptr SOMAMeasurement::open( std::shared_ptr ctx, std::optional timestamp) { try { - return std::make_unique(mode, uri, ctx, timestamp); + auto group = std::make_unique( + mode, uri, ctx, timestamp); + + if (!group->check_type("SOMAMeasurement")) { + throw TileDBSOMAError( + "[SOMAMeasurement::open] Object is not a SOMAMeasurement"); + } + + return group; } catch (TileDBError& e) { throw TileDBSOMAError(e.what()); } diff --git a/libtiledbsoma/src/soma/soma_multiscale_image.cc b/libtiledbsoma/src/soma/soma_multiscale_image.cc index 82377144e6..feb625f4d4 100644 --- a/libtiledbsoma/src/soma/soma_multiscale_image.cc +++ b/libtiledbsoma/src/soma/soma_multiscale_image.cc @@ -59,7 +59,16 @@ std::unique_ptr SOMAMultiscaleImage::open( std::shared_ptr ctx, std::optional timestamp) { try { - return std::make_unique(mode, uri, ctx, timestamp); + auto group = std::make_unique( + mode, uri, ctx, timestamp); + + if (!group->check_type("SOMAMultiscaleImage")) { + throw TileDBSOMAError( + "[SOMAMultiscaleImage::open] Object is not a " + "SOMAMultiscaleImage"); + } + + return group; } catch (TileDBError& e) { throw TileDBSOMAError(e.what()); } diff --git a/libtiledbsoma/src/soma/soma_object.cc b/libtiledbsoma/src/soma/soma_object.cc index e9cc3627bf..2a4eea25fd 100644 --- a/libtiledbsoma/src/soma/soma_object.cc +++ b/libtiledbsoma/src/soma/soma_object.cc @@ -35,7 +35,8 @@ std::unique_ptr SOMAObject::open( soma_type = "SOMAGroup"; break; default: - throw TileDBSOMAError("Saw invalid TileDB type"); + throw TileDBSOMAError( + "[SOMAObject::open] Saw invalid TileDB type"); } } @@ -45,7 +46,8 @@ std::unique_ptr SOMAObject::open( auto array_type = array_->type(); if (!array_type.has_value()) - throw TileDBSOMAError("SOMAArray has no type info"); + throw TileDBSOMAError( + "[SOMAObject::open] SOMAArray has no type info"); std::transform( array_type->begin(), @@ -64,14 +66,16 @@ std::unique_ptr SOMAObject::open( } else if (array_type == "somageometrydataframe") { return std::make_unique(*array_); } else { - throw TileDBSOMAError("Saw invalid SOMAArray type"); + throw TileDBSOMAError( + "[SOMAObject::open] Saw invalid SOMAArray type"); } } else if (soma_type == "SOMAGroup") { auto group_ = SOMAGroup::open(mode, uri, ctx, "", timestamp); auto group_type = group_->type(); if (!group_type.has_value()) - throw TileDBSOMAError("SOMAGroup has no type info"); + throw TileDBSOMAError( + "[SOMAObject::open] SOMAGroup has no type info"); std::transform( group_type->begin(), @@ -90,11 +94,13 @@ std::unique_ptr SOMAObject::open( } else if (group_type == "somamultiscaleimage") { return std::make_unique(*group_); } else { - throw TileDBSOMAError("Saw invalid SOMAGroup type"); + throw TileDBSOMAError( + "[SOMAObject::open] Saw invalid SOMAGroup type"); } } - throw TileDBSOMAError("Invalid TileDB object passed to SOMAObject::open"); + throw TileDBSOMAError( + "[SOMAObject::open] Invalid TileDB object passed to SOMAObject::open"); } const std::optional SOMAObject::type() { @@ -110,4 +116,25 @@ const std::optional SOMAObject::type() { return std::string(dtype, sz); } +bool SOMAObject::check_type(std::string expected_type) { + auto soma_object_type = this->type(); + + if (!soma_object_type.has_value()) + return false; + + std::transform( + soma_object_type->begin(), + soma_object_type->end(), + soma_object_type->begin(), + [](unsigned char c) { return std::tolower(c); }); + + std::transform( + expected_type.begin(), + expected_type.end(), + expected_type.begin(), + [](unsigned char c) { return std::tolower(c); }); + + return soma_object_type == expected_type; +}; + } // namespace tiledbsoma diff --git a/libtiledbsoma/src/soma/soma_object.h b/libtiledbsoma/src/soma/soma_object.h index 99bee2fa08..a9656b90fd 100644 --- a/libtiledbsoma/src/soma/soma_object.h +++ b/libtiledbsoma/src/soma/soma_object.h @@ -64,6 +64,8 @@ class SOMAObject { */ const std::optional type(); + bool check_type(std::string expected_type); + /** * @brief Get URI of the SOMAObject. * diff --git a/libtiledbsoma/src/soma/soma_point_cloud_dataframe.cc b/libtiledbsoma/src/soma/soma_point_cloud_dataframe.cc index a7984712d7..cb27e81436 100644 --- a/libtiledbsoma/src/soma/soma_point_cloud_dataframe.cc +++ b/libtiledbsoma/src/soma/soma_point_cloud_dataframe.cc @@ -65,8 +65,16 @@ std::unique_ptr SOMAPointCloudDataFrame::open( std::vector column_names, ResultOrder result_order, std::optional timestamp) { - return std::make_unique( + auto array = std::make_unique( mode, uri, ctx, column_names, result_order, timestamp); + + if (!array->check_type("SOMAPointCloudDataFrame")) { + throw TileDBSOMAError( + "[SOMAPointCloudDataFrame::open] Object is not a " + "SOMAPointCloudDataFrame"); + } + + return array; } bool SOMAPointCloudDataFrame::exists( diff --git a/libtiledbsoma/src/soma/soma_scene.cc b/libtiledbsoma/src/soma/soma_scene.cc index 680d21a186..d8a3df539c 100644 --- a/libtiledbsoma/src/soma/soma_scene.cc +++ b/libtiledbsoma/src/soma/soma_scene.cc @@ -62,7 +62,14 @@ std::unique_ptr SOMAScene::open( std::shared_ptr ctx, std::optional timestamp) { try { - return std::make_unique(mode, uri, ctx, timestamp); + auto group = std::make_unique(mode, uri, ctx, timestamp); + + if (!group->check_type("SOMAScene")) { + throw TileDBSOMAError( + "[SOMAScene::open] Object is not a SOMAScene"); + } + + return group; } catch (TileDBError& e) { throw TileDBSOMAError(e.what()); } diff --git a/libtiledbsoma/src/soma/soma_sparse_ndarray.cc b/libtiledbsoma/src/soma/soma_sparse_ndarray.cc index f518ebe342..87d66ade6a 100644 --- a/libtiledbsoma/src/soma/soma_sparse_ndarray.cc +++ b/libtiledbsoma/src/soma/soma_sparse_ndarray.cc @@ -100,8 +100,15 @@ std::unique_ptr SOMASparseNDArray::open( std::vector column_names, ResultOrder result_order, std::optional timestamp) { - return std::make_unique( + auto array = std::make_unique( mode, uri, ctx, column_names, result_order, timestamp); + + if (!array->check_type("SOMASparseNDArray")) { + throw TileDBSOMAError( + "[SOMASparseNDArray::open] Object is not a SOMASparseNDArray"); + } + + return array; } bool SOMASparseNDArray::exists( diff --git a/libtiledbsoma/test/unit_soma_dataframe.cc b/libtiledbsoma/test/unit_soma_dataframe.cc index a1e28aaab2..3780f85549 100644 --- a/libtiledbsoma/test/unit_soma_dataframe.cc +++ b/libtiledbsoma/test/unit_soma_dataframe.cc @@ -377,10 +377,10 @@ TEST_CASE_METHOD( REQUIRE(!SOMADataFrame::exists(uri_, ctx_)); - create(dim_infos, attr_infos, PlatformConfig(), TimestampRange(0, 2)); + create(dim_infos, attr_infos, PlatformConfig(), TimestampRange(0, 1)); auto sdf = open( - OpenMode::write, ResultOrder::automatic, TimestampRange(1, 1)); + OpenMode::write, ResultOrder::automatic, TimestampRange(0, 2)); int32_t val = 100; sdf->set_metadata("md", TILEDB_INT32, 1, &val); @@ -398,8 +398,8 @@ TEST_CASE_METHOD( REQUIRE(*((const int32_t*)std::get(*mdval)) == 100); sdf->close(); - // md should not be available at (2, 2) - sdf->open(OpenMode::read, TimestampRange(2, 2)); + // md should not be available at (0, 1) + sdf->open(OpenMode::read, TimestampRange(0, 1)); REQUIRE(sdf->metadata_num() == 2); REQUIRE(sdf->has_metadata("soma_object_type")); REQUIRE(sdf->has_metadata("soma_encoding_version")); @@ -407,7 +407,7 @@ TEST_CASE_METHOD( sdf->close(); // Metadata should also be retrievable in write mode - sdf->open(OpenMode::write, TimestampRange(0, 2)); + sdf->open(OpenMode::write); REQUIRE(sdf->metadata_num() == 3); REQUIRE(sdf->has_metadata("soma_object_type")); REQUIRE(sdf->has_metadata("soma_encoding_version")); @@ -415,15 +415,14 @@ TEST_CASE_METHOD( mdval = sdf->get_metadata("md"); REQUIRE(*((const int32_t*)std::get(*mdval)) == 100); - // Delete and have it reflected when reading metadata while in - // write mode + // Delete and have it reflected when reading metadata while in write mode sdf->delete_metadata("md"); mdval = sdf->get_metadata("md"); REQUIRE(!mdval.has_value()); sdf->close(); // Confirm delete in read mode - sdf->open(OpenMode::read, TimestampRange(0, 2)); + sdf->open(OpenMode::read); REQUIRE(!sdf->has_metadata("md")); REQUIRE(sdf->metadata_num() == 2); } diff --git a/libtiledbsoma/test/unit_soma_dense_ndarray.cc b/libtiledbsoma/test/unit_soma_dense_ndarray.cc index 4d1aae7f9c..697f87db45 100644 --- a/libtiledbsoma/test/unit_soma_dense_ndarray.cc +++ b/libtiledbsoma/test/unit_soma_dense_ndarray.cc @@ -157,14 +157,14 @@ TEST_CASE("SOMADenseNDArray: metadata", "[SOMADenseNDArray]") { auto index_columns = helper::create_column_index_info(dim_infos); - SOMASparseNDArray::create( + SOMADenseNDArray::create( uri, arrow_format, ArrowTable( std::move(index_columns.first), std::move(index_columns.second)), ctx, PlatformConfig(), - TimestampRange(0, 2)); + TimestampRange(0, 1)); auto dnda = SOMADenseNDArray::open( uri, @@ -172,7 +172,7 @@ TEST_CASE("SOMADenseNDArray: metadata", "[SOMADenseNDArray]") { ctx, {}, ResultOrder::automatic, - std::pair(1, 1)); + TimestampRange(0, 2)); int32_t val = 100; dnda->set_metadata("md", TILEDB_INT32, 1, &val); @@ -190,8 +190,8 @@ TEST_CASE("SOMADenseNDArray: metadata", "[SOMADenseNDArray]") { REQUIRE(*((const int32_t*)std::get(*mdval)) == 100); dnda->close(); - // md should not be available at (2, 2) - dnda->open(OpenMode::read, TimestampRange(2, 2)); + // md should not be available at (0, 1) + dnda->open(OpenMode::read, TimestampRange(0, 1)); REQUIRE(dnda->metadata_num() == 2); REQUIRE(dnda->has_metadata("soma_object_type")); REQUIRE(dnda->has_metadata("soma_encoding_version")); @@ -199,7 +199,7 @@ TEST_CASE("SOMADenseNDArray: metadata", "[SOMADenseNDArray]") { dnda->close(); // Metadata should also be retrievable in write mode - dnda->open(OpenMode::write, TimestampRange(0, 2)); + dnda->open(OpenMode::write); REQUIRE(dnda->metadata_num() == 3); REQUIRE(dnda->has_metadata("soma_object_type")); REQUIRE(dnda->has_metadata("soma_encoding_version")); @@ -207,15 +207,14 @@ TEST_CASE("SOMADenseNDArray: metadata", "[SOMADenseNDArray]") { mdval = dnda->get_metadata("md"); REQUIRE(*((const int32_t*)std::get(*mdval)) == 100); - // Delete and have it reflected when reading metadata while in write - // mode + // Delete and have it reflected when reading metadata while in write mode dnda->delete_metadata("md"); mdval = dnda->get_metadata("md"); REQUIRE(!mdval.has_value()); dnda->close(); // Confirm delete in read mode - dnda->open(OpenMode::read, TimestampRange(0, 2)); + dnda->open(OpenMode::read); REQUIRE(!dnda->has_metadata("md")); REQUIRE(dnda->metadata_num() == 2); } diff --git a/libtiledbsoma/test/unit_soma_sparse_ndarray.cc b/libtiledbsoma/test/unit_soma_sparse_ndarray.cc index b386443213..24cf9b4f0b 100644 --- a/libtiledbsoma/test/unit_soma_sparse_ndarray.cc +++ b/libtiledbsoma/test/unit_soma_sparse_ndarray.cc @@ -223,7 +223,7 @@ TEST_CASE("SOMASparseNDArray: metadata", "[SOMASparseNDArray]") { std::move(index_columns.first), std::move(index_columns.second)), ctx, PlatformConfig(), - TimestampRange(0, 2)); + TimestampRange(0, 1)); auto snda = SOMASparseNDArray::open( uri, @@ -231,7 +231,7 @@ TEST_CASE("SOMASparseNDArray: metadata", "[SOMASparseNDArray]") { ctx, {}, ResultOrder::automatic, - std::pair(1, 1)); + TimestampRange(0, 2)); int32_t val = 100; snda->set_metadata("md", TILEDB_INT32, 1, &val); @@ -249,8 +249,8 @@ TEST_CASE("SOMASparseNDArray: metadata", "[SOMASparseNDArray]") { REQUIRE(*((const int32_t*)std::get(*mdval)) == 100); snda->close(); - // md should not be available at (2, 2) - snda->open(OpenMode::read, TimestampRange(2, 2)); + // md should not be available at (0, 1) + snda->open(OpenMode::read, TimestampRange(0, 1)); REQUIRE(snda->metadata_num() == 2); REQUIRE(snda->has_metadata("soma_object_type")); REQUIRE(snda->has_metadata("soma_encoding_version")); @@ -258,7 +258,7 @@ TEST_CASE("SOMASparseNDArray: metadata", "[SOMASparseNDArray]") { snda->close(); // Metadata should also be retrievable in write mode - snda->open(OpenMode::write, TimestampRange(0, 2)); + snda->open(OpenMode::write); REQUIRE(snda->metadata_num() == 3); REQUIRE(snda->has_metadata("soma_object_type")); REQUIRE(snda->has_metadata("soma_encoding_version")); @@ -266,15 +266,16 @@ TEST_CASE("SOMASparseNDArray: metadata", "[SOMASparseNDArray]") { mdval = snda->get_metadata("md"); REQUIRE(*((const int32_t*)std::get(*mdval)) == 100); - // Delete and have it reflected when reading metadata while in write - // mode + // Delete and have it reflected when reading metadata while in write mode snda->delete_metadata("md"); + REQUIRE(!snda->has_metadata("md")); + REQUIRE(snda->metadata_num() == 2); mdval = snda->get_metadata("md"); REQUIRE(!mdval.has_value()); snda->close(); // Confirm delete in read mode - snda->open(OpenMode::read, TimestampRange(0, 2)); + snda->open(OpenMode::read); REQUIRE(!snda->has_metadata("md")); REQUIRE(snda->metadata_num() == 2); }