client-query-cache 0.3.0__tar.gz → 0.4.0__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.
Files changed (49) hide show
  1. {client_query_cache-0.3.0 → client_query_cache-0.4.0}/PKG-INFO +2 -2
  2. {client_query_cache-0.3.0 → client_query_cache-0.4.0}/README.md +1 -1
  3. {client_query_cache-0.3.0 → client_query_cache-0.4.0}/pyproject.toml +3 -6
  4. {client_query_cache-0.3.0 → client_query_cache-0.4.0}/pyproject.toml.orig +12 -6
  5. {client_query_cache-0.3.0 → client_query_cache-0.4.0}/src/client_query_cache/asynchronous/collection.py +4 -4
  6. {client_query_cache-0.3.0 → client_query_cache-0.4.0}/src/client_query_cache/asynchronous/manager.py +8 -15
  7. {client_query_cache-0.3.0 → client_query_cache-0.4.0}/src/client_query_cache/synchronous/collection.py +4 -4
  8. {client_query_cache-0.3.0 → client_query_cache-0.4.0}/src/client_query_cache/synchronous/manager.py +8 -13
  9. {client_query_cache-0.3.0 → client_query_cache-0.4.0}/src/client_query_cache/__init__.py +0 -0
  10. {client_query_cache-0.3.0 → client_query_cache-0.4.0}/src/client_query_cache/_core/__init__.py +0 -0
  11. {client_query_cache-0.3.0 → client_query_cache-0.4.0}/src/client_query_cache/_core/canonical.py +0 -0
  12. {client_query_cache-0.3.0 → client_query_cache-0.4.0}/src/client_query_cache/_core/codec.py +0 -0
  13. {client_query_cache-0.3.0 → client_query_cache-0.4.0}/src/client_query_cache/_core/collation.py +0 -0
  14. {client_query_cache-0.3.0 → client_query_cache-0.4.0}/src/client_query_cache/_core/collection_metadata.py +0 -0
  15. {client_query_cache-0.3.0 → client_query_cache-0.4.0}/src/client_query_cache/_core/count_reads.py +0 -0
  16. {client_query_cache-0.3.0 → client_query_cache-0.4.0}/src/client_query_cache/_core/cursor_capture.py +0 -0
  17. {client_query_cache-0.3.0 → client_query_cache-0.4.0}/src/client_query_cache/_core/entries.py +0 -0
  18. {client_query_cache-0.3.0 → client_query_cache-0.4.0}/src/client_query_cache/_core/errors.py +0 -0
  19. {client_query_cache-0.3.0 → client_query_cache-0.4.0}/src/client_query_cache/_core/find_one_reads.py +0 -0
  20. {client_query_cache-0.3.0 → client_query_cache-0.4.0}/src/client_query_cache/_core/find_reads.py +0 -0
  21. {client_query_cache-0.3.0 → client_query_cache-0.4.0}/src/client_query_cache/_core/identity_reads.py +0 -0
  22. {client_query_cache-0.3.0 → client_query_cache-0.4.0}/src/client_query_cache/_core/keys.py +0 -0
  23. {client_query_cache-0.3.0 → client_query_cache-0.4.0}/src/client_query_cache/_core/lifecycle.py +0 -0
  24. {client_query_cache-0.3.0 → client_query_cache-0.4.0}/src/client_query_cache/_core/locking.py +0 -0
  25. {client_query_cache-0.3.0 → client_query_cache-0.4.0}/src/client_query_cache/_core/lru.py +0 -0
  26. {client_query_cache-0.3.0 → client_query_cache-0.4.0}/src/client_query_cache/_core/manager.py +0 -0
  27. {client_query_cache-0.3.0 → client_query_cache-0.4.0}/src/client_query_cache/_core/namespace.py +0 -0
  28. {client_query_cache-0.3.0 → client_query_cache-0.4.0}/src/client_query_cache/_core/order_sensitive_keys.py +0 -0
  29. {client_query_cache-0.3.0 → client_query_cache-0.4.0}/src/client_query_cache/_core/projection.py +0 -0
  30. {client_query_cache-0.3.0 → client_query_cache-0.4.0}/src/client_query_cache/_core/query_filters.py +0 -0
  31. {client_query_cache-0.3.0 → client_query_cache-0.4.0}/src/client_query_cache/_core/read_classification.py +0 -0
  32. {client_query_cache-0.3.0 → client_query_cache-0.4.0}/src/client_query_cache/_core/read_validation.py +0 -0
  33. {client_query_cache-0.3.0 → client_query_cache-0.4.0}/src/client_query_cache/_core/snapshots.py +0 -0
  34. {client_query_cache-0.3.0 → client_query_cache-0.4.0}/src/client_query_cache/_core/stream_cost.py +0 -0
  35. {client_query_cache-0.3.0 → client_query_cache-0.4.0}/src/client_query_cache/_core/stream_events.py +0 -0
  36. {client_query_cache-0.3.0 → client_query_cache-0.4.0}/src/client_query_cache/_core/stream_health.py +0 -0
  37. {client_query_cache-0.3.0 → client_query_cache-0.4.0}/src/client_query_cache/_core/stream_options.py +0 -0
  38. {client_query_cache-0.3.0 → client_query_cache-0.4.0}/src/client_query_cache/_core/traversal.py +0 -0
  39. {client_query_cache-0.3.0 → client_query_cache-0.4.0}/src/client_query_cache/_core/unique_keys.py +0 -0
  40. {client_query_cache-0.3.0 → client_query_cache-0.4.0}/src/client_query_cache/asynchronous/__init__.py +0 -0
  41. {client_query_cache-0.3.0 → client_query_cache-0.4.0}/src/client_query_cache/asynchronous/cursors.py +0 -0
  42. {client_query_cache-0.3.0 → client_query_cache-0.4.0}/src/client_query_cache/asynchronous/database.py +0 -0
  43. {client_query_cache-0.3.0 → client_query_cache-0.4.0}/src/client_query_cache/asynchronous/streams.py +0 -0
  44. {client_query_cache-0.3.0 → client_query_cache-0.4.0}/src/client_query_cache/otel.py +0 -0
  45. {client_query_cache-0.3.0 → client_query_cache-0.4.0}/src/client_query_cache/py.typed +0 -0
  46. {client_query_cache-0.3.0 → client_query_cache-0.4.0}/src/client_query_cache/synchronous/__init__.py +0 -0
  47. {client_query_cache-0.3.0 → client_query_cache-0.4.0}/src/client_query_cache/synchronous/cursors.py +0 -0
  48. {client_query_cache-0.3.0 → client_query_cache-0.4.0}/src/client_query_cache/synchronous/database.py +0 -0
  49. {client_query_cache-0.3.0 → client_query_cache-0.4.0}/src/client_query_cache/synchronous/streams.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: client-query-cache
