fast-cache-middleware 0.0.1__py3-none-any.whl → 0.0.3__py3-none-any.whl
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.
- fast_cache_middleware/__init__.py +35 -34
- fast_cache_middleware/controller.py +231 -231
- fast_cache_middleware/depends.py +66 -66
- fast_cache_middleware/exceptions.py +6 -6
- fast_cache_middleware/middleware.py +306 -293
- fast_cache_middleware/schemas.py +21 -21
- fast_cache_middleware/serializers.py +92 -92
- fast_cache_middleware/storages.py +238 -238
- {fast_cache_middleware-0.0.1.dist-info → fast_cache_middleware-0.0.3.dist-info}/METADATA +3 -2
- fast_cache_middleware-0.0.3.dist-info/RECORD +11 -0
- fast_cache_middleware-0.0.1.dist-info/RECORD +0 -11
- {fast_cache_middleware-0.0.1.dist-info → fast_cache_middleware-0.0.3.dist-info}/WHEEL +0 -0
@@ -1,238 +1,238 @@
|
|
1
|
-
import logging
|
2
|
-
import re
|
3
|
-
import time
|
4
|
-
import typing as tp
|
5
|
-
from collections import OrderedDict
|
6
|
-
|
7
|
-
from starlette.requests import Request
|
8
|
-
from starlette.responses import Response
|
9
|
-
from typing_extensions import TypeAlias
|
10
|
-
|
11
|
-
from .exceptions import StorageError
|
12
|
-
from .serializers import BaseSerializer, JSONSerializer, Metadata
|
13
|
-
|
14
|
-
logger = logging.getLogger(__name__)
|
15
|
-
|
16
|
-
# Define type for stored response
|
17
|
-
StoredResponse: TypeAlias = tp.Tuple[Response, Request, Metadata]
|
18
|
-
|
19
|
-
|
20
|
-
# Define base class for cache storage
|
21
|
-
class BaseStorage:
|
22
|
-
"""Base class for cache storage.
|
23
|
-
|
24
|
-
Args:
|
25
|
-
serializer: Serializer for converting Response/Request to string/bytes
|
26
|
-
ttl: Cache lifetime in seconds. None for permanent storage
|
27
|
-
"""
|
28
|
-
|
29
|
-
def __init__(
|
30
|
-
self,
|
31
|
-
serializer: tp.Optional[BaseSerializer] = None,
|
32
|
-
ttl: tp.Optional[tp.Union[int, float]] = None,
|
33
|
-
) -> None:
|
34
|
-
self._serializer = serializer or JSONSerializer()
|
35
|
-
|
36
|
-
if ttl is not None and ttl <= 0:
|
37
|
-
raise StorageError("TTL must be positive")
|
38
|
-
|
39
|
-
self._ttl = ttl
|
40
|
-
|
41
|
-
async def store(
|
42
|
-
self, key: str, response: Response, request: Request, metadata: Metadata
|
43
|
-
) -> None:
|
44
|
-
raise NotImplementedError()
|
45
|
-
|
46
|
-
async def retrieve(self, key: str) -> tp.Optional[StoredResponse]:
|
47
|
-
raise NotImplementedError()
|
48
|
-
|
49
|
-
async def remove(self, path: re.Pattern) -> None:
|
50
|
-
raise NotImplementedError()
|
51
|
-
|
52
|
-
async def close(self) -> None:
|
53
|
-
raise NotImplementedError()
|
54
|
-
|
55
|
-
|
56
|
-
class InMemoryStorage(BaseStorage):
|
57
|
-
"""In-memory cache storage with TTL and LRU eviction support.
|
58
|
-
|
59
|
-
Implements optimized storage of cached responses in memory with:
|
60
|
-
- LRU (Least Recently Used) eviction when max_size is exceeded
|
61
|
-
- TTL (Time To Live) with lazy checking on read
|
62
|
-
- Batch cleanup for better performance
|
63
|
-
|
64
|
-
Args:
|
65
|
-
max_size: Maximum number of cache entries
|
66
|
-
serializer: Serializer not used for InMemoryStorage
|
67
|
-
ttl: Cache lifetime in seconds. None for permanent storage
|
68
|
-
"""
|
69
|
-
|
70
|
-
def __init__(
|
71
|
-
self,
|
72
|
-
max_size: int = 1000,
|
73
|
-
serializer: tp.Optional[BaseSerializer] = None,
|
74
|
-
ttl: tp.Optional[tp.Union[int, float]] = None,
|
75
|
-
) -> None:
|
76
|
-
super().__init__(serializer=serializer, ttl=ttl)
|
77
|
-
|
78
|
-
if max_size <= 0:
|
79
|
-
raise StorageError("Max size must be positive")
|
80
|
-
|
81
|
-
self._max_size = max_size
|
82
|
-
# Cleanup batch size - default 10% of max_size, minimum 1
|
83
|
-
self._cleanup_batch_size = max(1, max_size // 10)
|
84
|
-
# Cleanup threshold - 5% more than max_size
|
85
|
-
self._cleanup_threshold = max_size + max(1, max_size // 20)
|
86
|
-
|
87
|
-
# OrderedDict for efficient LRU
|
88
|
-
self._storage: OrderedDict[str, StoredResponse] = OrderedDict()
|
89
|
-
# Separate expiry time storage for fast TTL checking
|
90
|
-
self._expiry_times: tp.Dict[str, float] = {}
|
91
|
-
self._last_expiry_check_time: float = 0
|
92
|
-
self._expiry_check_interval: float = 60
|
93
|
-
|
94
|
-
async def store(
|
95
|
-
self, key: str, response: Response, request: Request, metadata: Metadata
|
96
|
-
) -> None:
|
97
|
-
"""Saves response to cache with TTL and LRU eviction support.
|
98
|
-
|
99
|
-
If element already exists, it moves to the end (most recently used).
|
100
|
-
When size limit is exceeded, batch cleanup of old elements starts.
|
101
|
-
|
102
|
-
Args:
|
103
|
-
key: Key for saving
|
104
|
-
response: HTTP response to cache
|
105
|
-
request: Original HTTP request
|
106
|
-
metadata: Cache metadata
|
107
|
-
"""
|
108
|
-
current_time = time.time()
|
109
|
-
|
110
|
-
# Update metadata
|
111
|
-
metadata = metadata.copy()
|
112
|
-
metadata["write_time"] = current_time
|
113
|
-
|
114
|
-
# If element already exists, remove it (it will be added to the end)
|
115
|
-
if key in self._storage:
|
116
|
-
logger.info("Element %s removed from cache - overwrite", key)
|
117
|
-
self._pop_item(key)
|
118
|
-
|
119
|
-
self._storage[key] = (response, request, metadata)
|
120
|
-
|
121
|
-
data_ttl = metadata.get("ttl", self._ttl)
|
122
|
-
if data_ttl is not None:
|
123
|
-
self._expiry_times[key] = current_time + data_ttl
|
124
|
-
|
125
|
-
self._remove_expired_items()
|
126
|
-
|
127
|
-
self._cleanup_lru_items()
|
128
|
-
|
129
|
-
async def retrieve(self, key: str) -> tp.Optional[StoredResponse]:
|
130
|
-
"""Gets response from cache with lazy TTL checking.
|
131
|
-
|
132
|
-
Element moves to the end to update LRU position.
|
133
|
-
Expired elements are automatically removed.
|
134
|
-
|
135
|
-
Args:
|
136
|
-
key: Key to search
|
137
|
-
|
138
|
-
Returns:
|
139
|
-
Tuple (response, request, metadata) if found and not expired, None if not found or expired
|
140
|
-
"""
|
141
|
-
if key not in self._storage:
|
142
|
-
return None
|
143
|
-
|
144
|
-
# Lazy TTL check
|
145
|
-
if self._is_expired(key):
|
146
|
-
self._pop_item(key)
|
147
|
-
logger.debug("Element %s removed from cache - TTL expired", key)
|
148
|
-
return None
|
149
|
-
|
150
|
-
self._storage.move_to_end(key)
|
151
|
-
|
152
|
-
return self._storage[key]
|
153
|
-
|
154
|
-
async def remove(self, path: re.Pattern) -> None:
|
155
|
-
"""Removes responses from cache by request path pattern.
|
156
|
-
|
157
|
-
Args:
|
158
|
-
path: Regular expression for matching request paths
|
159
|
-
"""
|
160
|
-
# Find all keys matching path pattern
|
161
|
-
keys_to_remove = []
|
162
|
-
for key, (_, request, _) in self._storage.items():
|
163
|
-
if path.match(request.url.path):
|
164
|
-
keys_to_remove.append(key)
|
165
|
-
|
166
|
-
# Remove found keys
|
167
|
-
for key in keys_to_remove:
|
168
|
-
self._pop_item(key)
|
169
|
-
|
170
|
-
logger.debug(
|
171
|
-
"Removed %d entries from cache by pattern %s",
|
172
|
-
len(keys_to_remove),
|
173
|
-
path.pattern,
|
174
|
-
)
|
175
|
-
|
176
|
-
async def close(self) -> None:
|
177
|
-
"""Clears storage and frees resources."""
|
178
|
-
self._storage.clear()
|
179
|
-
self._expiry_times.clear()
|
180
|
-
logger.debug("Cache storage cleared")
|
181
|
-
|
182
|
-
def __len__(self) -> int:
|
183
|
-
"""Returns current number of elements in cache."""
|
184
|
-
return len(self._storage)
|
185
|
-
|
186
|
-
def _pop_item(self, key: str) -> StoredResponse | None:
|
187
|
-
"""Removes element from storage and expiry times.
|
188
|
-
|
189
|
-
Args:
|
190
|
-
key: Element key to remove
|
191
|
-
"""
|
192
|
-
self._expiry_times.pop(key, None)
|
193
|
-
return self._storage.pop(key, None)
|
194
|
-
|
195
|
-
def _is_expired(self, key: str) -> bool:
|
196
|
-
"""Checks if element is expired by TTL."""
|
197
|
-
try:
|
198
|
-
return time.time() > self._expiry_times[key]
|
199
|
-
except KeyError:
|
200
|
-
return False
|
201
|
-
|
202
|
-
def _remove_expired_items(self) -> None:
|
203
|
-
"""Removes all expired elements from cache."""
|
204
|
-
current_time = time.time()
|
205
|
-
|
206
|
-
if current_time - self._last_expiry_check_time < self._expiry_check_interval:
|
207
|
-
return
|
208
|
-
|
209
|
-
self._last_expiry_check_time = current_time
|
210
|
-
|
211
|
-
expired_keys = [
|
212
|
-
key
|
213
|
-
for key, expiry_time in self._expiry_times.items()
|
214
|
-
if current_time > expiry_time
|
215
|
-
]
|
216
|
-
if not expired_keys:
|
217
|
-
return
|
218
|
-
|
219
|
-
for key in expired_keys:
|
220
|
-
self._pop_item(key)
|
221
|
-
|
222
|
-
logger.debug("Removed %d expired elements from cache", len(expired_keys))
|
223
|
-
|
224
|
-
def _cleanup_lru_items(self) -> None:
|
225
|
-
"""Removes old elements by LRU strategy when limit is exceeded."""
|
226
|
-
if len(self._storage) <= self._cleanup_threshold:
|
227
|
-
return
|
228
|
-
|
229
|
-
# Remove elements in batches for better performance
|
230
|
-
items_to_remove = min(
|
231
|
-
self._cleanup_batch_size, len(self._storage) - self._max_size
|
232
|
-
)
|
233
|
-
|
234
|
-
for _ in range(items_to_remove):
|
235
|
-
key, _ = self._storage.popitem(last=False) # FIFO
|
236
|
-
self._expiry_times.pop(key, None)
|
237
|
-
|
238
|
-
logger.debug("Removed %d elements from cache by LRU strategy", items_to_remove)
|
1
|
+
import logging
|
2
|
+
import re
|
3
|
+
import time
|
4
|
+
import typing as tp
|
5
|
+
from collections import OrderedDict
|
6
|
+
|
7
|
+
from starlette.requests import Request
|
8
|
+
from starlette.responses import Response
|
9
|
+
from typing_extensions import TypeAlias
|
10
|
+
|
11
|
+
from .exceptions import StorageError
|
12
|
+
from .serializers import BaseSerializer, JSONSerializer, Metadata
|
13
|
+
|
14
|
+
logger = logging.getLogger(__name__)
|
15
|
+
|
16
|
+
# Define type for stored response
|
17
|
+
StoredResponse: TypeAlias = tp.Tuple[Response, Request, Metadata]
|
18
|
+
|
19
|
+
|
20
|
+
# Define base class for cache storage
|
21
|
+
class BaseStorage:
|
22
|
+
"""Base class for cache storage.
|
23
|
+
|
24
|
+
Args:
|
25
|
+
serializer: Serializer for converting Response/Request to string/bytes
|
26
|
+
ttl: Cache lifetime in seconds. None for permanent storage
|
27
|
+
"""
|
28
|
+
|
29
|
+
def __init__(
|
30
|
+
self,
|
31
|
+
serializer: tp.Optional[BaseSerializer] = None,
|
32
|
+
ttl: tp.Optional[tp.Union[int, float]] = None,
|
33
|
+
) -> None:
|
34
|
+
self._serializer = serializer or JSONSerializer()
|
35
|
+
|
36
|
+
if ttl is not None and ttl <= 0:
|
37
|
+
raise StorageError("TTL must be positive")
|
38
|
+
|
39
|
+
self._ttl = ttl
|
40
|
+
|
41
|
+
async def store(
|
42
|
+
self, key: str, response: Response, request: Request, metadata: Metadata
|
43
|
+
) -> None:
|
44
|
+
raise NotImplementedError()
|
45
|
+
|
46
|
+
async def retrieve(self, key: str) -> tp.Optional[StoredResponse]:
|
47
|
+
raise NotImplementedError()
|
48
|
+
|
49
|
+
async def remove(self, path: re.Pattern) -> None:
|
50
|
+
raise NotImplementedError()
|
51
|
+
|
52
|
+
async def close(self) -> None:
|
53
|
+
raise NotImplementedError()
|
54
|
+
|
55
|
+
|
56
|
+
class InMemoryStorage(BaseStorage):
|
57
|
+
"""In-memory cache storage with TTL and LRU eviction support.
|
58
|
+
|
59
|
+
Implements optimized storage of cached responses in memory with:
|
60
|
+
- LRU (Least Recently Used) eviction when max_size is exceeded
|
61
|
+
- TTL (Time To Live) with lazy checking on read
|
62
|
+
- Batch cleanup for better performance
|
63
|
+
|
64
|
+
Args:
|
65
|
+
max_size: Maximum number of cache entries
|
66
|
+
serializer: Serializer not used for InMemoryStorage
|
67
|
+
ttl: Cache lifetime in seconds. None for permanent storage
|
68
|
+
"""
|
69
|
+
|
70
|
+
def __init__(
|
71
|
+
self,
|
72
|
+
max_size: int = 1000,
|
73
|
+
serializer: tp.Optional[BaseSerializer] = None,
|
74
|
+
ttl: tp.Optional[tp.Union[int, float]] = None,
|
75
|
+
) -> None:
|
76
|
+
super().__init__(serializer=serializer, ttl=ttl)
|
77
|
+
|
78
|
+
if max_size <= 0:
|
79
|
+
raise StorageError("Max size must be positive")
|
80
|
+
|
81
|
+
self._max_size = max_size
|
82
|
+
# Cleanup batch size - default 10% of max_size, minimum 1
|
83
|
+
self._cleanup_batch_size = max(1, max_size // 10)
|
84
|
+
# Cleanup threshold - 5% more than max_size
|
85
|
+
self._cleanup_threshold = max_size + max(1, max_size // 20)
|
86
|
+
|
87
|
+
# OrderedDict for efficient LRU
|
88
|
+
self._storage: OrderedDict[str, StoredResponse] = OrderedDict()
|
89
|
+
# Separate expiry time storage for fast TTL checking
|
90
|
+
self._expiry_times: tp.Dict[str, float] = {}
|
91
|
+
self._last_expiry_check_time: float = 0
|
92
|
+
self._expiry_check_interval: float = 60
|
93
|
+
|
94
|
+
async def store(
|
95
|
+
self, key: str, response: Response, request: Request, metadata: Metadata
|
96
|
+
) -> None:
|
97
|
+
"""Saves response to cache with TTL and LRU eviction support.
|
98
|
+
|
99
|
+
If element already exists, it moves to the end (most recently used).
|
100
|
+
When size limit is exceeded, batch cleanup of old elements starts.
|
101
|
+
|
102
|
+
Args:
|
103
|
+
key: Key for saving
|
104
|
+
response: HTTP response to cache
|
105
|
+
request: Original HTTP request
|
106
|
+
metadata: Cache metadata
|
107
|
+
"""
|
108
|
+
current_time = time.time()
|
109
|
+
|
110
|
+
# Update metadata
|
111
|
+
metadata = metadata.copy()
|
112
|
+
metadata["write_time"] = current_time
|
113
|
+
|
114
|
+
# If element already exists, remove it (it will be added to the end)
|
115
|
+
if key in self._storage:
|
116
|
+
logger.info("Element %s removed from cache - overwrite", key)
|
117
|
+
self._pop_item(key)
|
118
|
+
|
119
|
+
self._storage[key] = (response, request, metadata)
|
120
|
+
|
121
|
+
data_ttl = metadata.get("ttl", self._ttl)
|
122
|
+
if data_ttl is not None:
|
123
|
+
self._expiry_times[key] = current_time + data_ttl
|
124
|
+
|
125
|
+
self._remove_expired_items()
|
126
|
+
|
127
|
+
self._cleanup_lru_items()
|
128
|
+
|
129
|
+
async def retrieve(self, key: str) -> tp.Optional[StoredResponse]:
|
130
|
+
"""Gets response from cache with lazy TTL checking.
|
131
|
+
|
132
|
+
Element moves to the end to update LRU position.
|
133
|
+
Expired elements are automatically removed.
|
134
|
+
|
135
|
+
Args:
|
136
|
+
key: Key to search
|
137
|
+
|
138
|
+
Returns:
|
139
|
+
Tuple (response, request, metadata) if found and not expired, None if not found or expired
|
140
|
+
"""
|
141
|
+
if key not in self._storage:
|
142
|
+
return None
|
143
|
+
|
144
|
+
# Lazy TTL check
|
145
|
+
if self._is_expired(key):
|
146
|
+
self._pop_item(key)
|
147
|
+
logger.debug("Element %s removed from cache - TTL expired", key)
|
148
|
+
return None
|
149
|
+
|
150
|
+
self._storage.move_to_end(key)
|
151
|
+
|
152
|
+
return self._storage[key]
|
153
|
+
|
154
|
+
async def remove(self, path: re.Pattern) -> None:
|
155
|
+
"""Removes responses from cache by request path pattern.
|
156
|
+
|
157
|
+
Args:
|
158
|
+
path: Regular expression for matching request paths
|
159
|
+
"""
|
160
|
+
# Find all keys matching path pattern
|
161
|
+
keys_to_remove = []
|
162
|
+
for key, (_, request, _) in self._storage.items():
|
163
|
+
if path.match(request.url.path):
|
164
|
+
keys_to_remove.append(key)
|
165
|
+
|
166
|
+
# Remove found keys
|
167
|
+
for key in keys_to_remove:
|
168
|
+
self._pop_item(key)
|
169
|
+
|
170
|
+
logger.debug(
|
171
|
+
"Removed %d entries from cache by pattern %s",
|
172
|
+
len(keys_to_remove),
|
173
|
+
path.pattern,
|
174
|
+
)
|
175
|
+
|
176
|
+
async def close(self) -> None:
|
177
|
+
"""Clears storage and frees resources."""
|
178
|
+
self._storage.clear()
|
179
|
+
self._expiry_times.clear()
|
180
|
+
logger.debug("Cache storage cleared")
|
181
|
+
|
182
|
+
def __len__(self) -> int:
|
183
|
+
"""Returns current number of elements in cache."""
|
184
|
+
return len(self._storage)
|
185
|
+
|
186
|
+
def _pop_item(self, key: str) -> StoredResponse | None:
|
187
|
+
"""Removes element from storage and expiry times.
|
188
|
+
|
189
|
+
Args:
|
190
|
+
key: Element key to remove
|
191
|
+
"""
|
192
|
+
self._expiry_times.pop(key, None)
|
193
|
+
return self._storage.pop(key, None)
|
194
|
+
|
195
|
+
def _is_expired(self, key: str) -> bool:
|
196
|
+
"""Checks if element is expired by TTL."""
|
197
|
+
try:
|
198
|
+
return time.time() > self._expiry_times[key]
|
199
|
+
except KeyError:
|
200
|
+
return False
|
201
|
+
|
202
|
+
def _remove_expired_items(self) -> None:
|
203
|
+
"""Removes all expired elements from cache."""
|
204
|
+
current_time = time.time()
|
205
|
+
|
206
|
+
if current_time - self._last_expiry_check_time < self._expiry_check_interval:
|
207
|
+
return
|
208
|
+
|
209
|
+
self._last_expiry_check_time = current_time
|
210
|
+
|
211
|
+
expired_keys = [
|
212
|
+
key
|
213
|
+
for key, expiry_time in self._expiry_times.items()
|
214
|
+
if current_time > expiry_time
|
215
|
+
]
|
216
|
+
if not expired_keys:
|
217
|
+
return
|
218
|
+
|
219
|
+
for key in expired_keys:
|
220
|
+
self._pop_item(key)
|
221
|
+
|
222
|
+
logger.debug("Removed %d expired elements from cache", len(expired_keys))
|
223
|
+
|
224
|
+
def _cleanup_lru_items(self) -> None:
|
225
|
+
"""Removes old elements by LRU strategy when limit is exceeded."""
|
226
|
+
if len(self._storage) <= self._cleanup_threshold:
|
227
|
+
return
|
228
|
+
|
229
|
+
# Remove elements in batches for better performance
|
230
|
+
items_to_remove = min(
|
231
|
+
self._cleanup_batch_size, len(self._storage) - self._max_size
|
232
|
+
)
|
233
|
+
|
234
|
+
for _ in range(items_to_remove):
|
235
|
+
key, _ = self._storage.popitem(last=False) # FIFO
|
236
|
+
self._expiry_times.pop(key, None)
|
237
|
+
|
238
|
+
logger.debug("Removed %d elements from cache by LRU strategy", items_to_remove)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: fast-cache-middleware
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.3
|
4
4
|
Summary: Интеллектуальное middleware для кеширования ответов FastAPI
|
5
5
|
License: MIT
|
6
6
|
Author: Your Name
|
@@ -11,7 +11,8 @@ Classifier: Programming Language :: Python :: 3
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.11
|
12
12
|
Classifier: Programming Language :: Python :: 3.12
|
13
13
|
Classifier: Programming Language :: Python :: 3.13
|
14
|
-
Requires-Dist:
|
14
|
+
Requires-Dist: cachetools (>=5.0.0,<7.0.0)
|
15
|
+
Requires-Dist: fastapi (>=0.111.1,<1.0.0)
|
15
16
|
Description-Content-Type: text/markdown
|
16
17
|
|
17
18
|
# FastCacheMiddleware
|
@@ -0,0 +1,11 @@
|
|
1
|
+
fast_cache_middleware/__init__.py,sha256=0wuBPKrzjyBA2K9Fnm-xzC1mYSXCMuYr1uPutNXCI5Y,983
|
2
|
+
fast_cache_middleware/controller.py,sha256=Y0StElopHiBHa-TjLqi1yoldWkcGZubcCgT2xj2zpEI,7181
|
3
|
+
fast_cache_middleware/depends.py,sha256=00ShPXlG0A_Ylj0ypLGU0hgSNeLxHeI3PGi8Fo35wko,1867
|
4
|
+
fast_cache_middleware/exceptions.py,sha256=lv3p2-wAj6UZI7czVSy91eZtuXItr1hZHW2LDz8fI9s,109
|
5
|
+
fast_cache_middleware/middleware.py,sha256=a80t7jL0CgSmtXaEgrZW-7i6IrggNlTsHGyL1Ri8Oxw,9761
|
6
|
+
fast_cache_middleware/schemas.py,sha256=uOceBR4_itlj1NG3xszyLbE96bS_W6t6BGmPPkNkoqA,610
|
7
|
+
fast_cache_middleware/serializers.py,sha256=jf308cez8jVzTikK6BhzHhvuIrGkA8Vdn_Gopd6dfyM,2907
|
8
|
+
fast_cache_middleware/storages.py,sha256=GEeA-I0QvK6M_mAub08npxylLQ_WEBGyrjt9Zhe_r6A,7611
|
9
|
+
fast_cache_middleware-0.0.3.dist-info/METADATA,sha256=GPYuWY3YHPjjqhp2xSp7xgUQgoEmZ33EOjntmSvQkOE,13704
|
10
|
+
fast_cache_middleware-0.0.3.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
11
|
+
fast_cache_middleware-0.0.3.dist-info/RECORD,,
|
@@ -1,11 +0,0 @@
|
|
1
|
-
fast_cache_middleware/__init__.py,sha256=Uk8DeGM0DM75NXv5wl9nw8p2dEdnFLqcowikS_TKPcQ,917
|
2
|
-
fast_cache_middleware/controller.py,sha256=gYcTCUU4jJO-ULX7AEbbYuN7VDcWoH1MmH2GoOnfqfc,7412
|
3
|
-
fast_cache_middleware/depends.py,sha256=CQmvIip7aGr7R-JBQS5n6UwCeAv0ZLnzIHjeGzWuPd0,1934
|
4
|
-
fast_cache_middleware/exceptions.py,sha256=3Be38HYfAS6QD028md5ku2ss7GehfyyXbDaEj91JuA4,115
|
5
|
-
fast_cache_middleware/middleware.py,sha256=llYX0Cz5GhFglxoNLnUyFCUp4ID8qfrUHIbRY7yy23g,9647
|
6
|
-
fast_cache_middleware/schemas.py,sha256=c1GXLs9FK8ollCF0wrgmzi4Ccpi49P1nOsJRL3rg1bk,631
|
7
|
-
fast_cache_middleware/serializers.py,sha256=ieMblO2Or3BdvBH2rOxMC2qabr21bJ-9MFt6y8y5s6c,2999
|
8
|
-
fast_cache_middleware/storages.py,sha256=DtalrNIqvCQrbBLwvicpsH6vfQFwsdDjrTvI329y39s,7849
|
9
|
-
fast_cache_middleware-0.0.1.dist-info/METADATA,sha256=GgU-D0Gwu_NXrrWdrouDKpvFQqkAEPHhnu9qIr_Ye7c,13663
|
10
|
-
fast_cache_middleware-0.0.1.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
11
|
-
fast_cache_middleware-0.0.1.dist-info/RECORD,,
|
File without changes
|