pydocket 0.3.0__py3-none-any.whl → 0.3.1__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.

Potentially problematic release.


This version of pydocket might be problematic. Click here for more details.

docket/execution.py CHANGED
@@ -183,18 +183,33 @@ TaskStrikes = dict[str, ParameterStrikes]
183
183
  class StrikeList:
184
184
  task_strikes: TaskStrikes
185
185
  parameter_strikes: ParameterStrikes
186
+ _conditions: list[Callable[[Execution], bool]]
186
187
 
187
188
  def __init__(self) -> None:
188
189
  self.task_strikes = {}
189
190
  self.parameter_strikes = {}
191
+ self._conditions = [self._matches_task_or_parameter_strike]
192
+
193
+ def add_condition(self, condition: Callable[[Execution], bool]) -> None:
194
+ """Adds a temporary condition that indicates an execution is stricken."""
195
+ self._conditions.insert(0, condition)
196
+
197
+ def remove_condition(self, condition: Callable[[Execution], bool]) -> None:
198
+ """Adds a temporary condition that indicates an execution is stricken."""
199
+ assert condition is not self._matches_task_or_parameter_strike
200
+ self._conditions.remove(condition)
190
201
 
191
202
  def is_stricken(self, execution: Execution) -> bool:
192
203
  """
193
- Checks if an execution is stricken based on task name or parameter values.
204
+ Checks if an execution is stricken based on task, parameter, or temporary
205
+ conditions.
194
206
 
195
207
  Returns:
196
208
  bool: True if the execution is stricken, False otherwise.
197
209
  """
210
+ return any(condition(execution) for condition in self._conditions)
211
+
212
+ def _matches_task_or_parameter_strike(self, execution: Execution) -> bool:
198
213
  function_name = execution.function.__name__
199
214
 
200
215
  # Check if the entire task is stricken (without parameter conditions)