3
- Version: 0.3.0
3
+ Version: 0.4.0
4
4
  Summary: Client-side caching for PyMongo, kept coherent using MongoDB change streams.
5
5
  Author: Alessio Locatelli
6
6
  Author-email: Alessio Locatelli <<software.development@secure.mailbox.org>>
@@ -45,7 +45,7 @@ with (
45
45
  MongoClient("mongodb://localhost:27017") as client,
46
46
  CacheManager(client) as cache,
47
47
  ):
48
- users = cache.cached(client["my_database"]["users"])
48
+ users = cache.get_cached_collection(client["my_database"]["users"])
49
49
  users.find_one({"_id": "alice"}) # Reads from MongoDB.
50
50
  users.find_one({"_id": "alice"}) # Repeated reads can use the cache.
51
51
  ```
@@ -31,7 +31,7 @@ with (
31
31
  MongoClient("mongodb://localhost:27017") as client,
32
32
  CacheManager(client) as cache,
33
33
  ):
34
- users = cache.cached(client["my_database"]["users"])
34
+ users = cache.get_cached_collection(client["my_database"]["users"])
35
35
  users.find_one({"_id": "alice"}) # Reads from MongoDB.
36
36
  users.find_one({"_id": "alice"}) # Repeated reads can use the cache.
37
37
  ```
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "client-query-cache"
3
- version = "0.3.0"
3
+ version = "0.4.0"
4
4
  description = "Client-side caching for PyMongo, kept coherent using MongoDB change streams."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.14.6"
@@ -46,6 +46,8 @@ dev = [
46
46
  "python-snappy>=0.7.3",
47
47
  "pytest-xdist[psutil]>=3.8.0",
48
48
  "pytest-cov>=7.1.0",
49
+ "pyyaml>=6.0.3",
50
+ "types-pyyaml>=6.0.12.20260906",
49
51
  ]
