brainlessdb 0.17.3__tar.gz → 0.17.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- brainlessdb-0.17.4/.python-version +1 -0
- {brainlessdb-0.17.3 → brainlessdb-0.17.4}/CHANGELOG.md +9 -0
- {brainlessdb-0.17.3 → brainlessdb-0.17.4}/PKG-INFO +2 -2
- {brainlessdb-0.17.3 → brainlessdb-0.17.4}/brainlessdb/__init__.py +1 -1
- {brainlessdb-0.17.3 → brainlessdb-0.17.4}/brainlessdb/registry.py +7 -0
- {brainlessdb-0.17.3 → brainlessdb-0.17.4}/brainlessdb/struct.py +4 -1
- {brainlessdb-0.17.3 → brainlessdb-0.17.4}/pyproject.toml +1 -1
- {brainlessdb-0.17.3 → brainlessdb-0.17.4}/tests/test_registry.py +15 -0
- {brainlessdb-0.17.3 → brainlessdb-0.17.4}/uv.lock +23 -23
- brainlessdb-0.17.3/.python-version +0 -1
- {brainlessdb-0.17.3 → brainlessdb-0.17.4}/.gitignore +0 -0
- {brainlessdb-0.17.3 → brainlessdb-0.17.4}/README.md +0 -0
- {brainlessdb-0.17.3 → brainlessdb-0.17.4}/brainlessdb/bucket.py +0 -0
- {brainlessdb-0.17.3 → brainlessdb-0.17.4}/brainlessdb/client.py +0 -0
- {brainlessdb-0.17.3 → brainlessdb-0.17.4}/brainlessdb/collection.py +0 -0
- {brainlessdb-0.17.3 → brainlessdb-0.17.4}/brainlessdb/fields.py +0 -0
- {brainlessdb-0.17.3 → brainlessdb-0.17.4}/cliff.toml +0 -0
- {brainlessdb-0.17.3 → brainlessdb-0.17.4}/code_style_guide.md +0 -0
- {brainlessdb-0.17.3 → brainlessdb-0.17.4}/justfile +0 -0
- {brainlessdb-0.17.3 → brainlessdb-0.17.4}/test_results.txt +0 -0
- {brainlessdb-0.17.3 → brainlessdb-0.17.4}/tests/__init__.py +0 -0
- {brainlessdb-0.17.3 → brainlessdb-0.17.4}/tests/conftest.py +0 -0
- {brainlessdb-0.17.3 → brainlessdb-0.17.4}/tests/mock_nats.py +0 -0
- {brainlessdb-0.17.3 → brainlessdb-0.17.4}/tests/test_bucket.py +0 -0
- {brainlessdb-0.17.3 → brainlessdb-0.17.4}/tests/test_client.py +0 -0
- {brainlessdb-0.17.3 → brainlessdb-0.17.4}/tests/test_collection.py +0 -0
- {brainlessdb-0.17.3 → brainlessdb-0.17.4}/tests/test_dead_letter.py +0 -0
- {brainlessdb-0.17.3 → brainlessdb-0.17.4}/tests/test_deserialization.py +0 -0
- {brainlessdb-0.17.3 → brainlessdb-0.17.4}/tests/test_duplicates.py +0 -0
- {brainlessdb-0.17.3 → brainlessdb-0.17.4}/tests/test_fields.py +0 -0
- {brainlessdb-0.17.3 → brainlessdb-0.17.4}/tests/test_flush_errors.py +0 -0
- {brainlessdb-0.17.3 → brainlessdb-0.17.4}/tests/test_load_errors.py +0 -0
- {brainlessdb-0.17.3 → brainlessdb-0.17.4}/tests/test_sync.py +0 -0
- {brainlessdb-0.17.3 → brainlessdb-0.17.4}/tests/test_thread_safety.py +0 -0
- {brainlessdb-0.17.3 → brainlessdb-0.17.4}/tests/test_update_atomic.py +0 -0
- {brainlessdb-0.17.3 → brainlessdb-0.17.4}/tests/test_version.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.14
|
|
@@ -3,6 +3,15 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
5
|
|
|
6
|
+
## [0.17.4] - 2026-11-03
|
|
7
|
+
|
|
8
|
+
### 🐛 Bug Fixes
|
|
9
|
+
|
|
10
|
+
- Validate registry on save so invalid values are never flushed
|
|
11
|
+
- Plain attribute assignment is not type-checked by msgspec, so e.g. None in
|
|
12
|
+
a non-Optional field was flushed and every reader dead-lettered the entry.
|
|
13
|
+
|
|
14
|
+
|
|
6
15
|
## [0.17.3] - 2026-10-21
|
|
7
16
|
|
|
8
17
|
### 🐛 Bug Fixes
|
|
@@ -81,6 +81,13 @@ class Registry(Generic[T]):
|
|
|
81
81
|
# === Dirty / save ===
|
|
82
82
|
|
|
83
83
|
def mark_dirty(self) -> None:
|
|
84
|
+
"""Validate the instance and schedule it for flush.
|
|
85
|
+
|
|
86
|
+
Plain attribute assignment is not type-checked by msgspec, so an invalid
|
|
87
|
+
value (e.g. None for a non-Optional field) is caught here and raises
|
|
88
|
+
msgspec.ValidationError before it can be flushed and break every reader.
|
|
89
|
+
"""
|
|
90
|
+
self._apply(self._to_data())
|
|
84
91
|
self._dirty = True
|
|
85
92
|
self._client.schedule_flush()
|
|
86
93
|
|
|
@@ -205,7 +205,10 @@ class BrainlessRegistry(Struct, kw_only=True, tag=True, tag_field="TYPE", dict=T
|
|
|
205
205
|
return uuid5(_REGISTRY_NS, type(self).__key__)
|
|
206
206
|
|
|
207
207
|
def save(self) -> None:
|
|
208
|
-
"""
|
|
208
|
+
"""Validate and mark this registry entry dirty in its Registry.
|
|
209
|
+
|
|
210
|
+
Raises msgspec.ValidationError when a field holds a value of wrong type.
|
|
211
|
+
"""
|
|
209
212
|
owner_ref = getattr(self, "_owner", None)
|
|
210
213
|
owner = owner_ref() if owner_ref is not None else None
|
|
211
214
|
if owner is not None:
|
|
@@ -62,6 +62,21 @@ class TestRegistryBasics:
|
|
|
62
62
|
class TestRegistryPersistence:
|
|
63
63
|
"""Save / reload through the shared bucket."""
|
|
64
64
|
|
|
65
|
+
@pytest.mark.asyncio
|
|
66
|
+
async def test_save_rejects_invalid_value(self):
|
|
67
|
+
nats = MockNats()
|
|
68
|
+
db = BrainlessDB(nats, namespace="loc1", flush_interval=0)
|
|
69
|
+
sip = db.registry(SipConfigV1)
|
|
70
|
+
await db.start()
|
|
71
|
+
|
|
72
|
+
sip.host = None # not Optional
|
|
73
|
+
with pytest.raises(msgspec.ValidationError):
|
|
74
|
+
sip.save()
|
|
75
|
+
assert await db.flush() == 0
|
|
76
|
+
assert SipConfigV1.storage_key() not in await nats.jetstream()._buckets[REGISTRY_BUCKET].keys()
|
|
77
|
+
|
|
78
|
+
await db.stop()
|
|
79
|
+
|
|
65
80
|
@pytest.mark.asyncio
|
|
66
81
|
async def test_save_and_reload(self):
|
|
67
82
|
nats = MockNats()
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
version = 1
|
|
2
|
-
revision =
|
|
2
|
+
revision = 5
|
|
3
3
|
requires-python = ">=3.9"
|
|
4
4
|
resolution-markers = [
|
|
5
5
|
"python_full_version >= '3.10'",
|
|
@@ -17,7 +17,7 @@ wheels = [
|
|
|
17
17
|
|
|
18
18
|
[[package]]
|
|
19
19
|
name = "brainlessdb"
|
|
20
|
-
version = "0.17.
|
|
20
|
+
version = "0.17.3"
|
|
21
21
|
source = { editable = "." }
|
|
22
22
|
dependencies = [
|
|
23
23
|
{ name = "msgspec" },
|
|
@@ -60,7 +60,7 @@ name = "exceptiongroup"
|
|
|
60
60
|
version = "1.3.1"
|
|
61
61
|
source = { registry = "https://pypi.org/simple" }
|
|
62
62
|
dependencies = [
|
|
63
|
-
{ name = "typing-extensions"
|
|
63
|
+
{ name = "typing-extensions" },
|
|
64
64
|
]
|
|
65
65
|
sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" }
|
|
66
66
|
wheels = [
|
|
@@ -196,13 +196,13 @@ resolution-markers = [
|
|
|
196
196
|
"python_full_version < '3.10'",
|
|
197
197
|
]
|
|
198
198
|
dependencies = [
|
|
199
|
-
{ name = "colorama", marker = "
|
|
200
|
-
{ name = "exceptiongroup"
|
|
201
|
-
{ name = "iniconfig", version = "2.1.0", source = { registry = "https://pypi.org/simple" }
|
|
202
|
-
{ name = "packaging"
|
|
203
|
-
{ name = "pluggy"
|
|
204
|
-
{ name = "pygments"
|
|
205
|
-
{ name = "tomli"
|
|
199
|
+
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
|
200
|
+
{ name = "exceptiongroup" },
|
|
201
|
+
{ name = "iniconfig", version = "2.1.0", source = { registry = "https://pypi.org/simple" } },
|
|
202
|
+
{ name = "packaging" },
|
|
203
|
+
{ name = "pluggy" },
|
|
204
|
+
{ name = "pygments" },
|
|
205
|
+
{ name = "tomli" },
|
|
206
206
|
]
|
|
207
207
|
sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" }
|
|
208
208
|
wheels = [
|
|
@@ -217,13 +217,13 @@ resolution-markers = [
|
|
|
217
217
|
"python_full_version >= '3.10'",
|
|
218
218
|
]
|
|
219
219
|
dependencies = [
|
|
220
|
-
{ name = "colorama", marker = "
|
|
221
|
-
{ name = "exceptiongroup", marker = "python_full_version
|
|
222
|
-
{ name = "iniconfig", version = "2.3.0", source = { registry = "https://pypi.org/simple" }
|
|
223
|
-
{ name = "packaging"
|
|
224
|
-
{ name = "pluggy"
|
|
225
|
-
{ name = "pygments"
|
|
226
|
-
{ name = "tomli", marker = "python_full_version
|
|
220
|
+
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
|
221
|
+
{ name = "exceptiongroup", marker = "python_full_version < '3.11'" },
|
|
222
|
+
{ name = "iniconfig", version = "2.3.0", source = { registry = "https://pypi.org/simple" } },
|
|
223
|
+
{ name = "packaging" },
|
|
224
|
+
{ name = "pluggy" },
|
|
225
|
+
{ name = "pygments" },
|
|
226
|
+
{ name = "tomli", marker = "python_full_version < '3.11'" },
|
|
227
227
|
]
|
|
228
228
|
sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" }
|
|
229
229
|
wheels = [
|
|
@@ -238,9 +238,9 @@ resolution-markers = [
|
|
|
238
238
|
"python_full_version < '3.10'",
|
|
239
239
|
]
|
|
240
240
|
dependencies = [
|
|
241
|
-
{ name = "backports-asyncio-runner"
|
|
242
|
-
{ name = "pytest", version = "8.4.2", source = { registry = "https://pypi.org/simple" }
|
|
243
|
-
{ name = "typing-extensions"
|
|
241
|
+
{ name = "backports-asyncio-runner" },
|
|
242
|
+
{ name = "pytest", version = "8.4.2", source = { registry = "https://pypi.org/simple" } },
|
|
243
|
+
{ name = "typing-extensions" },
|
|
244
244
|
]
|
|
245
245
|
sdist = { url = "https://files.pythonhosted.org/packages/42/86/9e3c5f48f7b7b638b216e4b9e645f54d199d7abbbab7a64a13b4e12ba10f/pytest_asyncio-1.2.0.tar.gz", hash = "sha256:c609a64a2a8768462d0c99811ddb8bd2583c33fd33cf7f21af1c142e824ffb57", size = 50119, upload-time = "2025-09-12T07:33:53.816Z" }
|
|
246
246
|
wheels = [
|
|
@@ -255,9 +255,9 @@ resolution-markers = [
|
|
|
255
255
|
"python_full_version >= '3.10'",
|
|
256
256
|
]
|
|
257
257
|
dependencies = [
|
|
258
|
-
{ name = "backports-asyncio-runner", marker = "python_full_version
|
|
259
|
-
{ name = "pytest", version = "9.0.2", source = { registry = "https://pypi.org/simple" }
|
|
260
|
-
{ name = "typing-extensions", marker = "python_full_version
|
|
258
|
+
{ name = "backports-asyncio-runner", marker = "python_full_version < '3.11'" },
|
|
259
|
+
{ name = "pytest", version = "9.0.2", source = { registry = "https://pypi.org/simple" } },
|
|
260
|
+
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
|
|
261
261
|
]
|
|
262
262
|
sdist = { url = "https://files.pythonhosted.org/packages/90/2c/8af215c0f776415f3590cac4f9086ccefd6fd463befeae41cd4d3f193e5a/pytest_asyncio-1.3.0.tar.gz", hash = "sha256:d7f52f36d231b80ee124cd216ffb19369aa168fc10095013c6b014a34d3ee9e5", size = 50087, upload-time = "2025-11-10T16:07:47.256Z" }
|
|
263
263
|
wheels = [
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
3.9
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|