Skip to content

Commit 3637f01

Browse files
committed
feat(adapters): support to __conform__() method
1 parent 2ad05fd commit 3637f01

File tree

2 files changed

+71
-2
lines changed

2 files changed

+71
-2
lines changed

src/sqlitecloud/dbapi2.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -275,17 +275,22 @@ def cursor(self):
275275

276276
def _apply_adapter(self, value: object) -> SQLiteTypes:
277277
"""
278-
Applies the adapter to convert the Python type into a SQLite supported type.
278+
Applies the registered adapter to convert the Python type into a SQLite supported type.
279+
In the case there is no registered adapter, it calls the __conform__() method when the value object implements it.
279280
280281
Args:
281282
value (object): The Python type to convert.
282283
283284
Returns:
284-
SQLiteTypes: The SQLite supported type.
285+
SQLiteTypes: The SQLite supported type or the given value when no adapter is found.
285286
"""
286287
if type(value) in adapters:
287288
return adapters[type(value)](value)
288289

290+
if hasattr(value, "__conform__"):
291+
# we don't support sqlite3.PrepareProtocol
292+
return value.__conform__(None)
293+
289294
return value
290295

291296
def __del__(self) -> None:

src/tests/integration/test_sqlite3_parity.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,70 @@ def adapt_datetime(ts):
477477

478478
assert result[0] == adapt_datetime(now)
479479

480+
@pytest.mark.parametrize(
481+
"connection",
482+
[
483+
"sqlitecloud_dbapi2_connection",
484+
"sqlite3_connection",
485+
],
486+
)
487+
def test_conform_object(self, connection, request):
488+
connection = request.getfixturevalue(connection)
489+
490+
class Point:
491+
def __init__(self, x, y):
492+
self.x, self.y = x, y
493+
494+
def __conform__(self, protocol):
495+
if isinstance(connection, sqlitecloud.Connection):
496+
assert protocol is None
497+
elif isinstance(connection, sqlite3.Connection):
498+
assert protocol is sqlite3.PrepareProtocol
499+
else:
500+
pytest.fail("Unknown connection type")
501+
502+
return f"{self.x};{self.y}"
503+
504+
p = Point(4.0, -3.2)
505+
cursor = connection.execute("SELECT ?", (p,))
506+
507+
result = cursor.fetchone()
508+
509+
assert result[0] == "4.0;-3.2"
510+
511+
@pytest.mark.parametrize(
512+
"connection, module",
513+
[
514+
("sqlitecloud_dbapi2_connection", sqlitecloud),
515+
("sqlite3_connection", sqlite3),
516+
],
517+
)
518+
def test_adapters_to_have_precedence_over_conform_object(
519+
self, connection, module, request
520+
):
521+
connection = request.getfixturevalue(connection)
522+
523+
class Point:
524+
def __init__(self, x, y):
525+
self.x, self.y = x, y
526+
527+
def __conform__(self, protocol):
528+
# 4.0;1.1
529+
return f"{self.x};{self.y}"
530+
531+
def adapt_point(point):
532+
# 4.0, 1.1
533+
return f"{point.x}, {point.y}"
534+
535+
module.register_adapter(Point, adapt_point)
536+
537+
p = Point(4.0, -3.2)
538+
cursor = connection.execute("SELECT ?", (p,))
539+
540+
result = cursor.fetchone()
541+
542+
assert result[0] == "4.0, -3.2"
543+
480544
# def test_datatypes(self, sqlite3_connection):
481545
# class Point:
482546
# def __init__(self, x, y):

0 commit comments

Comments
 (0)