Repository: TileDB-Inc/TileDB-Py
Pull request #2052 (2024-08-28 10:44:01)

Fix serialization test and add command line option
A problem identified even before `scikit-build-core` was introduced was that the serialization test was always skipped, even after setting it using the old/deprecated way: `python setup.py -DTILEDB_SERIALIZATION=ON`.

Additionally, elements like `tiledb.main.test_serialization.create_serialized_test_query` or `tiledb.main.TILEDB_CAPNP` were also undefined.

It turns out that the way the `test_serialization` import was handled was incorrect:

```
>>> import tiledb.main.test_serialization
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named 'tiledb.main.test_serialization'; 'tiledb.main' is not a package
```
but we were silently receiving only the `Serialization not enabled.` message

Here is the correct way:
```
>>> from tiledb.main import test_serialization
```

This PR:
- adds flag for serialization: `pip install . -Cskbuild.cmake.define.TILEDB_SERIALIZATION=ON` and `python setup.py install --enable-serialization` [**deprecated!**]

- fixes import error of `tiledb/tests/test_serialization.py`

---

[sc-49775]


Patch:
diff --git a/pyproject.toml b/pyproject.toml
index ae25be63..b0b086b9 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -83,6 +83,7 @@ TILEDB_PATH = {env="TILEDB_PATH"}
 TILEDB_VERSION = {env="TILEDB_VERSION"}
 TILEDB_HASH = {env="TILEDB_HASH"}
 TILEDB_REMOVE_DEPRECATIONS = "ON"
+TILEDB_SERIALIZATION = "OFF"
 
 [tool.pytest.ini_options]
 python_classes = "*Test*"
diff --git a/setup.py b/setup.py
index 4e67a292..37ee5cb4 100644
--- a/setup.py
+++ b/setup.py
@@ -9,6 +9,7 @@ def main():
     parser.add_argument("--tiledb", type=str, required=False)
     parser.add_argument("--debug", action="store_true")
     parser.add_argument("--enable-deprecations", action="store_true", required=False)
+    parser.add_argument("--enable-serialization", action="store_true", required=False)
     parser.add_argument("-v", action="store_true")
     args = parser.parse_args()
 
@@ -33,6 +34,9 @@ def main():
     if args.enable_deprecations:
         cmd.append(f"-Cskbuild.cmake.define.TILEDB_REMOVE_DEPRECATIONS=OFF")
 
+    if args.enable_serialization:
+        cmd.append(f"-Cskbuild.cmake.define.TILEDB_SERIALIZATION=ON")
+
     if args.v:
         cmd.append("-v")
 
diff --git a/tiledb/CMakeLists.txt b/tiledb/CMakeLists.txt
index 98d82e7c..3f9dc05a 100644
--- a/tiledb/CMakeLists.txt
+++ b/tiledb/CMakeLists.txt
@@ -56,6 +56,14 @@ target_compile_features(
     cxx_std_20
 )
 
+if (TILEDB_SERIALIZATION)
+    target_compile_definitions(
+        main
+        PRIVATE
+        TILEDB_SERIALIZATION
+    )
+endif()
+
 install(TARGETS main libtiledb DESTINATION tiledb)
 
 if(TILEDB_DOWNLOADED)
diff --git a/tiledb/__init__.py b/tiledb/__init__.py
index 8b430cc4..8ba220bc 100644
--- a/tiledb/__init__.py
+++ b/tiledb/__init__.py
@@ -65,7 +65,6 @@ from .filter import (
 from .fragment import (
     FragmentInfo,
     FragmentInfoList,
-    FragmentsInfo,
     copy_fragments_to_existing_array,
     create_array_from_fragments,
 )
diff --git a/tiledb/cc/CMakeLists.txt b/tiledb/cc/CMakeLists.txt
index af4d7fe6..ced8acaf 100644
--- a/tiledb/cc/CMakeLists.txt
+++ b/tiledb/cc/CMakeLists.txt
@@ -42,6 +42,14 @@ if (TILEDB_REMOVE_DEPRECATIONS)
     )
 endif()
 
