diff --git a/apis/python/setup.py b/apis/python/setup.py index e1cb167f8e..999f95fafa 100644 --- a/apis/python/setup.py +++ b/apis/python/setup.py @@ -303,6 +303,7 @@ def run(self): "src/tiledbsoma/common.cc", "src/tiledbsoma/reindexer.cc", "src/tiledbsoma/query_condition.cc", + "src/tiledbsoma/soma_vfs.cc", "src/tiledbsoma/soma_context.cc", "src/tiledbsoma/soma_array.cc", "src/tiledbsoma/soma_object.cc", diff --git a/apis/python/src/tiledbsoma/io/_registration/ambient_label_mappings.py b/apis/python/src/tiledbsoma/io/_registration/ambient_label_mappings.py index 5d8723ffd2..6ecff32bb5 100644 --- a/apis/python/src/tiledbsoma/io/_registration/ambient_label_mappings.py +++ b/apis/python/src/tiledbsoma/io/_registration/ambient_label_mappings.py @@ -189,8 +189,7 @@ def from_isolated_h5ad( experiment, not in append mode, but allowing us to still have the bulk of the ingestor code to be non-duplicated between non-append mode and append mode. """ - tiledb_ctx = None if context is None else context.tiledb_ctx - with read_h5ad(h5ad_file_name, mode="r", ctx=tiledb_ctx) as adata: + with read_h5ad(h5ad_file_name, mode="r", ctx=context) as adata: return cls.from_isolated_anndata( adata, measurement_name=measurement_name, @@ -434,8 +433,7 @@ def from_h5ad_append_on_experiment( """Extends registration data to one more H5AD input file.""" tiledbsoma.logging.logger.info(f"Registration: registering {h5ad_file_name}.") - tiledb_ctx = None if context is None else context.tiledb_ctx - with read_h5ad(h5ad_file_name, mode="r", ctx=tiledb_ctx) as adata: + with read_h5ad(h5ad_file_name, mode="r", ctx=context) as adata: return cls.from_anndata_append_on_experiment( adata, previous, diff --git a/apis/python/src/tiledbsoma/io/_util.py b/apis/python/src/tiledbsoma/io/_util.py index 3376b3be81..e69c97a673 100644 --- a/apis/python/src/tiledbsoma/io/_util.py +++ b/apis/python/src/tiledbsoma/io/_util.py @@ -16,10 +16,10 @@ import pyarrow as pa from anndata._core import file_backing -import tiledb - +from .. import pytiledbsoma as clib from .._exception import SOMAError from .._types import Path +from ..options import SOMATileDBContext _pa_type_to_str_fmt = { pa.string(): "U", @@ -42,12 +42,14 @@ @contextmanager def read_h5ad( - input_path: Path, *, mode: str = "r", ctx: Optional[tiledb.Ctx] = None + input_path: Path, *, mode: str = "r", ctx: Optional[SOMATileDBContext] = None ) -> Iterator[ad.AnnData]: """ This lets us ingest H5AD with "r" (backed mode) from S3 URIs. """ - input_handle = tiledb.VFS(ctx=ctx).open(input_path) + ctx = ctx or SOMATileDBContext() + vfs = clib.SOMAVFS(ctx.native_context) + input_handle = clib.SOMAVFSFilebuf(vfs).open(str(input_path)) try: with _hack_patch_anndata(): anndata = ad.read_h5ad(_FSPathWrapper(input_handle, input_path), mode) diff --git a/apis/python/src/tiledbsoma/io/ingest.py b/apis/python/src/tiledbsoma/io/ingest.py index caccd0a699..ae0132109f 100644 --- a/apis/python/src/tiledbsoma/io/ingest.py +++ b/apis/python/src/tiledbsoma/io/ingest.py @@ -358,7 +358,7 @@ def from_h5ad( logging.log_io(None, f"START READING {input_path}") - with read_h5ad(input_path, mode="r", ctx=context.tiledb_ctx) as anndata: + with read_h5ad(input_path, mode="r", ctx=context) as anndata: logging.log_io(None, _util.format_elapsed(s, f"FINISH READING {input_path}")) uri = from_anndata( diff --git a/apis/python/src/tiledbsoma/options/_soma_tiledb_context.py b/apis/python/src/tiledbsoma/options/_soma_tiledb_context.py index f607dd9b9b..163578080f 100644 --- a/apis/python/src/tiledbsoma/options/_soma_tiledb_context.py +++ b/apis/python/src/tiledbsoma/options/_soma_tiledb_context.py @@ -9,34 +9,17 @@ import functools import threading import time -import warnings from concurrent.futures import ThreadPoolExecutor from typing import Any, Dict, Literal, Mapping, Optional, Union from somacore import ContextBase from typing_extensions import Self -import tiledb - from .. import pytiledbsoma as clib from .._types import OpenTimestamp from .._util import ms_to_datetime, to_timestamp_ms -def _warn_ctx_deprecation() -> None: - pass - # https://github.com/single-cell-data/TileDB-SOMA/issues/3134 - # Skipping for 1.15.0rc0 - # assert_version_before(1, 15) - warnings.warn( - "tiledb_ctx is now deprecated for removal in 1.15. " - "Use tiledb_config instead by passing " - "SOMATileDBContext(tiledb_config=ctx.config().dict()).", - DeprecationWarning, - stacklevel=3, - ) - - def _default_config( override: Mapping[str, Union[str, float]] ) -> Dict[str, Union[str, float]]: @@ -53,9 +36,9 @@ def _default_config( @functools.lru_cache(maxsize=None) -def _default_global_ctx() -> tiledb.Ctx: - """Lazily builds a default TileDB Context with the default config.""" - return tiledb.Ctx(_default_config({})) +def _default_global_native_context() -> clib.SOMAContext: + """Lazily builds a default SOMAContext with the default config.""" + return clib.SOMAContext({k: str(v) for k, v in _default_config({}).items()}) def _maybe_timestamp_ms(input: Optional[OpenTimestamp]) -> Optional[int]: @@ -83,7 +66,6 @@ class SOMATileDBContext(ContextBase): def __init__( self, - tiledb_ctx: Optional[tiledb.Ctx] = None, tiledb_config: Optional[Dict[str, Union[str, float]]] = None, timestamp: Optional[OpenTimestamp] = None, threadpool: Optional[ThreadPoolExecutor] = None, @@ -135,27 +117,12 @@ def __init__( provided, a new ThreadPoolExecutor will be created with default settings. """ - if tiledb_ctx is not None: - _warn_ctx_deprecation() - - if tiledb_ctx is not None and tiledb_config is not None: - raise ValueError( - "only one of tiledb_ctx or tiledb_config" - " may be set when constructing a SOMATileDBContext" - ) self._lock = threading.Lock() """A lock to ensure single initialization of ``_tiledb_ctx``.""" - self._initial_config = ( + self._initial_config: Optional[Dict[str, Union[str, float]]] = ( None if tiledb_config is None else _default_config(tiledb_config) ) - """A dictionary of options to override the default TileDB config. - - This includes both the user-provided options and the default options - that we provide to TileDB. If this is unset, then either we were - provided with a TileDB Ctx, or we need to use The Default Global Ctx. - """ - self._tiledb_ctx = tiledb_ctx """The TileDB context to use, either provided or lazily constructed.""" self._timestamp_ms = _maybe_timestamp_ms(timestamp) @@ -186,25 +153,14 @@ def native_context(self) -> clib.SOMAContext: """The C++ SOMAContext for this SOMA context.""" with self._lock: if self._native_context is None: - cfg = self._internal_tiledb_config() - self._native_context = clib.SOMAContext( - {k: str(v) for k, v in cfg.items()} - ) - return self._native_context - - @property - def tiledb_ctx(self) -> tiledb.Ctx: - """The TileDB-Py Context for this SOMA context.""" - _warn_ctx_deprecation() - - with self._lock: - if self._tiledb_ctx is None: if self._initial_config is None: - # Special case: we need to use the One Global Default. - self._tiledb_ctx = _default_global_ctx() + self._native_context = _default_global_native_context() else: - self._tiledb_ctx = tiledb.Ctx(self._initial_config) - return self._tiledb_ctx + cfg = self._internal_tiledb_config() + self._native_context = clib.SOMAContext( + {k: str(v) for k, v in cfg.items()} + ) + return self._native_context @property def tiledb_config(self) -> Dict[str, Union[str, float]]: @@ -230,11 +186,6 @@ def _internal_tiledb_config(self) -> Dict[str, Union[str, float]]: if self._native_context is not None: return dict(self._native_context.config()) - # We have TileDB Context. Return its actual config. - # TODO This block will be deleted once tiledb_ctx is removed in 1.15 - if self._tiledb_ctx is not None: - return dict(self._tiledb_ctx.config()) - # Our context has not yet been built. # We return what will be passed into the context. return ( @@ -247,7 +198,6 @@ def replace( self, *, tiledb_config: Optional[Dict[str, Any]] = None, - tiledb_ctx: Optional[tiledb.Ctx] = None, timestamp: Union[None, OpenTimestamp, _Unset] = _UNSET, threadpool: Union[None, ThreadPoolExecutor, _Unset] = _UNSET, ) -> Self: @@ -279,15 +229,7 @@ def replace( ... tiledb_config={"vfs.s3.region": None}) """ with self._lock: - if tiledb_ctx is not None: - _warn_ctx_deprecation() - if tiledb_config is not None: - if tiledb_ctx: - raise ValueError( - "Either tiledb_config or tiledb_ctx may be provided" - " to replace(), but not both." - ) new_config = self._internal_tiledb_config() new_config.update(tiledb_config) tiledb_config = {k: v for (k, v) in new_config.items() if v is not None} @@ -302,7 +244,6 @@ def replace( assert timestamp is None or isinstance(timestamp, (datetime.datetime, int)) return type(self)( tiledb_config=tiledb_config, - tiledb_ctx=tiledb_ctx, timestamp=timestamp, threadpool=threadpool, ) @@ -327,11 +268,6 @@ def _validate_soma_tiledb_context(context: Any) -> SOMATileDBContext: if context is None: return SOMATileDBContext() - if isinstance(context, tiledb.Ctx): - raise TypeError( - "context is a tiledb.Ctx, not a SOMATileDBContext -- please wrap it in tiledbsoma.SOMATileDBContext(...)" - ) - if not isinstance(context, SOMATileDBContext): raise TypeError("context is not a SOMATileDBContext") diff --git a/apis/python/src/tiledbsoma/options/_tiledb_create_write_options.py b/apis/python/src/tiledbsoma/options/_tiledb_create_write_options.py index 259e0e9c65..fa0277a525 100644 --- a/apis/python/src/tiledbsoma/options/_tiledb_create_write_options.py +++ b/apis/python/src/tiledbsoma/options/_tiledb_create_write_options.py @@ -1,4 +1,3 @@ -import warnings from typing import ( Any, Dict, @@ -11,7 +10,6 @@ TypedDict, TypeVar, Union, - cast, ) import attrs as attrs_ # We use the name `attrs` later. @@ -19,10 +17,6 @@ from somacore import options from typing_extensions import Self -import tiledb - -from .._general_utilities import assert_version_before - # Most defaults are configured directly as default attribute values # within TileDBCreateOptions. DEFAULT_TILE_EXTENT = 2048 @@ -192,44 +186,6 @@ def cell_tile_orders(self) -> Tuple[Optional[str], Optional[str]]: return DEFAULT_CELL_ORDER, DEFAULT_TILE_ORDER return self.cell_order, self.tile_order - def offsets_filters_tiledb(self) -> Tuple[tiledb.Filter, ...]: - """Constructs the real TileDB Filters to use for offsets.""" - assert_version_before(1, 15) - warnings.warn( - "`offsets_filters_tiledb` is now deprecated for removal in 1.15 " - "as we no longer support returning tiledb.Filter. " - "Use `offsets_filters` instead.", - DeprecationWarning, - ) - - return tuple(_build_filter(f) for f in self.offsets_filters) - - def validity_filters_tiledb(self) -> Optional[Tuple[tiledb.Filter, ...]]: - """Constructs the real TileDB Filters to use for the validity map.""" - assert_version_before(1, 15) - warnings.warn( - "`validity_filters_tiledb` is now deprecated for removal in 1.15 " - "as we no longer support returning tiledb.Filter. " - "Use `validity_filters` instead.", - DeprecationWarning, - ) - if self.validity_filters is None: - return None - return tuple(_build_filter(f) for f in self.validity_filters) - - def dim_filters_tiledb( - self, dim: str, default: Sequence[_FilterSpec] = () - ) -> Tuple[tiledb.Filter, ...]: - """Constructs the real TileDB Filters to use for the named dimension.""" - assert_version_before(1, 15) - warnings.warn( - "`dim_filters_tiledb` is now deprecated for removal in 1.15 " - "as we no longer support returning tiledb.Filter. " - "Use `dims` instead.", - DeprecationWarning, - ) - return _filters_from(self.dims, dim, default) - def dim_tile(self, dim_name: str, default: int = DEFAULT_TILE_EXTENT) -> int: """Returns the tile extent for the given dimension.""" try: @@ -238,19 +194,6 @@ def dim_tile(self, dim_name: str, default: int = DEFAULT_TILE_EXTENT) -> int: return default return default if dim.tile is None else dim.tile - def attr_filters_tiledb( - self, name: str, default: Sequence[_FilterSpec] = () - ) -> Tuple[tiledb.Filter, ...]: - """Constructs the real TileDB Filters to use for the named attribute.""" - assert_version_before(1, 15) - warnings.warn( - "`attr_filters_tiledb` is now deprecated for removal in 1.15 " - "as we no longer support returning tiledb.Filter. " - "Use `attrs` instead.", - DeprecationWarning, - ) - return _filters_from(self.attrs, name, default) - @attrs_.define(frozen=True, kw_only=True, slots=True) class TileDBWriteOptions: @@ -329,8 +272,25 @@ def _dig_platform_config( # Filter handling and construction. # -_FILTERS: Mapping[str, Type[tiledb.Filter]] = { - cls.__name__: cls for cls in tiledb.FilterList.filter_type_cc_to_python.values() +_FILTERS: Mapping[str, str] = { + "GzipFilter": "GZIP", + "ZstdFilter": "ZSTD", + "LZ4Filter": "LZ4", + "Bzip2Filter": "BZIP2", + "RleFilter": "RLE", + "DeltaFilter": "DELTA", + "DoubleDeltaFilter": "DOUBLE_DELTA", + "BitWidthReductionFilter": "BIT_WIDTH_REDUCTION", + "BitShuffleFilter": "BITSHUFFLE", + "ByteShuffleFilter": "BYTESHUFFLE", + "PositiveDeltaFilter": "POSITIVE_DELTA", + "ChecksumMD5Filter": "CHECKSUM_MD5", + "ChecksumSHA256Filter": "CHECKSUM_SHA256", + "DictionaryFilter": "DICTIONARY", + "FloatScaleFilter": "SCALE_FLOAT", + "XORFilter": "XOR", + "WebpFilter": "WEBP", + "NoOpFilter": "NONE", } @@ -355,30 +315,3 @@ def _normalize_filter(input: _FilterSpec) -> _DictFilterSpec: except KeyError as ke: raise ValueError(f"filter type {typ_name!r} unknown") from ke return dict(input) - - -def _filters_from( - col_configs: Mapping[str, _ColumnConfig], name: str, default: Sequence[_FilterSpec] -) -> Tuple[tiledb.Filter, ...]: - """Constructs the filters for the named column in ``col_configs``.""" - try: - cfg = col_configs[name] - except KeyError: - maybe_filters = None - else: - maybe_filters = cfg.filters - if maybe_filters is None: - filters = _normalize_filters(default) or () - else: - filters = maybe_filters - return tuple(_build_filter(f) for f in filters) - - -def _build_filter(item: _DictFilterSpec) -> tiledb.Filter: - """Build a single filter.""" - # Always make a copy here so we don't mutate the global state. - # We have validated this earlier so we don't do extra checking here. - kwargs = dict(item) - cls_name = cast(str, kwargs.pop("_type")) - cls = _FILTERS[cls_name] - return cls(**kwargs) diff --git a/apis/python/src/tiledbsoma/pytiledbsoma.cc b/apis/python/src/tiledbsoma/pytiledbsoma.cc index 3b8c141b22..c4aff6e4e1 100644 --- a/apis/python/src/tiledbsoma/pytiledbsoma.cc +++ b/apis/python/src/tiledbsoma/pytiledbsoma.cc @@ -27,6 +27,7 @@ void load_soma_group(py::module&); void load_soma_collection(py::module&); void load_query_condition(py::module&); void load_reindexer(py::module&); +void load_soma_vfs(py::module&); PYBIND11_MODULE(pytiledbsoma, m) { py::register_exception(m, "SOMAError"); @@ -150,6 +151,7 @@ PYBIND11_MODULE(pytiledbsoma, m) { load_soma_collection(m); load_query_condition(m); load_reindexer(m); + load_soma_vfs(m); } }; // namespace libtiledbsomacpp diff --git a/apis/python/src/tiledbsoma/soma_vfs.cc b/apis/python/src/tiledbsoma/soma_vfs.cc new file mode 100644 index 0000000000..14202fd975 --- /dev/null +++ b/apis/python/src/tiledbsoma/soma_vfs.cc @@ -0,0 +1,66 @@ +/** + * @file soma_vfs.cc + * + * @section LICENSE + * + * The MIT License + * + * @copyright Copyright (c) 2024 TileDB, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @section DESCRIPTION + * + * This file defines the VFS bindings. + */ + +#include "common.h" + +namespace libtiledbsomacpp { + +namespace py = pybind11; +using namespace py::literals; +using namespace tiledbsoma; +using VFSFilebuf = tiledb::impl::VFSFilebuf; + +// TODO This temporary workaround prevents namespace clash with tiledb-py. +// Bind tiledb::VFS directly once tiledb-py dependency is removed +class SOMAVFS : public tiledb::VFS { + public: + using tiledb::VFS::VFS; +}; + +void load_soma_vfs(py::module& m) { + py::class_(m, "SOMAVFS") + .def( + py::init([](std::shared_ptr context) { + return SOMAVFS(*context->tiledb_ctx()); + }), + "ctx"_a); + + py::class_(m, "SOMAVFSFilebuf") + .def(py::init()) + .def( + "open", + [](VFSFilebuf& buf, const std::string& uri) { + return buf.open(uri, std::ios::in); + }) + .def("close", &VFSFilebuf::close, "should_throw"_a = true); +} +} // namespace libtiledbsomacpp diff --git a/apis/python/src/tiledbsoma/vfs.cc b/apis/python/src/tiledbsoma/vfs.cc new file mode 100644 index 0000000000..f2bc0a16ef --- /dev/null +++ b/apis/python/src/tiledbsoma/vfs.cc @@ -0,0 +1,59 @@ +/** + * @file vfs.cc + * + * @section LICENSE + * + * The MIT License + * + * @copyright Copyright (c) 2024 TileDB, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @section DESCRIPTION + * + * This file defines the VFS bindings. + */ + +#include "common.h" + +namespace libtiledbsomacpp { + +namespace py = pybind11; +using namespace py::literals; +using namespace tiledbsoma; + +using VFSFilebuf = tiledb::impl::VFSFilebuf; + +void load_vfs(py::module& m) { + py::class_(m, "VFS").def( + py::init([](std::shared_ptr context) { + return tiledb::VFS(*context->tiledb_ctx()); + }), + "ctx"_a); + + py::class_(m, "VFSFilebuf") + .def(py::init()) + .def( + "open", + [](VFSFilebuf& buf, const std::string& uri) { + return buf.open(uri, std::ios::in); + }) + .def("close", &VFSFilebuf::close, "should_throw"_a = true); +} +} // namespace libtiledbsomacpp diff --git a/apis/python/tests/test_context.py b/apis/python/tests/test_context.py index 68cef0fe99..ac5a716c2f 100644 --- a/apis/python/tests/test_context.py +++ b/apis/python/tests/test_context.py @@ -6,40 +6,27 @@ import tiledbsoma import tiledbsoma.options._soma_tiledb_context as stc -import tiledb - - -@pytest.fixture(autouse=True) -def global_ctx_reset(): - stc._default_global_ctx.cache_clear() - yield +import tiledbsoma.pytiledbsoma as clib def test_lazy_init(): """Verifies we don't construct a Ctx until we have to.""" - with mock.patch.object(tiledb, "Ctx", wraps=tiledb.Ctx) as mock_ctx: + with mock.patch.object(clib, "SOMAContext", wraps=clib.SOMAContext) as mock_ctx: context = stc.SOMATileDBContext(tiledb_config={}) assert context.tiledb_config == { "sm.mem.reader.sparse_global_order.ratio_array_data": 0.3 } mock_ctx.assert_not_called() - assert context._tiledb_ctx is None + assert context._native_context is None # Invoke the @property twice to ensure we only build one Ctx. - with pytest.deprecated_call(): - assert context.tiledb_ctx is context.tiledb_ctx + assert context.native_context is not None + assert context.native_context is context.native_context mock_ctx.assert_called_once() -def test_tiledb_ctx_init(): - config = {"hither": "yon"} - with pytest.deprecated_call(): - context = stc.SOMATileDBContext(tiledb_ctx=tiledb.Ctx(config)) - assert "hither" in context.tiledb_config - - def test_lazy_replace_config(): """Verifies we don't construct a Ctx even if we call ``.replace``.""" - with mock.patch.object(tiledb, "Ctx", wraps=tiledb.Ctx) as mock_ctx: + with mock.patch.object(clib, "SOMAContext", wraps=clib.SOMAContext) as mock_ctx: context = stc.SOMATileDBContext() new_context = context.replace(tiledb_config={"hello": "goodbye"}) assert new_context.tiledb_config == { @@ -66,8 +53,7 @@ def test_shared_ctx(): """Verifies that one global context is shared by default.""" ctx = stc.SOMATileDBContext() ctx_2 = stc.SOMATileDBContext() - with pytest.deprecated_call(): - assert ctx.tiledb_ctx is ctx_2.tiledb_ctx + assert ctx.native_context is ctx_2.native_context def test_unshared_ctx(): @@ -75,9 +61,8 @@ def test_unshared_ctx(): ctx = stc.SOMATileDBContext() ctx_2 = stc.SOMATileDBContext(tiledb_config={}) ctx_3 = stc.SOMATileDBContext(tiledb_config={}) - with pytest.deprecated_call(): - assert ctx.tiledb_ctx is not ctx_2.tiledb_ctx - assert ctx_2.tiledb_ctx is not ctx_3.tiledb_ctx + assert ctx.native_context is not ctx_2.native_context + assert ctx_2.native_context is not ctx_3.native_context def test_replace_timestamp(): @@ -95,16 +80,6 @@ def test_replace_timestamp(): assert no_ts_ctx.timestamp is None -def test_replace_context(): - with pytest.deprecated_call(): - orig_ctx = stc.SOMATileDBContext(tiledb_ctx=tiledb.Ctx()) - new_tdb_ctx = tiledb.Ctx({"vfs.s3.region": "hy-central-1"}) - with pytest.deprecated_call(): - new_ctx = orig_ctx.replace(tiledb_ctx=new_tdb_ctx) - with pytest.deprecated_call(): - assert new_ctx.tiledb_ctx is new_tdb_ctx - - def test_replace_config_after_construction(): context = stc.SOMATileDBContext() @@ -123,13 +98,12 @@ def test_replace_config_after_construction(): assert context_ts_1._open_timestamp_ms(None) == 1 assert context_ts_1._open_timestamp_ms(2) == 2 - with mock.patch.object(tiledb, "Ctx", wraps=tiledb.Ctx) as mock_ctx: + with mock.patch.object(clib, "SOMAContext", wraps=clib.SOMAContext) as mock_ctx: # verify that the new context is lazily initialized. new_soma_ctx = context.replace(tiledb_config={"vfs.s3.region": "us-west-2"}) assert new_soma_ctx.tiledb_config["vfs.s3.region"] == "us-west-2" mock_ctx.assert_not_called() - with pytest.deprecated_call(): - new_tdb_ctx = new_soma_ctx.tiledb_ctx + new_tdb_ctx = new_soma_ctx.native_context mock_ctx.assert_called_once() assert new_tdb_ctx.config()["vfs.s3.region"] == "us-west-2" diff --git a/apis/python/tests/test_dense_nd_array.py b/apis/python/tests/test_dense_nd_array.py index 47feb19c38..a3c0fb9de3 100644 --- a/apis/python/tests/test_dense_nd_array.py +++ b/apis/python/tests/test_dense_nd_array.py @@ -275,7 +275,7 @@ def test_dense_nd_array_slicing(tmp_path, io): cfg = {} if "cfg" in io: cfg = io["cfg"] - context = SOMATileDBContext(tiledb_ctx=tiledb.Ctx(cfg)) + context = SOMATileDBContext(tiledb_config=cfg) nr = 4 nc = 6 diff --git a/libtiledbsoma/src/utils/arrow_adapter.h b/libtiledbsoma/src/utils/arrow_adapter.h index 566d1f6507..68ef3758f9 100644 --- a/libtiledbsoma/src/utils/arrow_adapter.h +++ b/libtiledbsoma/src/utils/arrow_adapter.h @@ -690,6 +690,8 @@ class ArrowAdapter { return Dimension::create(*ctx, name, {b[0], b[1]}, b[2]); } + static bool _isvar(const char* format); + static FilterList _create_filter_list( std::string filters, std::shared_ptr ctx);