Skip to content

Commit 6deb13a

Browse files
committed
feat: add ability to use sqlite3 account info
This commit is added due to the exposure of Backblaze/b2-sdk-python#175 which is an issue in the thread-safety of InMemoryAccountInfo
1 parent a9128e8 commit 6deb13a

File tree

3 files changed

+40
-17
lines changed

3 files changed

+40
-17
lines changed

django_backblaze_b2/options.py

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Any, Dict, Optional, Union
1+
from typing import Any, Dict, Optional, Union, cast
22

33
try:
44
from typing import TypedDict
@@ -26,18 +26,20 @@ class BackblazeB2StorageOptions(TypedDict):
2626
nonExistentBucketDetails: Optional[Dict[str, Union[str, Dict[str, Any]]]]
2727
defaultFileInfo: Dict[str, Any]
2828
specificBucketNames: ProxiedBucketNames
29+
sqliteDatabase: str # default unset
2930

3031

3132
def getDefaultB2StorageOptions() -> BackblazeB2StorageOptions:
32-
return {
33-
"realm": "production",
34-
"application_key_id": "---",
35-
"application_key": "---",
36-
"bucket": "django",
37-
"authorizeOnInit": True,
38-
"validateOnInit": True,
39-
"allowFileOverwrites": False,
40-
"nonExistentBucketDetails": None,
41-
"defaultFileInfo": {},
42-
"specificBucketNames": {"public": None, "loggedIn": None, "staff": None},
43-
}
33+
return cast(
34+
BackblazeB2StorageOptions,
35+
{
36+
"realm": "production",
37+
"bucket": "django",
38+
"authorizeOnInit": True,
39+
"validateOnInit": True,
40+
"allowFileOverwrites": False,
41+
"nonExistentBucketDetails": None,
42+
"defaultFileInfo": {},
43+
"specificBucketNames": {"public": None, "loggedIn": None, "staff": None},
44+
},
45+
)

django_backblaze_b2/storage.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from logging import getLogger
33
from typing import IO, Any, Dict, List, Optional, Tuple
44

5-
from b2sdk.v1 import B2Api, Bucket, InMemoryAccountInfo
5+
from b2sdk.v1 import B2Api, Bucket, InMemoryAccountInfo, SqliteAccountInfo
66
from b2sdk.v1.exception import FileNotPresent, NonExistentBucket
77
from django.core.exceptions import ImproperlyConfigured
88
from django.core.files.base import File
@@ -30,6 +30,9 @@ def __init__(self, **kwargs):
3030
)
3131
self._allowFileOverwrites = opts["allowFileOverwrites"]
3232

33+
if opts.get("sqliteDatabase"):
34+
self._sqliteDbPath = opts["sqliteDatabase"]
35+
3336
logger.info(f"{self.__class__.__name__} instantiated to use bucket {self._bucketName}")
3437
if opts["authorizeOnInit"]:
3538
logger.debug(f"{self.__class__.__name__} authorizing")
@@ -47,7 +50,7 @@ def _getDjangoSettingsOptions(self, kwargOpts: Dict) -> BackblazeB2StorageOption
4750
raise ImproperlyConfigured("add BACKBLAZE_CONFIG dict to django settings")
4851
if "application_key_id" not in settings.BACKBLAZE_CONFIG or "application_key" not in settings.BACKBLAZE_CONFIG:
4952
raise ImproperlyConfigured(
50-
"At minimium BACKBLAZE_CONFIG must contain auth 'application_key' and 'application_key_id'"
53+
"At minimum BACKBLAZE_CONFIG must contain auth 'application_key' and 'application_key_id'"
5154
f"\nfound: {settings.BACKBLAZE_CONFIG}"
5255
)
5356
opts = getDefaultB2StorageOptions()
@@ -57,7 +60,11 @@ def _getDjangoSettingsOptions(self, kwargOpts: Dict) -> BackblazeB2StorageOption
5760
@property
5861
def b2Api(self) -> B2Api:
5962
if not hasattr(self, "_b2Api"):
60-
self._accountInfo = InMemoryAccountInfo()
63+
self._accountInfo = (
64+
SqliteAccountInfo(file_name=self._sqliteDbPath)
65+
if hasattr(self, "_sqliteDbPath")
66+
else InMemoryAccountInfo()
67+
)
6168
self._b2Api = B2Api(self._accountInfo)
6269
self._b2Api.authorize_account(**self._authInfo)
6370
return self._b2Api

tests/test_b2_storage_class.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from unittest import mock
33

44
import pytest
5+
from b2sdk.account_info.exception import CorruptAccountInfo
56
from b2sdk.exception import FileNotPresent
67
from b2sdk.file_version import FileVersionInfoFactory
78
from b2sdk.v1 import B2Api, Bucket
@@ -25,7 +26,7 @@ def test_requiresConfigurationForAuth(settings):
2526
with pytest.raises(ImproperlyConfigured) as error:
2627
BackblazeB2Storage()
2728

28-
assert ("At minimium BACKBLAZE_CONFIG must contain auth 'application_key' and 'application_key_id'") in str(
29+
assert ("At minimum BACKBLAZE_CONFIG must contain auth 'application_key' and 'application_key_id'") in str(
2930
error
3031
)
3132

@@ -177,5 +178,18 @@ def test_existsFileDoesNotExist(settings):
177178
assert mockedBucket.get_file_info_by_name.call_count == 1
178179

179180

181+
def test_canUseSqliteAccountInfo(settings, tmpdir):
182+
tempFile = tmpdir.mkdir("sub").join("database.sqlite3")
183+
tempFile.write("some-invalid-context")
184+
with mock.patch.object(
185+
settings, "BACKBLAZE_CONFIG", _settingsDict({"sqliteDatabase": str(tempFile)})
186+
), mock.patch.object(B2Api, "authorize_account"), mock.patch.object(B2Api, "get_bucket_by_name"):
187+
188+
with pytest.raises(CorruptAccountInfo) as error:
189+
BackblazeB2Storage(opts={})
190+
191+
assert str(tempFile) in str(error.value)
192+
193+
180194
def _settingsDict(config: Dict[str, Any]) -> Dict[str, Any]:
181195
return {"application_key_id": "---", "application_key": "---", **config}

0 commit comments

Comments
 (0)