This is an automated email from the ASF dual-hosted git repository.

lidavidm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-adbc.git


The following commit(s) were added to refs/heads/main by this push:
     new f1d6412b8 fix(python/adbc_driver_manager): allow more key types in 
get_option (#4734)
f1d6412b8 is described below

commit f1d6412b809784a882ad1c971018e4401c91aecd
Author: Fredrik Fornwall <[email protected]>
AuthorDate: Wed Sep 2 07:17:19 2026 +0200

    fix(python/adbc_driver_manager): allow more key types in get_option (#4734)
    
    Cython treats a parameter annotated with a built-in type as [requiring
    that exact
    
type](https://docs.cython.org/en/stable/src/userguide/language_basics.html#types).
    
    So a `key: str`typing rejects both bytes and subclasses of `str` ->
    currently things like string-valued enums cannot be used with the
    `get_option_*` typed getters (while they can be used with `get_option`):
    
        class Options(str, enum.Enum):
            BATCH_ROWS = "adbc.sqlite.query.batch_rows"
    
        connection.get_option_int(Options.BATCH_ROWS)
    
    Fix this by using the same `str | bytes` annotation for the typed
    getters as for `get_option`. Cython leaves that union as a Python
    object, allowing `_to_bytes()` to validate it with isinstance()` and
    accept `bytes`, `str`,and `str` subclasses.
    
    Signed-off-by: Fredrik Fornwall <[email protected]>
---
 .../adbc_driver_manager/_lib.pyx                   | 18 +++++------
 python/adbc_driver_manager/tests/test_lowlevel.py  | 37 ++++++++++++++++++++++
 2 files changed, 46 insertions(+), 9 deletions(-)

diff --git a/python/adbc_driver_manager/adbc_driver_manager/_lib.pyx 
b/python/adbc_driver_manager/adbc_driver_manager/_lib.pyx
index f84b71b94..cd0417650 100644
--- a/python/adbc_driver_manager/adbc_driver_manager/_lib.pyx
+++ b/python/adbc_driver_manager/adbc_driver_manager/_lib.pyx
@@ -716,7 +716,7 @@ cdef class AdbcDatabase(_AdbcHandle):
             c_len -= 1
         return buf[:c_len].decode(encoding, errors)
 
-    def get_option_bytes(self, key: str) -> bytes:
+    def get_option_bytes(self, key: str | bytes) -> bytes:
         """Get the value of a binary option."""
         cdef CAdbcError c_error = empty_error()
         cdef CAdbcStatusCode status
@@ -747,7 +747,7 @@ cdef class AdbcDatabase(_AdbcHandle):
 
         return bytes(buf[:c_len])
 
-    def get_option_float(self, key: str) -> float:
+    def get_option_float(self, key: str | bytes) -> float:
         """Get the value of a floating-point option."""
         cdef CAdbcError c_error = empty_error()
         cdef CAdbcStatusCode status
@@ -760,7 +760,7 @@ cdef class AdbcDatabase(_AdbcHandle):
         check_error(status, &c_error)
         return c_value
 
-    def get_option_int(self, key: str) -> int:
+    def get_option_int(self, key: str | bytes) -> int:
         """Get the value of an integer option."""
         cdef CAdbcError c_error = empty_error()
         cdef CAdbcStatusCode status
@@ -1096,7 +1096,7 @@ cdef class AdbcConnection(_AdbcHandle):
             c_len -= 1
         return buf[:c_len].decode(encoding, errors)
 
-    def get_option_bytes(self, key: str) -> bytes:
+    def get_option_bytes(self, key: str | bytes) -> bytes:
         """Get the value of a binary option."""
         cdef CAdbcError c_error = empty_error()
         cdef CAdbcStatusCode status
@@ -1127,7 +1127,7 @@ cdef class AdbcConnection(_AdbcHandle):
 
         return bytes(buf[:c_len])
 
-    def get_option_float(self, key: str) -> float:
+    def get_option_float(self, key: str | bytes) -> float:
         """Get the value of a floating-point option."""
         cdef CAdbcError c_error = empty_error()
         cdef CAdbcStatusCode status
@@ -1140,7 +1140,7 @@ cdef class AdbcConnection(_AdbcHandle):
         check_error(status, &c_error)
         return c_value
 
-    def get_option_int(self, key: str) -> int:
+    def get_option_int(self, key: str | bytes) -> int:
         """Get the value of an integer option."""
         cdef CAdbcError c_error = empty_error()
         cdef CAdbcStatusCode status
@@ -1718,7 +1718,7 @@ cdef class AdbcStatement(_AdbcHandle):
             c_len -= 1
         return buf[:c_len].decode(encoding, errors)
 
-    def get_option_bytes(self, key: str) -> bytes:
+    def get_option_bytes(self, key: str | bytes) -> bytes:
         """Get the value of a binary option."""
         cdef CAdbcError c_error = empty_error()
         cdef CAdbcStatusCode status
@@ -1749,7 +1749,7 @@ cdef class AdbcStatement(_AdbcHandle):
 
         return bytes(buf[:c_len])
 
-    def get_option_float(self, key: str) -> float:
+    def get_option_float(self, key: str | bytes) -> float:
         """Get the value of a floating-point option."""
         cdef CAdbcError c_error = empty_error()
         cdef CAdbcStatusCode status
@@ -1762,7 +1762,7 @@ cdef class AdbcStatement(_AdbcHandle):
         check_error(status, &c_error)
         return c_value
 
-    def get_option_int(self, key: str) -> int:
+    def get_option_int(self, key: str | bytes) -> int:
         """Get the value of an integer option."""
         cdef CAdbcError c_error = empty_error()
         cdef CAdbcStatusCode status
diff --git a/python/adbc_driver_manager/tests/test_lowlevel.py 
b/python/adbc_driver_manager/tests/test_lowlevel.py
index ffd77b292..9017420f7 100644
--- a/python/adbc_driver_manager/tests/test_lowlevel.py
+++ b/python/adbc_driver_manager/tests/test_lowlevel.py
@@ -104,6 +104,43 @@ class ExampleEnum(enum.Enum):
     BAR = "BAR"
 
 
+class ExampleStringEnum(str, enum.Enum):
+    BATCH_ROWS = "adbc.sqlite.query.batch_rows"
+    BIND_BY_NAME = "adbc.statement.bind_by_name"
+    CONNECTION_AUTOCOMMIT = "adbc.connection.autocommit"
+    DATABASE_URI = "uri"
+
+
[email protected]("key_as_bytes", [False, True], ids=["str-subclass", 
"bytes"])
[email protected]
+def test_typed_get_option_key_types(sqlite_raw, key_as_bytes) -> None:
+    def key(option: ExampleStringEnum) -> str | bytes:
+        if key_as_bytes:
+            return option.value.encode()
+        return option
+
+    database, connection = sqlite_raw
+    assert 
database.get_option_bytes(key(ExampleStringEnum.DATABASE_URI)).startswith(
+        b"file:"
+    )
+    assert database.get_option_float(key(ExampleStringEnum.BATCH_ROWS)) == 
1024.0
+    assert database.get_option_int(key(ExampleStringEnum.BATCH_ROWS)) == 1024
+
+    assert (
+        
connection.get_option_bytes(key(ExampleStringEnum.CONNECTION_AUTOCOMMIT))
+        == b"true"
+    )
+    assert connection.get_option_float(key(ExampleStringEnum.BATCH_ROWS)) == 
1024.0
+    assert connection.get_option_int(key(ExampleStringEnum.BATCH_ROWS)) == 1024
+
+    with adbc_driver_manager.AdbcStatement(connection) as statement:
+        assert (
+            statement.get_option_bytes(key(ExampleStringEnum.BIND_BY_NAME)) == 
b"false"
+        )
+        assert statement.get_option_float(key(ExampleStringEnum.BATCH_ROWS)) 
== 1024.0
+        assert statement.get_option_int(key(ExampleStringEnum.BATCH_ROWS)) == 
1024
+
+
 @pytest.mark.sqlite
 def test_database_init(tmp_path) -> None:
     option = "adbc.sqlite.query.batch_rows"

Reply via email to