pycurb 0.2.2__tar.gz → 0.2.3__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 (52) hide show
  1. {pycurb-0.2.2 → pycurb-0.2.3}/PKG-INFO +1 -1
  2. {pycurb-0.2.2 → pycurb-0.2.3}/pyproject.toml +1 -1
  3. {pycurb-0.2.2 → pycurb-0.2.3}/src/pycurb/core/storage/memory.py +8 -2
  4. {pycurb-0.2.2 → pycurb-0.2.3}/src/pycurb/core/storage/memory_async.py +8 -2
  5. {pycurb-0.2.2 → pycurb-0.2.3}/src/pycurb/core/storage/redis.py +7 -2
  6. {pycurb-0.2.2 → pycurb-0.2.3}/src/pycurb/core/storage/redis_async.py +7 -2
  7. {pycurb-0.2.2 → pycurb-0.2.3}/src/pycurb.egg-info/PKG-INFO +1 -1
  8. {pycurb-0.2.2 → pycurb-0.2.3}/LICENSE +0 -0
  9. {pycurb-0.2.2 → pycurb-0.2.3}/README.md +0 -0
  10. {pycurb-0.2.2 → pycurb-0.2.3}/setup.cfg +0 -0
  11. {pycurb-0.2.2 → pycurb-0.2.3}/src/pycurb/__init__.py +0 -0
  12. {pycurb-0.2.2 → pycurb-0.2.3}/src/pycurb/adapters/django/__init__.py +0 -0
  13. {pycurb-0.2.2 → pycurb-0.2.3}/src/pycurb/adapters/django/decorators.py +0 -0
  14. {pycurb-0.2.2 → pycurb-0.2.3}/src/pycurb/adapters/django/extractors.py +0 -0
  15. {pycurb-0.2.2 → pycurb-0.2.3}/src/pycurb/adapters/django/middleware.py +0 -0
  16. {pycurb-0.2.2 → pycurb-0.2.3}/src/pycurb/adapters/fastapi/__init__.py +0 -0
  17. {pycurb-0.2.2 → pycurb-0.2.3}/src/pycurb/adapters/fastapi/dependencies.py +0 -0
  18. {pycurb-0.2.2 → pycurb-0.2.3}/src/pycurb/adapters/fastapi/extractors.py +0 -0
  19. {pycurb-0.2.2 → pycurb-0.2.3}/src/pycurb/adapters/fastapi/middleware.py +0 -0
  20. {pycurb-0.2.2 → pycurb-0.2.3}/src/pycurb/adapters/flask/__init__.py +0 -0
  21. {pycurb-0.2.2 → pycurb-0.2.3}/src/pycurb/adapters/flask/decorators.py +0 -0
  22. {pycurb-0.2.2 → pycurb-0.2.3}/src/pycurb/adapters/flask/extractors.py +0 -0
  23. {pycurb-0.2.2 → pycurb-0.2.3}/src/pycurb/adapters/flask/middleware.py +0 -0
  24. {pycurb-0.2.2 → pycurb-0.2.3}/src/pycurb/core/__init__.py +0 -0
  25. {pycurb-0.2.2 → pycurb-0.2.3}/src/pycurb/core/algorithms/__init__.py +0 -0
  26. {pycurb-0.2.2 → pycurb-0.2.3}/src/pycurb/core/algorithms/base.py +0 -0
  27. {pycurb-0.2.2 → pycurb-0.2.3}/src/pycurb/core/algorithms/base_async.py +0 -0
  28. {pycurb-0.2.2 → pycurb-0.2.3}/src/pycurb/core/algorithms/fixed_window.py +0 -0
  29. {pycurb-0.2.2 → pycurb-0.2.3}/src/pycurb/core/algorithms/fixed_window_async.py +0 -0
  30. {pycurb-0.2.2 → pycurb-0.2.3}/src/pycurb/core/algorithms/gcra.py +0 -0
  31. {pycurb-0.2.2 → pycurb-0.2.3}/src/pycurb/core/algorithms/gcra_async.py +0 -0
  32. {pycurb-0.2.2 → pycurb-0.2.3}/src/pycurb/core/algorithms/leaky_bucket.py +0 -0
  33. {pycurb-0.2.2 → pycurb-0.2.3}/src/pycurb/core/algorithms/leaky_bucket_async.py +0 -0
  34. {pycurb-0.2.2 → pycurb-0.2.3}/src/pycurb/core/algorithms/sliding_window.py +0 -0
  35. {pycurb-0.2.2 → pycurb-0.2.3}/src/pycurb/core/algorithms/sliding_window_async.py +0 -0
  36. {pycurb-0.2.2 → pycurb-0.2.3}/src/pycurb/core/algorithms/token_bucket.py +0 -0
  37. {pycurb-0.2.2 → pycurb-0.2.3}/src/pycurb/core/algorithms/token_bucket_async.py +0 -0
  38. {pycurb-0.2.2 → pycurb-0.2.3}/src/pycurb/core/decorators.py +0 -0
  39. {pycurb-0.2.2 → pycurb-0.2.3}/src/pycurb/core/limiter.py +0 -0
  40. {pycurb-0.2.2 → pycurb-0.2.3}/src/pycurb/core/limiter_async.py +0 -0
  41. {pycurb-0.2.2 → pycurb-0.2.3}/src/pycurb/core/models.py +0 -0
  42. {pycurb-0.2.2 → pycurb-0.2.3}/src/pycurb/core/resolver.py +0 -0
  43. {pycurb-0.2.2 → pycurb-0.2.3}/src/pycurb/core/storage/__init__.py +0 -0
  44. {pycurb-0.2.2 → pycurb-0.2.3}/src/pycurb/core/storage/base.py +0 -0
  45. {pycurb-0.2.2 → pycurb-0.2.3}/src/pycurb/core/storage/base_async.py +0 -0
  46. {pycurb-0.2.2 → pycurb-0.2.3}/src/pycurb/py.typed +0 -0
  47. {pycurb-0.2.2 → pycurb-0.2.3}/src/pycurb/utils.py +0 -0
  48. {pycurb-0.2.2 → pycurb-0.2.3}/src/pycurb.egg-info/SOURCES.txt +0 -0
  49. {pycurb-0.2.2 → pycurb-0.2.3}/src/pycurb.egg-info/dependency_links.txt +0 -0
  50. {pycurb-0.2.2 → pycurb-0.2.3}/src/pycurb.egg-info/requires.txt +0 -0
  51. {pycurb-0.2.2 → pycurb-0.2.3}/src/pycurb.egg-info/top_level.txt +0 -0
  52. {pycurb-0.2.2 → pycurb-0.2.3}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pycurb
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: A framework-agnostic Python rate limit library
5
5
  Author-email: Oluwaseun Ogunsanu <ogunsanu17@gmail.com>