+if (TILEDB_SERIALIZATION)
+    target_compile_definitions(
+        cc
+        PRIVATE
+        TILEDB_SERIALIZATION
+    )
+endif()
+
 install(TARGETS cc DESTINATION tiledb)
 
 if(TILEDB_DOWNLOADED)
diff --git a/tiledb/dataframe_.py b/tiledb/dataframe_.py
index 9543d91a..489bb10f 100644
--- a/tiledb/dataframe_.py
+++ b/tiledb/dataframe_.py
@@ -420,7 +420,9 @@ def _sparse_from_dtypes(dtypes, sparse=None):
     return sparse if sparse is not None else False
 
 
-def create_dims(df, index_dims, tile=None, full_domain=False, filters=None):
+def create_dims(
+    df, index_dims, column_infos, tile=None, full_domain=False, filters=None
+):
     check_dataframe_deps()
     import pandas as pd
 
@@ -445,7 +447,10 @@ def create_dims(df, index_dims, tile=None, full_domain=False, filters=None):
         else:
             raise ValueError(f"Unknown column or index named {name!r}")
 
-        dtype = ColumnInfo.from_values(values).dtype
+        if name in column_infos:
+            dtype = column_infos[name].dtype
+        else:
+            dtype = ColumnInfo.from_values(values).dtype
         internal_dtype = dtype
 
         if name == "__tiledb_rows" and isinstance(index, pd.RangeIndex):
@@ -659,6 +664,7 @@ def _create_array(uri, df, sparse, full_domain, index_dims, column_infos, tiledb
     dims, dim_metadata = create_dims(
         df,
         index_dims,
+        column_infos,
         full_domain=full_domain,
         tile=tiledb_args.get("tile"),
         filters=tiledb_args.get("dim_filters", True),
diff --git a/tiledb/filestore.py b/tiledb/filestore.py
index ced85614..ffe0de76 100644
--- a/tiledb/filestore.py
+++ b/tiledb/filestore.py
@@ -142,29 +142,6 @@ class Filestore:
 
         lt.Filestore._uri_export(ctx, filestore_array_uri, file_uri)
 
-    def uri_import(self, file_uri: str, mime_type: str = "AUTODETECT") -> None:
-        warnings.warn(
-            "Filestore.uri_import is deprecated; please use Filestore.copy_from",
-            DeprecationWarning,
-        )
-
-        if not isinstance(file_uri, str):
-            raise TypeError(
-                f"Unexpected file_uri type '{type(file_uri)}': expected str"
-            )
-
-        if not isinstance(mime_type, str):
-            raise TypeError(
-                f"Unexpected mime_type type '{type(mime_type)}': expected str"
-            )
-
-        lt.Filestore._uri_import(
-            self._ctx,
-            self._filestore_uri,
-            file_uri,
-            lt.Filestore._mime_type_from_str(mime_type),
-        )
-
     def __len__(self) -> int:
         """
         :rtype: int
diff --git a/tiledb/fragment.py b/tiledb/fragment.py
index db9ae0cd..04be4021 100644
--- a/tiledb/fragment.py
+++ b/tiledb/fragment.py
@@ -131,38 +131,6 @@ class FragmentInfoList:
         if libtiledb_version() >= (2, 5, 0):
             self.array_schema_name = fi.get_array_schema_name()
 
-    @property
-    def non_empty_domain(self):
-        raise tiledb.TileDBError(
-            "FragmentInfoList.non_empty_domain is deprecated; "
-            "you must use FragmentInfoList.nonempty_domain",
-            "This message will be removed in 0.21.0.",
-        )
-
-    @property
-    def to_vacuum_num(self):
-        raise tiledb.TileDBError(
-            "FragmentInfoList.to_vacuum_num is deprecated; "
-            "you must use len(FragmentInfoList.to_vacuum)",
-            "This message will be removed in 0.21.0.",
-        )
-
-    @property
-    def to_vacuum_uri(self):
-        raise tiledb.TileDBError(
-            "FragmentInfoList.to_vacuum_uri is deprecated; "
-            "you must use FragmentInfoList.to_vacuum",
-            "This message will be removed in 0.21.0.",
-        )
-
-    @property
-    def dense(self):
-        raise tiledb.TileDBError(
-            "FragmentInfoList.dense is deprecated; "
-            "you must use FragmentInfoList.sparse",
-            "This message will be removed in 0.21.0.",
-        )
-
     def __getattr__(self, name):
         if name == "mbrs":
             raise AttributeError(
@@ -297,50 +265,6 @@ class FragmentInfo:
             )
         return self.__getattribute__(name)
 
-    @property
-    def non_empty_domain(self):
-        raise tiledb.TileDBError(
-            "FragmentInfo.non_empty_domain is deprecated; "
-            "you must use FragmentInfo.nonempty_domain. ",
-            "This message will be removed in 0.21.0.",
-        )
-
-    @property
-    def to_vacuum_num(self):
-        raise tiledb.TileDBError(
-            "FragmentInfo.to_vacuum_num is deprecated; "
-            "you must use len(FragmentInfoList.to_vacuum).",
-            "This message will be removed in 0.21.0.",
-        )
-
-    @property
-    def to_vacuum_uri(self):
-        raise tiledb.TileDBError(
-            "FragmentInfo.to_vacuum_uri is deprecated; "
-            "you must use FragmentInfoList.to_vacuum.",
-            "This message will be removed in 0.21.0.",
-        )
-
-    @property
-    def dense(self):
-        raise tiledb.TileDBError(
-            "FragmentInfo.dense is deprecated; you must use FragmentInfo.sparse",
-            "This message will be removed in 0.21.0.",
-        )
-
-
-def FragmentsInfo(array_uri, ctx=None):
-    """
-    Deprecated in 0.8.8.
-
-    Renamed to FragmentInfoList to make name more distinguishable from FragmentInfo.
-    """
-
-    raise tiledb.TileDBError(
-        "FragmentsInfo is deprecated; you must use FragmentInfoList. "
-        "This message will be removed in 0.21.0.",
-    )
-
 
 def create_array_from_fragments(
     src_uri,
diff --git a/tiledb/libtiledb.pyx b/tiledb/libtiledb.pyx
index 0a20b112..f23670ff 100644
--- a/tiledb/libtiledb.pyx
+++ b/tiledb/libtiledb.pyx
@@ -1188,17 +1188,6 @@ cdef class Array(object):
         """The view attribute of this array."""
         return self.view_attr
 
-    @property
-    def timestamp(self):
-        """Deprecated in 0.9.2.
-
-        Use `timestamp_range`
-        """
-        raise TileDBError(
-            "timestamp is deprecated; you must use timestamp_range. "
-            "This message will be removed in 0.21.0.",
-        )
-
     @property
     def timestamp_range(self):
         """Returns the timestamp range the array is opened at
@@ -1223,16 +1212,6 @@ cdef class Array(object):
 
         return (int(timestamp_start), int(timestamp_end))
 
-    @property
-    def coords_dtype(self):
-        """
-        Deprecated in 0.8.10
-        """
-        raise TileDBError(
-            "`coords_dtype` is deprecated because combined coords have been "
-            "removed from libtiledb. This message will be removed in 0.21.0.",
-        )
-
     @property
     def uri(self):
         """Returns the URI of the array"""
@@ -1913,14 +1892,6 @@ cdef class Aggregation(object):
 
             if isinstance(cond, str):
                 q.set_cond(QueryCondition(cond))
-            elif isinstance(cond, QueryCondition):
-                raise TileDBError(
-                    "Passing `tiledb.QueryCondition` to `cond` is no longer "
-                    "supported as of 0.19.0. Instead of "
-                    "`cond=tiledb.QueryCondition('expression')` "
-                    "you must use `cond='expression'`. This message will be "
-                    "removed in 0.21.0.",
-                )
             else:
                 raise TypeError("`cond` expects type str.")        
 
@@ -2103,13 +2074,6 @@ cdef class Query(object):
         """List of attributes to include in Query."""
         return self.attrs
 
-    @property
-    def attr_cond(self):
-        raise TileDBError(
-            "`attr_cond` is no longer supported. You must use `cond`. "
-            "This message will be removed in 0.21.0."
-        )
-
     @property
     def cond(self):
         """QueryCondition used to filter attributes or dimensions in Query."""
@@ -2288,9 +2252,8 @@ cdef class DenseArrayImpl(Array):
         else:
             return "DenseArray(uri={0!r}, mode=closed)".format(self.uri)
 
-    def query(self, attrs=None, attr_cond=None, cond=None, dims=None,
-              coords=False, order='C', use_arrow=None, return_arrow=False,
-              return_incomplete=False):
+    def query(self, attrs=None, cond=None, dims=None, coords=False, order='C',
+              use_arrow=None, return_arrow=False, return_incomplete=False):
         """Construct a proxy Query object for easy subarray queries of cells
         for an item or region of the array across one or more attributes.
 
@@ -2341,24 +2304,12 @@ cdef class DenseArrayImpl(Array):
         if not self.isopen or self.mode != 'r':
             raise TileDBError("DenseArray must be opened in read mode")
 
-        if attr_cond is not None:
-            if cond is not None:
-                raise TileDBError("Both `attr_cond` and `cond` were passed. "
-                    "Only use `cond`."
-                )
-
-            raise TileDBError(
-                "`attr_cond` is no longer supported. You must use `cond`. "
-                "This message will be removed in 0.21.0."
-            )
-
         return Query(self, attrs=attrs, cond=cond, dims=dims,
                       coords=coords, order=order,
                       use_arrow=use_arrow, return_arrow=return_arrow,
                       return_incomplete=return_incomplete)
 
-    def subarray(self, selection, attrs=None, cond=None, attr_cond=None,
-                 coords=False, order=None):
+    def subarray(self, selection, attrs=None, cond=None, coords=False, order=None):
         """Retrieve data cells for an item or region of the array.
 
         Optionally subselect over attributes, return dense result coordinate values,
@@ -2398,12 +2349,6 @@ cdef class DenseArrayImpl(Array):
         if not self.isopen or self.mode != 'r':
             raise TileDBError("DenseArray must be opened in read mode")
 
-        if attr_cond is not None:
-            if cond is not None:
-                raise TileDBError("Both `attr_cond` and `cond` were passed. "
-                    "Only use `cond`."
-                )
-
         cdef tiledb_layout_t layout = TILEDB_UNORDERED
         if order is None or order == 'C':
             layout = TILEDB_ROW_MAJOR
@@ -2475,14 +2420,6 @@ cdef class DenseArrayImpl(Array):
 
             if isinstance(cond, str):
                 q.set_cond(QueryCondition(cond))
-            elif isinstance(cond, QueryCondition):
-                raise TileDBError(
-                    "Passing `tiledb.QueryCondition` to `cond` is no longer "
-                    "supported as of 0.19.0. Instead of "
-                    "`cond=tiledb.QueryCondition('expression')` "
-                    "you must use `cond='expression'`. This message will be "
-                    "removed in 0.21.0.",
-                )
             else:
                 raise TypeError("`cond` expects type str.")
 
@@ -3362,9 +3299,8 @@ cdef class SparseArrayImpl(Array):
 
         return result
 
-    def query(self, attrs=None, cond=None, attr_cond=None, dims=None,
-              index_col=True, coords=None, order='U', use_arrow=None,
-              return_arrow=None, return_incomplete=False):
+    def query(self, attrs=None, cond=None, dims=None, index_col=True, coords=None,
+              order='U', use_arrow=None, return_arrow=None, return_incomplete=False):
         """
         Construct a proxy Query object for easy subarray queries of cells
         for an item or region of the array across one or more attributes.
@@ -3415,17 +3351,6 @@ cdef class SparseArrayImpl(Array):
         if not self.isopen or self.mode not in  ('r', 'd'):
             raise TileDBError("SparseArray must be opened in read or delete mode")
 
-        if attr_cond is not None:
-            if cond is not None:
-                raise TileDBError("Both `attr_cond` and `cond` were passed. "
-                    "Only use `cond`."
-                )
-
-            raise TileDBError(
-                "`attr_cond` is no longer supported. You must use `cond`. "
-                "This message will be removed in 0.21.0."
-            )
-
         # backwards compatibility
         _coords = coords
         if dims is False:
@@ -3480,8 +3405,7 @@ cdef class SparseArrayImpl(Array):
         return result_dict
 
 
-    def subarray(self, selection, coords=True, attrs=None, cond=None,
-                 attr_cond=None, order=None):
+    def subarray(self, selection, coords=True, attrs=None, cond=None, order=None):
         """
         Retrieve dimension and data cells for an item or region of the array.
 
@@ -3529,17 +3453,6 @@ cdef class SparseArrayImpl(Array):
         if not self.isopen or self.mode not in ('r', 'd'):
             raise TileDBError("SparseArray is not opened in read or delete mode")
 
-        if attr_cond is not None:
-            if cond is not None:
-                raise TileDBError("Both `attr_cond` and `cond` were passed. "
-                    "`attr_cond` is no longer supported. You must use `cond`. "
-                )
-
-            raise TileDBError(
-                "`attr_cond` is no longer supported. You must use `cond`. "
-                "This message will be removed in 0.21.0.",
-            )
-
         cdef tiledb_layout_t layout = TILEDB_UNORDERED
         if order is None or order == 'U':
             layout = TILEDB_UNORDERED
@@ -3607,14 +3520,6 @@ cdef class SparseArrayImpl(Array):
 
             if isinstance(cond, str):
                 q.set_cond(QueryCondition(cond))
-            elif isinstance(cond, QueryCondition):
-                raise TileDBError(
-                    "Passing `tiledb.QueryCondition` to `cond` is no longer "
-                    "supported as of 0.19.0. Instead of "
-                    "`cond=tiledb.QueryCondition('expression')` "
-                    "you must use `cond='expression'`. This message will be "
-                    "removed in 0.21.0.",
-                )
             else:
                 raise TypeError("`cond` expects type str.")
 
diff --git a/tiledb/multirange_indexing.py b/tiledb/multirange_indexing.py
index dbbe3278..27e5725f 100644
--- a/tiledb/multirange_indexing.py
+++ b/tiledb/multirange_indexing.py
@@ -649,13 +649,6 @@ def _get_pyquery(
     if query and query.cond is not None:
         if isinstance(query.cond, str):
             pyquery.set_cond(QueryCondition(query.cond))
-        elif isinstance(query.cond, QueryCondition):
-            raise TileDBError(
-                "Passing `tiledb.QueryCondition` to `cond` is no longer supported "
-                "as of 0.19.0. Instead of `cond=tiledb.QueryCondition('expression')` "
-                "you must use `cond='expression'`. This message will be "
-                "removed in 0.21.0.",
-            )
         else:
             raise TypeError("`cond` expects type str.")
 
diff --git a/tiledb/query_condition.py b/tiledb/query_condition.py
index 39e722f2..baf4c9f0 100644
--- a/tiledb/query_condition.py
+++ b/tiledb/query_condition.py
@@ -321,11 +321,6 @@ class QueryConditionTree(ast.NodeVisitor):
             variable = variable_node.id
         elif isinstance(variable_node, ast.Constant):
             variable = variable_node.value
-        elif isinstance(variable_node, ast.Constant) or isinstance(
-            variable_node, ast.Constant
-        ):
-            # deprecated in 3.8
-            variable = variable_node.s
         else:
             raise TileDBError(
                 f"Incorrect type for variable name: {ast.dump(variable_node)}"
@@ -492,19 +487,3 @@ class QueryConditionTree(ast.NodeVisitor):
                 )
 
             return node.operand
-
-    def visit_Num(self, node: ast.Constant) -> ast.Constant:
-        # deprecated in 3.8
-        return node
-
-    def visit_Str(self, node: ast.Constant) -> ast.Constant:
-        # deprecated in 3.8
-        return node
-
-    def visit_Bytes(self, node: ast.Constant) -> ast.Constant:
-        # deprecated in 3.8
-        return node
-
-    def visit_NameConstant(self, node: ast.Constant) -> ast.Constant:
-        # deprecated in 3.8
-        return node
diff --git a/tiledb/vfs.py b/tiledb/vfs.py
index d95c4d52..0cdc745d 100644
--- a/tiledb/vfs.py
+++ b/tiledb/vfs.py
@@ -111,15 +111,7 @@ class VFS(lt.VFS):
         :rtype: :py:func:`bytes`
         :return: read bytes
         :raises: :py:exc:`tiledb.TileDBError`
-
         """
-        if isinstance(file, FileIO):
-            raise lt.TileDBError(
-                "`tiledb.VFS().open` now returns a FileIO object. Use "
-                "`FileIO.seek` and `FileIO.read`. This message will be removed "
-                "in 0.21.0."
-            )
-
         if nbytes == 0:
             return b""
 


Test patch:
diff --git a/tiledb/tests/test_filestore.py b/tiledb/tests/test_filestore.py
index bde45452..9d352ff2 100644
--- a/tiledb/tests/test_filestore.py
+++ b/tiledb/tests/test_filestore.py
@@ -74,22 +74,6 @@ class FilestoreTest(DiskTestCase):
             assert data == fs.read(0, len(data))
             assert len(fs) == len(data)
 
-    def test_deprecated_uri(self, text_fname):
-        path = self.path("test_uri")
-        schema = tiledb.ArraySchema.from_file(text_fname)
-        tiledb.Array.create(path, schema)
-
-        fs = tiledb.Filestore(path)
-        with pytest.warns(
-            DeprecationWarning, match="Filestore.uri_import is deprecated"
-        ):
-            fs.uri_import(text_fname)
-
-        with open(text_fname, "rb") as text:
-            data = text.read()
-            assert data == fs.read(0, len(data))
-            assert len(fs) == len(data)
-
     def test_multiple_writes(self):
         path = self.path("test_buffer")
         schema = tiledb.ArraySchema.from_file()
diff --git a/tiledb/tests/test_pandas_dataframe.py b/tiledb/tests/test_pandas_dataframe.py
index 51c02d74..9811144c 100644
--- a/tiledb/tests/test_pandas_dataframe.py
+++ b/tiledb/tests/test_pandas_dataframe.py
@@ -127,7 +127,7 @@ def make_dataframe_categorical():
     return df
 
 
-class TestColumnInfo:
+class TestColumnInfo(DiskTestCase):
     def assertColumnInfo(self, info, info_dtype, info_repr=None, info_nullable=False):
         assert isinstance(info.dtype, np.dtype)
         assert info.dtype == info_dtype
@@ -250,6 +250,96 @@ class TestColumnInfo:
                     # check that the column name is included in the error message
                     assert "supported (column foo)" in str(exc.value)
 
+    def test_apply_dtype_index_ingest(self):
+        uri = self.path("index_dtype_default_dtype")
+        tiledb.from_pandas(
+            uri,
+            pd.DataFrame({"a": np.arange(0, 20), "b": np.arange(20, 40)}),
+            sparse=True,
+            index_dims=["a"],
+        )
+        with tiledb.open(uri) as A:
+            if sys.platform == "win32" and sys.version_info[:2] == (3, 8):
+                assert A.schema.domain.dim(0).dtype == np.int32
+            else:
+                assert A.schema.domain.dim(0).dtype == np.int64
+
+        uri = self.path("index_dtype_casted_dtype")
+        tiledb.from_pandas(
+            uri,
+            pd.DataFrame({"a": np.arange(0, 20), "b": np.arange(20, 40)}),
+            sparse=True,
+            index_dims=["a"],
+            column_types={"a": np.uint8},
+        )
+        with tiledb.open(uri) as A:
+            assert A.schema.domain.dim(0).dtype == np.uint8
+
+        # multiple index dims
+        uri = self.path("index_dtype_default_dtype_multi")
+        tiledb.from_pandas(
+            uri,
+            pd.DataFrame(
+                {
+                    "a": np.random.random_sample(20),
+                    "b": [str(uuid.uuid4()) for _ in range(20)],
+                }
+            ),
+            sparse=True,
+            index_dims=["a", "b"],
+        )
+        with tiledb.open(uri) as A:
+            assert A.schema.domain.dim(0).dtype == np.float64
+            assert A.schema.domain.dim(1).dtype == np.bytes_
+
+        uri = self.path("index_dtype_casted_dtype_multi")
+        tiledb.from_pandas(
+            uri,
+            pd.DataFrame(
+                {
+                    "a": np.random.random_sample(20),
+                    "b": [str(uuid.uuid4()) for _ in range(20)],
+                }
+            ),
+            sparse=True,
+            index_dims=["a", "b"],
+            column_types={"a": np.float32, "b": np.bytes_},
+        )
+        with tiledb.open(uri) as A:
+            assert A.schema.domain.dim(0).dtype == np.float32
+            assert A.schema.domain.dim(1).dtype == np.bytes_
+
+    def test_apply_dtype_index_schema_only(self):
+        uri = self.path("index_dtype_casted_dtype")
+        tiledb.from_pandas(
+            uri,
+            pd.DataFrame({"a": np.arange(0, 20), "b": np.arange(20, 40)}),
+            sparse=True,
+            index_dims=["a"],
+            column_types={"a": np.uint8},
+            mode="schema_only",
+        )
+        with tiledb.open(uri) as A:
+            assert A.schema.domain.dim(0).dtype == np.uint8
+
+        uri = self.path("index_dtype_casted_dtype_multi")
+        tiledb.from_pandas(
+            uri,
+            pd.DataFrame(
+                {
+                    "a": np.random.random_sample(20),
+                    "b": [str(uuid.uuid4()) for _ in range(20)],
+                }
+            ),
+            sparse=True,
+            index_dims=["a", "b"],
+            column_types={"a": np.float32, "b": np.bytes_},
+            mode="schema_only",
+        )
+        with tiledb.open(uri) as A:
+            assert A.schema.domain.dim(0).dtype == np.float32
+            assert A.schema.domain.dim(1).dtype == np.bytes_
+
 
 class TestDimType:
     @pytest.mark.parametrize(
@@ -392,10 +482,6 @@ class TestPandasDataFrameRoundtrip(DiskTestCase):
         with tiledb.open(uri) as B:
             tm.assert_frame_equal(df, B.df[:])
 
-    @pytest.mark.skipif(
-        sys.version_info < (3, 8),
-        reason="requires Python 3.8 or higher. date_format argument is not supported in 3.7 and below",
-    )
     def test_dataframe_csv_rt1(self):
         def rand_dtype(dtype, size):
             nbytes = size * np.dtype(dtype).itemsize
@@ -703,10 +789,6 @@ class TestPandasDataFrameRoundtrip(DiskTestCase):
         tmp_array2 = os.path.join(tmp_dir, "array2")
         tiledb.from_csv(tmp_array2, tmp_csv, sparse=False)
 
-    @pytest.mark.skipif(
-        sys.version_info < (3, 8),
-        reason="requires Python 3.8 or higher. date_format argument is not supported in 3.7 and below",
-    )
     def test_csv_col_to_sparse_dims(self):
         df = make_dataframe_basic3(20)
 
@@ -780,10 +862,6 @@ class TestPandasDataFrameRoundtrip(DiskTestCase):
             cmp_df = df.set_index("int_vals").sort_values(by="time")
             tm.assert_frame_equal(res_df, cmp_df)
 
-    @pytest.mark.skipif(
-        sys.version_info < (3, 8),
-        reason="requires Python 3.8 or higher. date_format argument is not supported in 3.7 and below",
-    )
     def test_dataframe_csv_schema_only(self):
         col_size = 10
         df = make_dataframe_basic3(col_size)
@@ -893,10 +971,6 @@ class TestPandasDataFrameRoundtrip(DiskTestCase):
             df_bk.sort_index(level="time", inplace=True)
             tm.assert_frame_equal(df_bk, df_combined)
 
-    @pytest.mark.skipif(
-        sys.version_info < (3, 8),
-        reason="requires Python 3.8 or higher. date_format argument is not supported in 3.7 and below",
-    )
     def test_dataframe_csv_chunked(self):
         col_size = 200
         df = make_dataframe_basic3(col_size)
@@ -979,10 +1053,6 @@ class TestPandasDataFrameRoundtrip(DiskTestCase):
             df_idx_res = A.query(coords=False).df[int(ned[0]) : int(ned[1])]
             tm.assert_frame_equal(df_idx_res, df.reset_index(drop=True))
 
-    @pytest.mark.skipif(
-        sys.version_info < (3, 8),
-        reason="requires Python 3.8 or higher. date_format argument is not supported in 3.7 and below",
-    )
     def test_csv_fillna(self):
         if pytest.tiledb_vfs == "s3":
             pytest.skip(
diff --git a/tiledb/tests/test_query_condition.py b/tiledb/tests/test_query_condition.py
index 07549482..a3c3f157 100644
--- a/tiledb/tests/test_query_condition.py
+++ b/tiledb/tests/test_query_condition.py
@@ -119,12 +119,7 @@ class QueryConditionTest(DiskTestCase):
 
     def test_unsigned_sparse(self):
         with tiledb.open(self.create_input_array_UIDSA(sparse=True)) as A:
-            with pytest.raises(tiledb.TileDBError) as exc_info:
-                A.query(cond=tiledb.QueryCondition("U < 5"), attrs=["U"])[:]
-            assert (
-                "Passing `tiledb.QueryCondition` to `cond` is no longer supported"
-                in str(exc_info.value)
-            )
+            A.query(cond="U < 5", attrs=["U"])[:]
 
             result = A.query(cond="U < 5", attrs=["U"])[:]
             assert all(result["U"] < 5)
@@ -133,12 +128,7 @@ class QueryConditionTest(DiskTestCase):
         with tiledb.open(self.create_input_array_UIDSA(sparse=False)) as A:
             mask = A.attr("U").fill
 
-            with pytest.raises(tiledb.TileDBError) as exc_info:
-                A.query(cond=tiledb.QueryCondition("U < 5"), attrs=["U"])[:]
-            assert (
-                "Passing `tiledb.QueryCondition` to `cond` is no longer supported"
-                in str(exc_info.value)
-            )
+            A.query(cond="U < 5", attrs=["U"])[:]
 
             result = A.query(cond="U < 5", attrs=["U"])[:]
             assert all(self.filter_dense(result["U"], mask) < 5)
@@ -699,25 +689,9 @@ class QueryConditionTest(DiskTestCase):
         with tiledb.open(self.create_input_array_UIDSA(sparse=True)) as A:
             qc = "U < 3"
 
-            with pytest.raises(tiledb.TileDBError) as exc_info:
-                A.query(cond=qc, attr_cond=qc)
-            assert "Both `attr_cond` and `cond` were passed." in str(exc_info.value)
-
-            with pytest.raises(tiledb.TileDBError) as exc_info:
-                A.query(attr_cond=qc)
-            assert "`attr_cond` is no longer supported" in str(exc_info.value)
-
-            with pytest.raises(tiledb.TileDBError) as exc_info:
-                A.query(cond=qc).attr_cond
-            assert "`attr_cond` is no longer supported" in str(exc_info.value)
-
-            with pytest.raises(tiledb.TileDBError) as exc_info:
-                A.subarray(1, cond=qc, attr_cond=qc)
-            assert "Both `attr_cond` and `cond` were passed." in str(exc_info.value)
-
-            with pytest.raises(tiledb.TileDBError) as exc_info:
-                A.subarray(1, attr_cond=qc)
-            assert "`attr_cond` is no longer supported" in str(exc_info.value)
+            A.query(cond=qc)
+            A.query(cond=qc).cond
+            A.subarray(1, cond=qc)
 
     def test_on_dense_dimensions(self):
         with tiledb.open(self.create_input_array_UIDSA(sparse=False)) as A:
diff --git a/tiledb/tests/test_serialization.py b/tiledb/tests/test_serialization.py
index 1c5778fe..75529904 100644
--- a/tiledb/tests/test_serialization.py
+++ b/tiledb/tests/test_serialization.py
@@ -5,9 +5,10 @@ import tiledb
 
 from .common import DiskTestCase
 
-ser_test = pytest.importorskip(
-    "tiledb.main.test_serialization", reason="Serialization not enabled."
-)
+try:
+    from tiledb.main import test_serialization as ser_test
+except ImportError:
+    pytest.skip("Serialization not enabled.", allow_module_level=True)
 
 
 class SerializationTest(DiskTestCase):