Repository: TileDB-Inc/TileDB-Py
Pull request #2217 (2025-07-17 01:22:47)

Ensure that the encryption key is propagated correctly when opening an array
Attempting to read an encrypted array by passing the `key` parameter to `array_open` wasn't propagating the `key` to the Core. This was because `preload_array` only looked for the `key` parameter when no `Context` was passed to it. Since `preload_array` is an internal function and `Context` is always passed, this PR removes the case where `Context` is not present, thereby fixing the issue.

Closes CORE-243


Patch:
diff --git a/pyproject.toml b/pyproject.toml
index 62c8bb0d..f5cac430 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,7 +1,7 @@
 [build-system]
 requires = [
     "scikit-build-core",
-    "pybind11",
+    "pybind11<3",
     "numpy>=2.0.1"
 ]
 build-backend = "scikit_build_core.build"
diff --git a/tiledb/array.py b/tiledb/array.py
index 6e3aee6e..237bc762 100644
--- a/tiledb/array.py
+++ b/tiledb/array.py
@@ -298,15 +298,12 @@ def index_domain_subarray(array, dom, idx: tuple):
 
 
 # this function loads the pybind Array to determine whether it is a sparse or dense array.
-def preload_array(uri, mode, key, timestamp, ctx=None):
-    if ctx is None:
-        if key is not None:
-            config = tiledb.Config()
-            config["sm.encryption_key"] = key
-            config["sm.encryption_type"] = "AES_256_GCM"
-            ctx = tiledb.Ctx(config=config)
-        else:
-            ctx = default_ctx()
+def preload_array(uri, mode, key, timestamp, ctx):
+    if key is not None:
+        config = ctx.config()
+        config["sm.encryption_key"] = key
+        config["sm.encryption_type"] = "AES_256_GCM"
+        ctx = tiledb.Ctx(config=config)
 
     _mode_to_query_type = {
         "r": lt.QueryType.READ,


Test patch:
diff --git a/tiledb/tests/test_libtiledb.py b/tiledb/tests/test_libtiledb.py
index 8ea74871..efa1402c 100644
--- a/tiledb/tests/test_libtiledb.py
+++ b/tiledb/tests/test_libtiledb.py
@@ -190,14 +190,28 @@ class ArrayTest(DiskTestCase):
         config["sm.encryption_type"] = "AES_256_GCM"
         ctx = tiledb.Ctx(config=config)
 
-        with tiledb.Array(self.path("foo"), mode="r", ctx=ctx) as array:
+        # Open with context only
+        with tiledb.open(self.path("foo"), mode="r", ctx=ctx) as array:
             self.assertTrue(array.isopen)
             self.assertEqual(array.schema, schema)
             self.assertEqual(array.mode, "r")
+        # Open with both key-configured context and key
         with tiledb.open(self.path("foo"), mode="r", key=key, ctx=ctx) as array:
             self.assertTrue(array.isopen)
             self.assertEqual(array.schema, schema)
             self.assertEqual(array.mode, "r")
+        # Open with empty context and key
+        with tiledb.open(
+            self.path("foo"), mode="r", ctx=tiledb.Ctx(), key=key
+        ) as array:
+            self.assertTrue(array.isopen)
+            self.assertEqual(array.schema, schema)
+            self.assertEqual(array.mode, "r")
+        # Open with key only
+        with tiledb.open(self.path("foo"), mode="r", key=key) as array:
+            self.assertTrue(array.isopen)
+            self.assertEqual(array.schema, schema)
+            self.assertEqual(array.mode, "r")
 
         tiledb.consolidate(uri=self.path("foo"), ctx=tiledb.Ctx(config))