Skip to content

Commit 6f3ae86

Browse files
committed
Better support check for deterministic=True, closes #425
Bug first discovered in #421
1 parent 95522ad commit 6f3ae86

File tree

2 files changed

+39
-28
lines changed

2 files changed

+39
-28
lines changed

sqlite_utils/db.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -392,13 +392,18 @@ def register(fn):
392392
if not replace and (name, arity) in self._registered_functions:
393393
return fn
394394
kwargs = {}
395-
if (
396-
deterministic
397-
and sys.version_info >= (3, 8)
398-
and self.sqlite_version >= (3, 8, 3)
399-
):
400-
kwargs["deterministic"] = True
401-
self.conn.create_function(name, arity, fn, **kwargs)
395+
registered = False
396+
if deterministic:
397+
# Try this, but fall back if sqlite3.NotSupportedError
398+
try:
399+
self.conn.create_function(
400+
name, arity, fn, **dict(kwargs, deterministic=True)
401+
)
402+
registered = True
403+
except sqlite3.NotSupportedError:
404+
pass
405+
if not registered:
406+
self.conn.create_function(name, arity, fn, **kwargs)
402407
self._registered_functions.add((name, arity))
403408
return fn
404409

tests/test_register_function.py

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
# flake8: noqa
22
import pytest
33
import sys
4-
from unittest.mock import MagicMock
4+
from unittest.mock import MagicMock, call
5+
from sqlite_utils.utils import sqlite3
56

67

78
def test_register_function(fresh_db):
@@ -31,36 +32,41 @@ def to_lower(s):
3132
assert result == "bob"
3233

3334

34-
@pytest.mark.skipif(
35-
sys.version_info < (3, 8), reason="deterministic=True was added in Python 3.8"
36-
)
37-
@pytest.mark.parametrize(
38-
"fake_sqlite_version,should_use_deterministic",
39-
(
40-
("3.36.0", True),
41-
("3.8.3", True),
42-
("3.8.2", False),
43-
),
44-
)
45-
def test_register_function_deterministic_registered(
46-
fresh_db, fake_sqlite_version, should_use_deterministic
47-
):
35+
def test_register_function_deterministic_tries_again_if_exception_raised(fresh_db):
4836
fresh_db.conn = MagicMock()
4937
fresh_db.conn.create_function = MagicMock()
50-
fresh_db.conn.execute().fetchall.return_value = [(fake_sqlite_version,)]
5138

5239
@fresh_db.register_function(deterministic=True)
5340
def to_lower_2(s):
5441
return s.lower()
5542

56-
expected_kwargs = {}
57-
if should_use_deterministic:
58-
expected_kwargs = dict(deterministic=True)
59-
6043
fresh_db.conn.create_function.assert_called_with(
61-
"to_lower_2", 1, to_lower_2, **expected_kwargs
44+
"to_lower_2", 1, to_lower_2, deterministic=True
6245
)
6346

47+
first = True
48+
49+
def side_effect(*args, **kwargs):
50+
# Raise exception only first time this is called
51+
nonlocal first
52+
if first:
53+
first = False
54+
raise sqlite3.NotSupportedError()
55+
56+
# But if sqlite3.NotSupportedError is raised, it tries again
57+
fresh_db.conn.create_function.reset_mock()
58+
fresh_db.conn.create_function.side_effect = side_effect
59+
60+
@fresh_db.register_function(deterministic=True)
61+
def to_lower_3(s):
62+
return s.lower()
63+
64+
# Should have been called once with deterministic=True and once without
65+
assert fresh_db.conn.create_function.call_args_list == [
66+
call("to_lower_3", 1, to_lower_3, deterministic=True),
67+
call("to_lower_3", 1, to_lower_3),
68+
]
69+
6470

6571
def test_register_function_replace(fresh_db):
6672
@fresh_db.register_function()

0 commit comments

Comments
 (0)