50
52
  docs = [
51
53
  "mike",
@@ -61,16 +63,11 @@ build-backend = "uv_build"
61
63
  [tool.uv]
62
64
  environments = ["sys_platform == 'linux'"]
63
65
 
64
- [tool.uv.build-backend]
65
- module-root = "src"
66
-
67
66
  [tool.uv.sources.mike]
68
67
  git = "https://github.com/squidfunk/mike.git"
69
68
  rev = "2d4ad799442f4592db8ad53b179bfb33db8c69ac"
70
69
 
71
70
  [tool.slotscheck]
72
- strict-imports = true
73
- require-superclass = true
74
71
  require-subclass = true
75
72
  exclude-modules = """
76
73
  (
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "client-query-cache"
3
- version = "0.3.0"
3
+ version = "0.4.0"
4
4
  description = "Client-side caching for PyMongo, kept coherent using MongoDB change streams."
5
5
  authors = [
6
6
  { name = "Alessio Locatelli", email = "<software.development@secure.mailbox.org>" },
@@ -45,6 +45,8 @@ dev = [
45
45
  "python-snappy>=0.7.3",
46
46
  "pytest-xdist[psutil]>=3.8.0",
47
47
  "pytest-cov>=7.1.0",
48
+ "pyyaml>=6.0.3",
49
+ "types-pyyaml>=6.0.12.20260906",
48
50
  ]
49
51
  docs = ["mike", "tomli-w>=1.2.0", "zensical>=0.0.68"]
50
52
  memory = ["pytest-memray>=1.11.0"]
@@ -53,18 +55,17 @@ memory = ["pytest-memray>=1.11.0"]
53
55
  requires = ["uv_build>=0.12.18,<0.13.0"]
54
56
  build-backend = "uv_build"
55
57
 
56
- [tool.uv.build-backend]
57
- module-root = "src"
58
-
59
58
  [tool.uv]
59
+ # uv 0.12.9 / lock context: https://docs.astral.sh/uv/reference/settings/#environments
60
+ # The default is all environments. We override it because development dependencies and their lock resolution target Linux.
60
61
  environments = ["sys_platform == 'linux'"]
61
62
 
62
63
  [tool.uv.sources]
63
64
  mike = { git = "https://github.com/squidfunk/mike.git", rev = "2d4ad799442f4592db8ad53b179bfb33db8c69ac" } # pragma: allowlist secret
64
65
 
65
66
  [tool.slotscheck]
66
- strict-imports = true
67
- require-superclass = true
67
+ # slotscheck 0.21.0: https://github.com/ariebovenberg/slotscheck/blob/v0.21.0/src/slotscheck/config.py
68
+ # require-subclass: The default is false. We override it because subclasses must preserve their slotted parent's memory contract.
68
69
  require-subclass = true
69
70
  exclude-modules = '''
70
71
  (
@@ -77,10 +78,15 @@ exclude-modules = '''
77
78
  paths = ["src", "tests", "benchmarks", ".vulture_whitelist"]
78
79
 
79
80
  [tool.ruff-extra-rules]
81
+ # ruff-extra-rules v0.5.0: https://github.com/alessio-locatelli/ruff-extra-rules/blob/v0.5.0/README.md
82
+ # The default is false. We override it because hook runs should apply available repairs.
80
83
  fix = true
81
84
  exclude = ["specifications"]
82
85
  extend-select = ["unused-pytriage"]
83
86
  [tool.ruff-extra-rules.meaningless-vars]
87
+ # ruff-extra-rules v0.5.0: https://github.com/alessio-locatelli/ruff-extra-rules/tree/v0.5.0/src/pre_commit_hooks/ast_checks
88
+ # For meaningless-vars, redundant-assignment, redundant-type-conversion, redundant-dict-get, and defined-far-from-use level:
89
+ # The default is conservative. We override it because repository quality checks must include the broader supported simplifications and naming diagnostics.
84
90
  level = "aggressive"
85
91
  [tool.ruff-extra-rules.redundant-assignment]
86
92
  level = "aggressive"
@@ -178,7 +178,7 @@ class CachedCollection[DocumentType: Mapping[str, Any]]:
178
178
  )
179
179
  effective_collation = effective_find_one_collation(
180
180
  collation,
181
- self._database.manager.default_collation_for(self._namespace()),
181
+ self._database.manager._default_collation_for(self._namespace()), # noqa: SLF001
182
182
  )
183
183
  if collation is None and effective_collation is not None:
184
184
  read_shape = canonicalize(
@@ -195,7 +195,7 @@ class CachedCollection[DocumentType: Mapping[str, Any]]:
195
195
  )
196
196
  namespace = self._namespace()
197
197
  cache = self._database.manager.cache_core
198
- known_keys = await self._database.manager.unique_keys_for(namespace)
198
+ known_keys = await self._database.manager._unique_keys_for(namespace) # noqa: SLF001
199
199
  unique_key_match = (
200
200
  match_unique_key(filter_query, known_keys, effective_collation)
201
201
  if identity is NO_IDENTITY and known_keys is not None
@@ -495,7 +495,7 @@ class CachedCollection[DocumentType: Mapping[str, Any]]:
495
495
  async def _match_unique_key(
496
496
  self, filter_query: object, effective_collation: Mapping[str, Any] | None
497
497
  ) -> tuple[UniqueKeyDefinition, tuple[Any, ...]] | None:
498
- keys = await self._database.manager.unique_keys_for(
498
+ keys = await self._database.manager._unique_keys_for( # noqa: SLF001
499
499
  self._namespace(), self._list_indexes_probe
500
500
  )
501
501
  if not keys:
@@ -655,7 +655,7 @@ class CachedCollection[DocumentType: Mapping[str, Any]]:
655
655
  return self._forced_database
656
656
 
657
657
  async def _cache_ineligibility_reason(self) -> BypassReason | None:
658
- return await self._database.manager.cache_ineligibility_reason(
658
+ return await self._database.manager._cache_ineligibility_reason( # noqa: SLF001
659
659
  self._namespace(), self._probe_collection
660
660
  )
661
661
 
@@ -76,16 +76,7 @@ class CacheManager[DocumentType: Mapping[str, Any]]:
76
76
  def active_stream_cost_databases(self) -> list[str]:
77
77
  return self._cache.active_stream_cost_databases()
78
78
 
79
- async def ensure_cache_eligible(
80
- self,
81
- namespace: NamespaceId,
82
- collection_probe: Callable[[], Awaitable[CollectionProbeResult | None]],
83
- ) -> bool:
84
- return (
85
- await self.cache_ineligibility_reason(namespace, collection_probe) is None
86
- )
87
-
88
- async def cache_ineligibility_reason(
79
+ async def _cache_ineligibility_reason(
89
80
  self,
90
81
  namespace: NamespaceId,
91
82
  collection_probe: Callable[[], Awaitable[CollectionProbeResult | None]],
@@ -112,23 +103,25 @@ class CacheManager[DocumentType: Mapping[str, Any]]:
112
103
  self._metadata.put(namespace, cached)
113
104
  return cached.bypass_reason
114
105
 
115
- def default_collation_for(self, namespace: NamespaceId) -> Mapping[str, Any] | None:
106
+ def _default_collation_for(
107
+ self, namespace: NamespaceId
108
+ ) -> Mapping[str, Any] | None:
116
109
  cached = self._metadata.get(namespace)
117
110
  return cached.default_collation if cached is not None else None
118
111
 
119
112
  @overload
120
- async def unique_keys_for(
113
+ async def _unique_keys_for(
121
114
  self,
122
115
  namespace: NamespaceId,
123
116
  list_indexes: Callable[[], Awaitable[Sequence[Mapping[str, Any]] | None]],
124
117
  ) -> tuple[UniqueKeyDefinition, ...]: ...
125
118
 
126
119
  @overload
127
- async def unique_keys_for(
120
+ async def _unique_keys_for(
128
121
  self, namespace: NamespaceId, list_indexes: None = None
129
122
  ) -> tuple[UniqueKeyDefinition, ...] | None: ...
130
123
 
131
- async def unique_keys_for(
124
+ async def _unique_keys_for(
132
125
  self,
133
126
  namespace: NamespaceId,
134
127
  list_indexes: Callable[[], Awaitable[Sequence[Mapping[str, Any]] | None]]
@@ -154,7 +147,7 @@ class CacheManager[DocumentType: Mapping[str, Any]]:
154
147
  def __getitem__(self, name: str) -> CachedDatabase[DocumentType]:
155
148
  return CachedDatabase(self, self._client[name])
156
149
 
157
- def cached(
150
+ def get_cached_collection(
158
151
  self, collection: AsyncCollection[DocumentType]
159
152
  ) -> CachedCollection[DocumentType]:
160
153
  if collection.database.client is not self._client:
@@ -178,7 +178,7 @@ class CachedCollection[DocumentType: Mapping[str, Any]]:
178
178
  )
179
179
  effective_collation = effective_find_one_collation(
180
180
  collation,
181
- self._database.manager.default_collation_for(self._namespace()),
181
+ self._database.manager._default_collation_for(self._namespace()), # noqa: SLF001
182
182
  )
183
183
  if collation is None and effective_collation is not None:
184
184
  read_shape = canonicalize(
@@ -195,7 +195,7 @@ class CachedCollection[DocumentType: Mapping[str, Any]]:
195
195
  )
196
196
  namespace = self._namespace()
197
197
  cache = self._database.manager.cache_core
198
- known_keys = self._database.manager.unique_keys_for(namespace)
198
+ known_keys = self._database.manager._unique_keys_for(namespace) # noqa: SLF001
199
199
  unique_key_match = (
200
200
  match_unique_key(filter_query, known_keys, effective_collation)
201
201
  if identity is NO_IDENTITY and known_keys is not None
@@ -491,7 +491,7 @@ class CachedCollection[DocumentType: Mapping[str, Any]]:
491
491
  def _match_unique_key(
492
492
  self, filter_query: object, effective_collation: Mapping[str, Any] | None
493
493
  ) -> tuple[UniqueKeyDefinition, tuple[Any, ...]] | None:
494
- keys = self._database.manager.unique_keys_for(
494
+ keys = self._database.manager._unique_keys_for( # noqa: SLF001
495
495
  self._namespace(), self._list_indexes_probe
496
496
  )
497
497
  if not keys:
@@ -651,7 +651,7 @@ class CachedCollection[DocumentType: Mapping[str, Any]]:
651
651
  return self._forced_database
652
652
 
653
653
  def _cache_ineligibility_reason(self) -> BypassReason | None:
654
- return self._database.manager.cache_ineligibility_reason(
654
+ return self._database.manager._cache_ineligibility_reason( # noqa: SLF001
655
655
  self._namespace(), self._probe_collection
656
656
  )
657
657
 
@@ -76,14 +76,7 @@ class CacheManager[DocumentType: Mapping[str, Any]]:
76
76
  def active_stream_cost_databases(self) -> list[str]:
77
77
  return self._cache.active_stream_cost_databases()
78
78
 
79
- def ensure_cache_eligible(
80
- self,
81
- namespace: NamespaceId,
82
- collection_probe: Callable[[], CollectionProbeResult | None],
83
- ) -> bool:
84
- return self.cache_ineligibility_reason(namespace, collection_probe) is None
85
-
86
- def cache_ineligibility_reason(
79
+ def _cache_ineligibility_reason(
87
80
  self,
88
81
  namespace: NamespaceId,
89
82
  collection_probe: Callable[[], CollectionProbeResult | None],
@@ -110,23 +103,25 @@ class CacheManager[DocumentType: Mapping[str, Any]]:
110
103
  self._metadata.put(namespace, cached)
111
104
  return cached.bypass_reason
112
105
 
113
- def default_collation_for(self, namespace: NamespaceId) -> Mapping[str, Any] | None:
106
+ def _default_collation_for(
107
+ self, namespace: NamespaceId
108
+ ) -> Mapping[str, Any] | None:
114
109
  cached = self._metadata.get(namespace)
115
110
  return cached.default_collation if cached is not None else None
116
111
 
117
112
  @overload
118
- def unique_keys_for(
113
+ def _unique_keys_for(
119
114
  self,
120
115
  namespace: NamespaceId,
121
116
  list_indexes: Callable[[], Sequence[Mapping[str, Any]] | None],
122
117
  ) -> tuple[UniqueKeyDefinition, ...]: ...
123
118
 
124
119
  @overload
125
- def unique_keys_for(
120
+ def _unique_keys_for(
126
121
  self, namespace: NamespaceId, list_indexes: None = None
127
122
  ) -> tuple[UniqueKeyDefinition, ...] | None: ...
128
123
 
129
- def unique_keys_for(
124
+ def _unique_keys_for(
130
125
  self,
131
126
  namespace: NamespaceId,
132
127
  list_indexes: Callable[[], Sequence[Mapping[str, Any]] | None] | None = None,
@@ -151,7 +146,7 @@ class CacheManager[DocumentType: Mapping[str, Any]]:
151
146
  def __getitem__(self, name: str) -> CachedDatabase[DocumentType]:
152
147
  return CachedDatabase(self, self._client[name])
153
148
 
154
- def cached(
149
+ def get_cached_collection(
155
150
  self, collection: Collection[DocumentType]
156
151
  ) -> CachedCollection[DocumentType]:
157
152
  if collection.database.client is not self._client: