taskiq-redis 1.2.0__tar.gz → 1.2.2__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.
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: taskiq-redis
3
- Version: 1.2.0
3
+ Version: 1.2.2
4
4
  Summary: Redis integration for taskiq
5
- Keywords: taskiq,tasks,distributed,async,redis,result_backend
5
+ Keywords: async,distributed,redis,result_backend,taskiq,tasks
6
6
  Author: Taskiq team
7
7
  Author-email: Taskiq team <taskiq@no-reply.com>
8
8
  License-Expression: MIT
@@ -14,7 +14,7 @@ Classifier: Programming Language :: Python :: 3.10
14
14
  Classifier: Programming Language :: Python :: 3.11
15
15
  Classifier: Programming Language :: Python :: 3.12
16
16
  Classifier: Programming Language :: Python :: 3.13
17
- Requires-Dist: redis>=7.0.0,<7.1.0
17
+ Requires-Dist: redis>=7.0.0,<8
18
18
  Requires-Dist: taskiq>=0.12.0
19
19
  Maintainer: Taskiq team
20
20
  Maintainer-email: Taskiq team <taskiq@no-reply.com>
@@ -0,0 +1,155 @@
1
+ [project]
2
+ name = "taskiq-redis"
3
+ version = "1.2.2"
4
+ description = "Redis integration for taskiq"
5
+ readme = "README.md"
6
+ requires-python = ">=3.10"
7
+ license = "MIT"
8
+ license-files = ["LICENSE"]
9
+ authors = [{ name = "Taskiq team", email = "taskiq@no-reply.com" }]
10
+ maintainers = [{ name = "Taskiq team", email = "taskiq@no-reply.com" }]
11
+ keywords = [
12
+ "async",
13
+ "distributed",
14
+ "redis",
15
+ "result_backend",
16
+ "taskiq",
17
+ "tasks",
18
+ ]
19
+ classifiers = [
20
+ "Programming Language :: Python",
21
+ "Programming Language :: Python :: 3",
22
+ "Programming Language :: Python :: 3 :: Only",
23
+ "Programming Language :: Python :: 3.10",
24
+ "Programming Language :: Python :: 3.11",
25
+ "Programming Language :: Python :: 3.12",
26
+ "Programming Language :: Python :: 3.13",
27
+ ]
28
+ dependencies = [
29
+ "redis>=7.0.0,<8", # TODO: fix issues in tests with 7.1.0
30
+ "taskiq>=0.12.0",
31
+ ]
32
+
33
+ [project.urls]
34
+ homepage = "https://github.com/taskiq-python/taskiq-redis"
35
+ repository = "https://github.com/taskiq-python/taskiq-redis"
36
+
37
+ [dependency-groups]
38
+ dev = [
39
+ "pre-commit>=4.5.0",
40
+ { include-group = "lint" },
41
+ { include-group = "test" },
42
+ ]
43
+ lint = [
44
+ "black>=25.11.0",
45
+ "mypy>=1.19.0",
46
+ "ruff>=0.14.7",
47
+ ]
48
+ test = [
49
+ "fakeredis>=2.32.1",
50
+ "freezegun>=1.5.5",
51
+ "pytest>=9.0.1",
52
+ "pytest-cov>=7.0.0",
53
+ "pytest-env>=1.2.0",
54
+ "pytest-xdist>=3.8.0",
55
+ ]
56
+
57
+ [build-system]
58
+ requires = ["uv_build>=0.9.13,<0.10.0"]
59
+ build-backend = "uv_build"
60
+
61
+ [tool.mypy]
62
+ strict = true
63
+ ignore_missing_imports = true
64
+ allow_subclassing_any = true
65
+ allow_untyped_calls = true
66
+ pretty = true
67
+ show_error_codes = true
68
+ implicit_reexport = true
69
+ allow_untyped_decorators = true
70
+ warn_return_any = false
71
+
72
+ [[tool.mypy.overrides]]
73
+ module = ["redis"]
74
+ ignore_missing_imports = true
75
+ ignore_errors = true
76
+ strict = false
77
+
78
+ [tool.pytest.ini_options]
79
+ filterwarnings = [
80
+ # about deprecated RedisScheduleSource usage - delete after removing RedisScheduleSource
81
+ "ignore:RedisScheduleSource is deprecated:DeprecationWarning",
82
+ ]
83
+
84
+ [tool.ruff]
85
+ # List of enabled rulsets.
86
+ # See https://docs.astral.sh/ruff/rules/ for more information.
87
+ lint.select = [
88
+ "E", # Error
89
+ "F", # Pyflakes
90
+ "W", # Pycodestyle
91
+ "C90", # McCabe complexity
92
+ "I", # Isort
93
+ "N", # pep8-naming
94
+ "D", # Pydocstyle
95
+ "ANN", # Pytype annotations
96
+ "S", # Bandit
97
+ "B", # Bugbear
98
+ "COM", # Commas
99
+ "C4", # Comprehensions
100
+ "ISC", # Implicit string concat
101
+ "PIE", # Unnecessary code
102
+ "T20", # Catch prints
103
+ "PYI", # validate pyi files
104
+ "Q", # Checks for quotes
105
+ "RSE", # Checks raise statements
106
+ "RET", # Checks return statements
107
+ "SLF", # Self checks
108
+ "SIM", # Simplificator
109
+ "PTH", # Pathlib checks
110
+ "ERA", # Checks for commented out code
111
+ "PL", # PyLint checks
112
+ "RUF", # Specific to Ruff checks
113
+ "FA102", # Future annotations
114
+ "UP", # Pyupgrade
115
+ ]
116
+ lint.ignore = [
117
+ "D105", # Missing docstring in magic method
118
+ "D107", # Missing docstring in __init__
119
+ "D212", # Multi-line docstring summary should start at the first line
120
+ "D401", # First line should be in imperative mood
121
+ "D104", # Missing docstring in public package
122
+ "D100", # Missing docstring in public module
123
+ "ANN401", # typing.Any are disallowed in `**kwargs
124
+ "PLR0913", # Too many arguments for function call
125
+ "D106", # Missing docstring in public nested class
126
+ ]
127
+ exclude = [".venv/"]
128
+ line-length = 88
129
+
130
+ [tool.ruff.lint.mccabe]
131
+ max-complexity = 10
132
+
133
+ [tool.ruff.lint.per-file-ignores]
134
+ "tests/*" = [
135
+ "S101", # Use of assert detected
136
+ "S301", # Use of pickle detected
137
+ "D103", # Missing docstring in public function
138
+ "SLF001", # Private member accessed
139
+ "S311", # Standard pseudo-random generators are not suitable for security/cryptographic purposes
140
+ "D101", # Missing docstring in public class
141
+ ]
142
+
143
+ [tool.ruff.lint.pydocstyle]
144
+ convention = "pep257"
145
+ ignore-decorators = ["typing.overload"]
146
+
147
+ [tool.ruff.lint.pylint]
148
+ allow-magic-value-types = ["int", "str", "float"]
149
+
150
+ [tool.ruff.lint.flake8-bugbear]
151
+ extend-immutable-calls = ["taskiq_dependencies.Depends", "taskiq.TaskiqDepends"]
152
+
153
+ [tool.uv.build-backend]
154
+ module-root = ""
155
+ module-name = "taskiq_redis"
@@ -85,6 +85,9 @@ class ListRedisScheduleSource(ScheduleSource):
85
85
  """Get the key for a cron-based schedule."""
86
86
  return f"{self._prefix}:cron"
87
87
 
88
+ def _get_interval_key(self) -> str:
89
+ return f"{self._prefix}:interval"
90
+
88
91
  def _get_data_key(self, schedule_id: str) -> str:
89
92
  """Get the key for a schedule data."""
90
93
  return f"{self._prefix}:data:{schedule_id}"
@@ -150,6 +153,8 @@ class ListRedisScheduleSource(ScheduleSource):
150
153
  elif schedule.time is not None:
151
154
  time_key = self._get_time_key(schedule.time)
152
155
  await redis.lrem(time_key, 0, schedule_id) # type: ignore[misc]
156
+ elif schedule.interval:
157
+ await redis.lrem(self._get_interval_key(), 0, schedule_id) # type: ignore[misc]
153
158
 
154
159
  async def add_schedule(self, schedule: "ScheduledTask") -> None:
155
160
  """Add a schedule to the source."""
@@ -169,6 +174,11 @@ class ListRedisScheduleSource(ScheduleSource):
169
174
  self._get_time_key(schedule.time),
170
175
  schedule.schedule_id,
171
176
  )
177
+ elif schedule.interval:
178
+ await redis.rpush( # type: ignore[misc]
179
+ self._get_interval_key(),
180
+ schedule.schedule_id,
181
+ )
172
182
 
173
183
  async def post_send(self, task: ScheduledTask) -> None:
174
184
  """Delete a task after it's completed."""
@@ -199,6 +209,10 @@ class ListRedisScheduleSource(ScheduleSource):
199
209
  logger.debug("Got %d cron schedules", len(crons))
200
210
  if crons:
201
211
  buffer.extend(crons)
212
+ intervals = await redis.lrange(self._get_interval_key(), 0, -1) # type: ignore[misc]
213
+ logger.debug("Got %d interval schedules", len(intervals))
214
+ if intervals:
215
+ buffer.extend(intervals)
202
216
  timed.extend(await redis.lrange(self._get_time_key(current_time), 0, -1)) # type: ignore[misc]
203
217
  logger.debug("Got %d timed schedules", len(timed))
204
218
  if timed:
@@ -161,6 +161,7 @@ class RedisStreamBroker(BaseRedisBroker):
161
161
  approximate: bool = True,
162
162
  idle_timeout: int = 600000, # 10 minutes
163
163
  unacknowledged_batch_size: int = 100,
164
+ unacknowledged_lock_timeout: float | None = None,
164
165
  xread_count: int | None = 100,
165
166
  additional_streams: dict[str, str | int] | None = None,
166
167
  **connection_kwargs: Any,
@@ -188,8 +189,10 @@ class RedisStreamBroker(BaseRedisBroker):
188
189
  :param xread_count: number of messages to fetch from the stream at once.
189
190
  :param additional_streams: additional streams to read from.
190
191
  Each key is a stream name, value is a consumer id.
191
- :param redeliver_timeout: time in ms to wait before redelivering a message.
192
192
  :param unacknowledged_batch_size: number of unacknowledged messages to fetch.
193
+ :param unacknowledged_lock_timeout: time in seconds before auto-releasing
194
+ the lock. Useful when the worker crashes or gets killed.
195
+ If not set, the lock can remain locked indefinitely.
193
196
  """
194
197
  super().__init__(
195
198
  url,
@@ -209,6 +212,7 @@ class RedisStreamBroker(BaseRedisBroker):
209
212
  self.additional_streams = additional_streams or {}
210
213
  self.idle_timeout = idle_timeout
211
214
  self.unacknowledged_batch_size = unacknowledged_batch_size
215
+ self.unacknowledged_lock_timeout = unacknowledged_lock_timeout
212
216
  self.count = xread_count
213
217
 
214
218
  async def _declare_consumer_group(self) -> None:
@@ -290,6 +294,7 @@ class RedisStreamBroker(BaseRedisBroker):
290
294
  for stream in [self.queue_name, *self.additional_streams.keys()]:
291
295
  lock = redis_conn.lock(
292
296
  f"autoclaim:{self.consumer_group_name}:{stream}",
297
+ timeout=self.unacknowledged_lock_timeout,
293
298
  )
294
299
  if await lock.locked():
295
300
  continue
@@ -1,159 +0,0 @@
1
- [project]
2
- name = "taskiq-redis"
3
- version = "1.2.0"
4
- description = "Redis integration for taskiq"
5
- authors = [
6
- { name = "Taskiq team", email = "taskiq@no-reply.com" }
7
- ]
8
- maintainers = [
9
- { name = "Taskiq team", email = "taskiq@no-reply.com" }
10
- ]
11
- license = "MIT"
12
- license-files = ["LICENSE"]
13
- readme = "README.md"
14
- requires-python = ">=3.10"
15
- classifiers = [
16
- "Programming Language :: Python",
17
- "Programming Language :: Python :: 3",
18
- "Programming Language :: Python :: 3 :: Only",
19
- "Programming Language :: Python :: 3.10",
20
- "Programming Language :: Python :: 3.11",
21
- "Programming Language :: Python :: 3.12",
22
- "Programming Language :: Python :: 3.13",
23
- ]
24
- keywords = [
25
- "taskiq",
26
- "tasks",
27
- "distributed",
28
- "async",
29
- "redis",
30
- "result_backend",
31
- ]
32
- dependencies = [
33
- "redis>=7.0.0,<7.1.0", # TODO: fix issues in tests with 7.1.0
34
- "taskiq>=0.12.0",
35
- ]
36
-
37
- [dependency-groups]
38
- dev = [
39
- {include-group = "lint"},
40
- {include-group = "test"},
41
- "pre-commit>=4.5.0",
42
- ]
43
- test = [
44
- "fakeredis>=2.32.1",
45
- "freezegun>=1.5.5",
46
- "pytest>=9.0.1",
47
- "pytest-cov>=7.0.0",
48
- "pytest-env>=1.2.0",
49
- "pytest-xdist>=3.8.0",
50
- ]
51
- lint = [
52
- "black>=25.11.0",
53
- "mypy>=1.19.0",
54
- "ruff>=0.14.7",
55
- ]
56
-
57
- [project.urls]
58
- homepage = "https://github.com/taskiq-python/taskiq-redis"
59
- repository = "https://github.com/taskiq-python/taskiq-redis"
60
-
61
- [tool.mypy]
62
- strict = true
63
- ignore_missing_imports = true
64
- allow_subclassing_any = true
65
- allow_untyped_calls = true
66
- pretty = true
67
- show_error_codes = true
68
- implicit_reexport = true
69
- allow_untyped_decorators = true
70
- warn_return_any = false
71
-
72
- [[tool.mypy.overrides]]
73
- module = ['redis']
74
- ignore_missing_imports = true
75
- ignore_errors = true
76
- strict = false
77
-
78
- [build-system]
79
- requires = ["uv_build>=0.9.13,<0.10.0"]
80
- build-backend = "uv_build"
81
-
82
- [tool.uv.build-backend]
83
- module-root = ""
84
- module-name = "taskiq_redis"
85
-
86
- [tool.ruff]
87
- # List of enabled rulsets.
88
- # See https://docs.astral.sh/ruff/rules/ for more information.
89
- lint.select = [
90
- "E", # Error
91
- "F", # Pyflakes
92
- "W", # Pycodestyle
93
- "C90", # McCabe complexity
94
- "I", # Isort
95
- "N", # pep8-naming
96
- "D", # Pydocstyle
97
- "ANN", # Pytype annotations
98
- "S", # Bandit
99
- "B", # Bugbear
100
- "COM", # Commas
101
- "C4", # Comprehensions
102
- "ISC", # Implicit string concat
103
- "PIE", # Unnecessary code
104
- "T20", # Catch prints
105
- "PYI", # validate pyi files
106
- "Q", # Checks for quotes
107
- "RSE", # Checks raise statements
108
- "RET", # Checks return statements
109
- "SLF", # Self checks
110
- "SIM", # Simplificator
111
- "PTH", # Pathlib checks
112
- "ERA", # Checks for commented out code
113
- "PL", # PyLint checks
114
- "RUF", # Specific to Ruff checks
115
- "FA102", # Future annotations
116
- "UP", # Pyupgrade
117
- ]
118
- lint.ignore = [
119
- "D105", # Missing docstring in magic method
120
- "D107", # Missing docstring in __init__
121
- "D212", # Multi-line docstring summary should start at the first line
122
- "D401", # First line should be in imperative mood
123
- "D104", # Missing docstring in public package
124
- "D100", # Missing docstring in public module
125
- "ANN401", # typing.Any are disallowed in `**kwargs
126
- "PLR0913", # Too many arguments for function call
127
- "D106", # Missing docstring in public nested class
128
- ]
129
- exclude = [".venv/"]
130
- line-length = 88
131
-
132
- [tool.ruff.lint.mccabe]
133
- max-complexity = 10
134
-
135
- [tool.ruff.lint.per-file-ignores]
136
- "tests/*" = [
137
- "S101", # Use of assert detected
138
- "S301", # Use of pickle detected
139
- "D103", # Missing docstring in public function
140
- "SLF001", # Private member accessed
141
- "S311", # Standard pseudo-random generators are not suitable for security/cryptographic purposes
142
- "D101", # Missing docstring in public class
143
- ]
144
-
145
- [tool.ruff.lint.pydocstyle]
146
- convention = "pep257"
147
- ignore-decorators = ["typing.overload"]
148
-
149
- [tool.ruff.lint.pylint]
150
- allow-magic-value-types = ["int", "str", "float"]
151
-
152
- [tool.ruff.lint.flake8-bugbear]
153
- extend-immutable-calls = ["taskiq_dependencies.Depends", "taskiq.TaskiqDepends"]
154
-
155
- [tool.pytest.ini_options]
156
- filterwarnings = [
157
- # about deprecated RedisScheduleSource usage - delete after removing RedisScheduleSource
158
- 'ignore:RedisScheduleSource is deprecated:DeprecationWarning',
159
- ]
File without changes
File without changes