6
6
  License-Expression: Apache-2.0
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pycurb"
7
- version = "0.2.2"
7
+ version = "0.2.3"
8
8
  description = "A framework-agnostic Python rate limit library"
9
9
  readme = "README.md"
10
10
  license = "Apache-2.0"
@@ -79,7 +79,9 @@ class MemoryStorage(Storage):
79
79
  with lock:
80
80
  tokens, last_refill_time = self._token.get(key, (float(capacity), now))
81
81
 
82
- time_elapsed = now - last_refill_time
82
+ # Clamp elapsed at zero: a clock regression must not silently
83
+ # refill tokens (negative elapsed would drain them).
84
+ time_elapsed = max(now - last_refill_time, 0.0)
83
85
  tokens_during_elapsed = time_elapsed * refill_rate
84
86
 
85
87
  new_tokens = min(tokens + tokens_during_elapsed, float(capacity))
@@ -102,7 +104,11 @@ class MemoryStorage(Storage):
102
104
  with lock:
103
105
  queue_size, last_leak_time = self._leaky.get(key, (0, now))
104
106
 
105
- leaked = math.floor((now - last_leak_time) * leak_rate)
107
+ # Clamp elapsed at zero: with negative elapsed, floor() of a
108
+ # negative number yields -1 and the queue would silently grow
109
+ # (queue - (-1)) on every clock regression.
110
+ elapsed = max(now - last_leak_time, 0.0)
111
+ leaked = math.floor(elapsed * leak_rate)
106
112
  new_queue = max(queue_size - leaked, 0)
