Skip to content

Commit ea53eb6

Browse files
authored
Merge pull request #14 from JJ11teen/dev
Add `read_blindly_error` flag
2 parents 3c2c9ea + 3060a07 commit ea53eb6

File tree

5 files changed

+57
-22
lines changed

5 files changed

+57
-22
lines changed

.devcontainer/devcontainer.json

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
},
99
// Set *default* container specific settings.json values on container create.
1010
"settings": {
11-
"terminal.integrated.defaultProfile.linux": "/bin/bash",
1211
"python.pythonPath": "/usr/local/bin/python",
1312
"python.languageServer": "Pylance",
1413
"python.linting.enabled": false,
@@ -21,21 +20,19 @@
2120
"--section-default",
2221
"THIRDPARTY",
2322
"--project",
24-
"src",
25-
"--project",
26-
"cloudmappings",
23+
"src"
2724
],
2825
"[python]": {
2926
"editor.codeActionsOnSave": {
30-
"source.organizeImports": true,
31-
},
32-
},
27+
"source.organizeImports": true
28+
}
29+
}
3330
},
3431
// Add the IDs of extensions you want installed when the container is created.
3532
"extensions": [
3633
"ms-python.python",
3734
"ms-python.vscode-pylance",
38-
"eamodio.gitlens",
35+
"eamodio.gitlens"
3936
],
4037
// Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
4138
"remoteUser": "vscode"

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ del cm["key"]
8181

8282
Each `cloud-mapping` keeps an internal dict of [etags](https://en.wikipedia.org/wiki/HTTP_ETag) which it uses to ensure it is only reading/overwriting/deleting data it expects to. If the value in storage is not what the `cloud-mapping` expects, a `cloudmappings.errors.KeySyncError()` will be thrown.
8383

84-
If you would like to enable read (get) operations without ensuring etags, you can set `read_blindly=True`. This can be set in the constructor, or dynamically on the cloud-mapping instance. Blindly reading a value that doesn't exist in the cloud will return the mapping's current value of `read_blindly_default` (which itself defaults to `None`).
84+
If you would like to enable read (get) operations without ensuring etags, you can set `read_blindly=True`. This can be set in the constructor, or dynamically on the cloud-mapping instance. Blindly reading a value that doesn't exist in the cloud will only raise a KeyError if `read_blindly_error=True`, otherwise it will return the current value of `read_blindly_default` (which itself defaults to `None`). All of these can be changed dynamically and set at initialisation.
8585

8686
If you know what you are doing and you want an operation other than get to go through despite etags, you will need to sync your `cloud-mapping` with the cloud by calling either `.sync_with_cloud()` to sync all keys or `.sync_with_cloud(key_prefix)` to sync a specific key or subset of keys. By default `.sync_with_cloud()` is called on instantiation of a `cloud-mapping` if the underlying provider storage already exists. You may skip this initial sync by passing an additional `sync_initially=False` parameter when you instantiate your `cloud-mapping`.
8787

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[metadata]
22
name = cloud-mappings
3-
version = 1.1.0
3+
version = 1.2.0
44
author = Lucas Sargent
55
author_email = lucas.sargent@outlook.com
66
description = MutableMapping interfaces for common cloud storage providers

src/cloudmappings/cloudstoragemapping.py

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,12 @@ class CloudMapping(MutableMapping):
1616
read_blindly : bool, default=False
1717
Whether to read blindly or not by default. See `read_blindly` attribute for more
1818
information.
19+
read_blindly_error : bool, default=False
20+
Whether to raise `KeyError`s when read_blindly is enabled and the key does not have a value
21+
in the cloud.
1922
read_blindly_default : Any, default=None
20-
The value to return when read_blindly is enabled and the key does not have
21-
a value in the cloud.
23+
The value to return when read_blindly is enabled, the key does not have a value in the
24+
cloud, and read_blindly_error is `False`.
2225
ordered_dumps_funcs : List[Callable]
2326
An ordered list of functions to pass values through before saving bytes to the cloud.
2427
The last function must return a bytes-like object.
@@ -37,25 +40,32 @@ class CloudMapping(MutableMapping):
3740
3841
When read blindly is `True`, a cloud-mapping will return the latest cloud version
3942
for any key accessed, including keys it has no prior knowledge of (ie not in it's etag
40-
dict). If there is no value for a key in the cloud, the current value of
43+
dict). If there is no value for a key in the cloud, whether a `KeyValue` error is
44+
raised is controlled by the `read_blindly_error` flag. If `False`, the current value of
4145
`read_blindly_default` will be returned.
4246
43-
When read blindly is `True` a cloud-mapping will not raise `KeyValue` or
44-
`cloudmappings.errors.KeySyncError` errors for read/get operations.
47+
When read blindly is `True` a cloud-mapping will not raise `cloudmappings.errors.KeySyncError`
48+
errors for read/get operations.
4549
4650
By default a cloud-mapping is instantiated with read blindly set to `False`.
4751
"""
4852

53+
read_blindly_error: bool
54+
"""Whether to raise a `KeyValue` error when read_blindly is `True` and the key does not have
55+
a value in the cloud. If `True`, this takes prescedence over `read_blindly_default`.
56+
"""
57+
4958
read_blindly_default: Any
50-
"""The value to return when read_blindly is `True` and the key does not have
51-
a value in the cloud.
59+
"""The value to return when read_blindly is `True`, the key does not have a value in the cloud,
60+
and read_blindly_error is `False`.
5261
"""
5362

5463
def __init__(
5564
self,
5665
storage_provider: StorageProvider,
5766
sync_initially: bool = True,
5867
read_blindly: bool = False,
68+
read_blindly_error: bool = False,
5969
read_blindly_default: Any = None,
6070
ordered_dumps_funcs: List[Callable] = None,
6171
ordered_loads_funcs: List[Callable] = None,
@@ -71,9 +81,12 @@ def __init__(
7181
read_blindly : bool, default=False
7282
Whether to read blindly or not by default. See `read_blindly` attribute for more
7383
information
84+
read_blindly_error : bool, default=False
85+
Whether to raise `KeyError`s when read_blindly is enabled and the key does not have a value
86+
in the cloud
7487
read_blindly_default : Any, default=None
75-
The value to return when read_blindly is enabled and the key does not have
76-
a value in the cloud
88+
The value to return when read_blindly is enabled, the key does not have a value in the
89+
cloud, and read_blindly_error is `False`
7790
ordered_dumps_funcs : List[Callable], default=None
7891
An ordered list of functions to pass values through before saving bytes to the cloud.
7992
The last function must return a bytes-like object.
@@ -87,6 +100,7 @@ def __init__(
87100
self._ordered_loads_funcs = ordered_loads_funcs if ordered_loads_funcs is not None else []
88101

89102
self.read_blindly = read_blindly
103+
self.read_blindly_error = read_blindly_error
90104
self.read_blindly_default = read_blindly_default
91105

92106
if self._storage_provider.create_if_not_exists() and sync_initially:
@@ -141,6 +155,8 @@ def __getitem__(self, key: str) -> Any:
141155
key=self._encode_key(key), etag=None if self.read_blindly else self._etags[key]
142156
)
143157
if self.read_blindly and value is None:
158+
if self.read_blindly_error:
159+
raise KeyError(key)
144160
return self.read_blindly_default
145161
for loads in self._ordered_loads_funcs:
146162
value = loads(value)

tests/tests/2_singlecloudmapping.py

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,16 +53,38 @@ def test_basic_setting_and_getting(self, storage_provider: StorageProvider, test
5353
assert cm[test_id + "-key-a"] == b"uncapitalised"
5454
assert cm[test_id + "-key-3"] == b"three"
5555

56-
def test_read_blindly_defaults_none(self, storage_provider: StorageProvider, test_id: str):
56+
def test_read_blindy(self, storage_provider: StorageProvider, test_id: str):
5757
cm = CloudMapping(storage_provider=storage_provider, sync_initially=False)
5858
key = test_id + "/read-blindly-test"
5959

60-
# CloudMappings default to not getting blindly:
6160
assert not cm.read_blindly
62-
# If get_blindly, values default to None
61+
with pytest.raises(KeyError):
62+
cm[key]
63+
6364
cm.read_blindly = True
6465
assert cm[key] is None
6566

67+
def test_read_blindly_error(self, storage_provider: StorageProvider, test_id: str):
68+
cm = CloudMapping(storage_provider=storage_provider, sync_initially=False, read_blindly=True)
69+
key = test_id + "/read-blindly-error-test"
70+
71+
assert not cm.read_blindly_error
72+
assert cm[key] is None
73+
74+
cm.read_blindly_error = True
75+
with pytest.raises(KeyError):
76+
cm[key]
77+
78+
def test_read_blindly_default(self, storage_provider: StorageProvider, test_id: str):
79+
cm = CloudMapping(storage_provider=storage_provider, sync_initially=False, read_blindly=True)
80+
key = test_id + "/read-blindly-default-test"
81+
82+
assert cm.read_blindly_default is None
83+
assert cm[key] is None
84+
85+
cm.read_blindly_default = 10
86+
assert cm[key] == 10
87+
6688
def test_complex_keys(self, storage_provider: StorageProvider, test_id: str):
6789
cm = CloudMapping(storage_provider=storage_provider, sync_initially=False)
6890
key1 = test_id + "/here/are/some/sub/dirs"

0 commit comments

Comments
 (0)