docket/worker.py CHANGED
@@ -7,7 +7,6 @@ from types import TracebackType
7
7
  from typing import (
8
8
  TYPE_CHECKING,
9
9
  Any,
10
- Callable,
11
10
  Mapping,
12
11
  Protocol,
13
12
  Self,
@@ -69,7 +68,6 @@ class Worker:
69
68
  redelivery_timeout: timedelta
70
69
  reconnection_delay: timedelta
71
70
  minimum_check_interval: timedelta
72
- _strike_conditions: list[Callable[[Execution], bool]] = []
73
71
 
74
72
  def __init__(
75
73
  self,
@@ -87,13 +85,9 @@ class Worker:
87
85
  self.reconnection_delay = reconnection_delay
88
86
  self.minimum_check_interval = minimum_check_interval
89
87
 
90
- self._strike_conditions = [
91
- docket.strike_list.is_stricken,
92
- ]
93
-
94
88
  async def __aenter__(self) -> Self:
95
89
  self._heartbeat_task = asyncio.create_task(self._heartbeat())
96
-
90
+ self._execution_counts = {}
97
91
  return self
98
92
 
99
93
  async def __aexit__(
@@ -102,6 +96,8 @@ class Worker:
102
96
  exc_value: BaseException | None,
103
97
  traceback: TracebackType | None,
104
98
  ) -> None:
99
+ del self._execution_counts
100
+
105
101
  self._heartbeat_task.cancel()
106
102
  try:
107
103
  await self._heartbeat_task
@@ -162,6 +158,8 @@ class Worker:
162
158
  """Run the worker indefinitely."""
163
159
  return await self._run(forever=True) # pragma: no cover
164
160
 
161
+ _execution_counts: dict[str, int]
162
+
165
163
  async def run_at_most(self, iterations_by_key: Mapping[str, int]) -> None:
166
164
  """
167
165
  Run the worker until there are no more tasks to process, but limit specified
@@ -173,23 +171,25 @@ class Worker:
173
171
  Args:
174
172
  iterations_by_key: Maps task keys to their maximum allowed executions
175
173
  """
176
- execution_counts: dict[str, int] = {key: 0 for key in iterations_by_key}
174
+ self._execution_counts = {key: 0 for key in iterations_by_key}
177
175
 
178
176
  def has_reached_max_iterations(execution: Execution) -> bool:
179
- if execution.key not in iterations_by_key:
177
+ key = execution.key
178
+
179
+ if key not in iterations_by_key:
180
180
  return False
181
181
 
182
- if execution_counts[execution.key] >= iterations_by_key[execution.key]:
182
+ if self._execution_counts[key] >= iterations_by_key[key]:
183
183
  return True
184
184
 
185
- execution_counts[execution.key] += 1
186
185
  return False
187
186
 
188
- self._strike_conditions.insert(0, has_reached_max_iterations)
187
+ self.docket.strike_list.add_condition(has_reached_max_iterations)
189
188
  try:
190
189
  await self.run_until_finished()
191
190
  finally:
192
- self._strike_conditions.remove(has_reached_max_iterations)
191
+ self.docket.strike_list.remove_condition(has_reached_max_iterations)
192
+ self._execution_counts = {}
193
193
 
194
194
  async def _run(self, forever: bool = False) -> None:
195
195
  logger.info("Starting worker %r with the following tasks:", self.name)
@@ -380,12 +380,15 @@ class Worker:
380
380
  arrow = "↬" if execution.attempt > 1 else "↪"
381
381
  call = execution.call_repr()
382
382
 
383
- if any(condition(execution) for condition in self._strike_conditions):
383
+ if self.docket.strike_list.is_stricken(execution):
384
384
  arrow = "🗙"
385
385
  logger.warning("%s %s", arrow, call, extra=log_context)
386
386
  TASKS_STRICKEN.add(1, counter_labels | {"docket.where": "worker"})
387
387
  return
388
388
 
389
+ if execution.key in self._execution_counts:
390
+ self._execution_counts[execution.key] += 1
391
+
389
392
  dependencies = self._get_dependencies(execution)
390
393
 
391
394
  context = propagate.extract(message, getter=message_getter)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pydocket
3
- Version: 0.3.0
3
+ Version: 0.3.1
4
4
  Summary: A distributed background task system for Python functions
5
5
  Project-URL: Homepage, https://github.com/chrisguidry/docket
6
6
  Project-URL: Bug Tracker, https://github.com/chrisguidry/docket/issues
@@ -4,13 +4,13 @@ docket/annotations.py,sha256=GZwOPtPXyeIhnsLh3TQMBnXrjtTtSmF4Ratv4vjPx8U,950
4
4
  docket/cli.py,sha256=EseF0Sj7IEgd9QDC-FSbHSffvF7DNsrmDGYGgZBdJc8,19413
5
5
  docket/dependencies.py,sha256=S3KqXxEF0Q2t_jO3R-kI5IIA3M-tqybtiSod2xnRO4o,4991
6
6
  docket/docket.py,sha256=zva6ofTm7i5hRwAaAnNtlgIqoMPaNLqCTs2PXGka_8s,19723
7
- docket/execution.py,sha256=ShP8MoLmxEslk2pAuhKi6KEEKbHdneyQukR9oQwXdjQ,11732
7
+ docket/execution.py,sha256=PDrlAr8VzmB6JvqKO71YhXUcTcGQW7eyXrSKiTcAexE,12508
8
8
  docket/instrumentation.py,sha256=bZlGA02JoJcY0J1WGm5_qXDfY0AXKr0ZLAYu67wkeKY,4611
9
9
  docket/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
10
  docket/tasks.py,sha256=RIlSM2omh-YDwVnCz6M5MtmK8T_m_s1w2OlRRxDUs6A,1437
11
- docket/worker.py,sha256=A0jfi6f2QZ2OA5z9rVvEcstSiIAQPrxaJmKkFBHH48g,21752
12
- pydocket-0.3.0.dist-info/METADATA,sha256=O6NoNE03rUVEMokkKArLaH6_sXhnrx-kWTnVBN8h5Ak,13092
13
- pydocket-0.3.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
14
- pydocket-0.3.0.dist-info/entry_points.txt,sha256=4WOk1nUlBsUT5O3RyMci2ImuC5XFswuopElYcLHtD5k,47
15
- pydocket-0.3.0.dist-info/licenses/LICENSE,sha256=YuVWU_ZXO0K_k2FG8xWKe5RGxV24AhJKTvQmKfqXuyk,1087
16
- pydocket-0.3.0.dist-info/RECORD,,
11
+ docket/worker.py,sha256=AO5k7rn5xHLGN0L6vckQuMyt_LSHh01me_LqNtsobtc,21786
12
+ pydocket-0.3.1.dist-info/METADATA,sha256=tIHiKQjQ-to6bp437vH3-2LsUPnfUP-iG61FAcQa2GE,13092
13
+ pydocket-0.3.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
14
+ pydocket-0.3.1.dist-info/entry_points.txt,sha256=4WOk1nUlBsUT5O3RyMci2ImuC5XFswuopElYcLHtD5k,47
15
+ pydocket-0.3.1.dist-info/licenses/LICENSE,sha256=YuVWU_ZXO0K_k2FG8xWKe5RGxV24AhJKTvQmKfqXuyk,1087
16
+ pydocket-0.3.1.dist-info/RECORD,,