107
113
 
108
114
  # Preserve fractional leak tracking history
@@ -79,7 +79,9 @@ class AsyncMemoryStorage(AsyncStorage):
79
79
  async with lock:
80
80
  tokens, last_refill_time = self._token.get(key, (float(capacity), now))
81
81
 
82
- time_elapsed = now - last_refill_time
82
+ # Clamp elapsed at zero: a clock regression must not silently
83
+ # refill tokens (negative elapsed would drain them).
84
+ time_elapsed = max(now - last_refill_time, 0.0)
83
85
  tokens_during_elapsed = time_elapsed * refill_rate
84
86
 
85
87
  new_tokens = min(tokens + tokens_during_elapsed, float(capacity))
@@ -102,7 +104,11 @@ class AsyncMemoryStorage(AsyncStorage):
102
104
  async with lock:
103
105
  queue_size, last_leak_time = self._leaky.get(key, (0, now))
104
106
 
105
- leaked = math.floor((now - last_leak_time) * leak_rate)
107
+ # Clamp elapsed at zero: with negative elapsed, floor() of a
108
+ # negative number yields -1 and the queue would silently grow
109
+ # (queue - (-1)) on every clock regression.
110
+ elapsed = max(now - last_leak_time, 0.0)
111
+ leaked = math.floor(elapsed * leak_rate)
106
112
  new_queue = max(queue_size - leaked, 0)
107
113
 
108
114
  # Preserve fractional leak tracking history
@@ -184,7 +184,9 @@ class RedisStorage(Storage):
184
184
  last_refill = now
185
185
  end
186
186
 
187
- local elapsed = now - last_refill
187
+ -- Clamp elapsed at zero: a clock regression or out-of-order
188
+ -- timestamp must not silently refill tokens.
189
+ local elapsed = math.max(now - last_refill, 0)
188
190
  local tokens_during_elapsed = elapsed * rate
189
191
  local new_tokens = math.min(capacity, tokens + tokens_during_elapsed)
190
192
 
@@ -249,7 +251,10 @@ class RedisStorage(Storage):
249
251
  last_leak = now
250
252
  end
251
253
 
252
- local elapsed = now - last_leak
254
+ -- Clamp elapsed at zero: with negative elapsed, floor() of a
255
+ -- negative number yields -1 and the queue would silently grow
256
+ -- (queue - (-1)) on every clock regression.
257
+ local elapsed = math.max(now - last_leak, 0)
253
258
  local leaked = math.floor(elapsed * rate)
254
259
  local new_queue = math.max(queue_size - leaked, 0)
255
260
 
@@ -188,7 +188,9 @@ class AsyncRedisStorage(AsyncStorage):
188
188
  last_refill = now
189
189
  end
190
190
 
191
- local elapsed = now - last_refill
191
+ -- Clamp elapsed at zero: a clock regression or out-of-order
192
+ -- timestamp must not silently refill tokens.
193
+ local elapsed = math.max(now - last_refill, 0)
192
194
  local tokens_during_elapsed = elapsed * rate
193
195
  local new_tokens = math.min(capacity, tokens + tokens_during_elapsed)
194
196
 
@@ -257,7 +259,10 @@ class AsyncRedisStorage(AsyncStorage):
257
259
  last_leak = now
258
260
  end
259
261
 
260
- local elapsed = now - last_leak
262
+ -- Clamp elapsed at zero: with negative elapsed, floor() of a
263
+ -- negative number yields -1 and the queue would silently grow
264
+ -- (queue - (-1)) on every clock regression.
265
+ local elapsed = math.max(now - last_leak, 0)
261
266
  local leaked = math.floor(elapsed * rate)
262
267
  local new_queue = math.max(queue_size - leaked, 0)
263
268
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pycurb
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: A framework-agnostic Python rate limit library
5
5
  Author-email: Oluwaseun Ogunsanu <ogunsanu17@gmail.com>
6
6
  License-Expression: Apache-2.0
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes