pydocket 0.7.0__py3-none-any.whl → 0.7.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/annotations.py CHANGED
@@ -2,6 +2,8 @@ import abc
2
2
  import inspect
3
3
  from typing import Any, Iterable, Mapping, Self
4
4
 
5
+ from .instrumentation import CACHE_SIZE
6
+
5
7
 
6
8
  class Annotation(abc.ABC):
7
9
  _cache: dict[tuple[type[Self], inspect.Signature], Mapping[str, Self]] = {}
@@ -10,6 +12,7 @@ class Annotation(abc.ABC):
10
12
  def annotated_parameters(cls, signature: inspect.Signature) -> Mapping[str, Self]:
11
13
  key = (cls, signature)
12
14
  if key in cls._cache:
15
+ CACHE_SIZE.set(len(cls._cache), {"cache": "annotation"})
13
16
  return cls._cache[key]
14
17
 
15
18
  annotated: dict[str, Self] = {}
@@ -30,6 +33,7 @@ class Annotation(abc.ABC):
30
33
  annotated[param_name] = arg_type()
31
34
 
32
35
  cls._cache[key] = annotated
36
+ CACHE_SIZE.set(len(cls._cache), {"cache": "annotation"})
33
37
  return annotated
34
38
 
35
39
 
docket/cli.py CHANGED
@@ -358,6 +358,32 @@ def strike(
358
358
  asyncio.run(run())
359
359
 
360
360
 
361
+ @app.command(help="Clear all pending and scheduled tasks from the docket")
362
+ def clear(
363
+ docket_: Annotated[
364
+ str,
365
+ typer.Option(
366
+ "--docket",
367
+ help="The name of the docket",
368
+ envvar="DOCKET_NAME",
369
+ ),
370
+ ] = "docket",
371
+ url: Annotated[
372
+ str,
373
+ typer.Option(
374
+ help="The URL of the Redis server",
375
+ envvar="DOCKET_URL",
376
+ ),
377
+ ] = "redis://localhost:6379/0",
378
+ ) -> None:
379
+ async def run() -> None:
380
+ async with Docket(name=docket_, url=url) as docket:
381
+ cleared_count = await docket.clear()
382
+ print(f"Cleared {cleared_count} tasks from docket '{docket_}'")
383
+
384
+ asyncio.run(run())
385
+
386
+
361
387
  @app.command(help="Restores a task or parameters to the Docket")
362
388
  def restore(
363
389
  function: Annotated[
docket/dependencies.py CHANGED
@@ -21,6 +21,7 @@ from typing import (
21
21
 
22
22
  from .docket import Docket
23
23
  from .execution import Execution, TaskFunction, get_signature
24
+ from .instrumentation import CACHE_SIZE
24
25
 
25
26
  if TYPE_CHECKING: # pragma: no cover
26
27
  from .worker import Worker
@@ -415,6 +416,7 @@ def get_dependency_parameters(
415
416
  function: TaskFunction | DependencyFunction[Any],
416
417
  ) -> dict[str, Dependency]:
417
418
  if function in _parameter_cache:
419
+ CACHE_SIZE.set(len(_parameter_cache), {"cache": "parameter"})
418
420
  return _parameter_cache[function]
419
421
 
420
422
  dependencies: dict[str, Dependency] = {}
@@ -428,6 +430,7 @@ def get_dependency_parameters(
428
430
  dependencies[parameter] = param.default
429
431
 
430
432
  _parameter_cache[function] = dependencies
433
+ CACHE_SIZE.set(len(_parameter_cache), {"cache": "parameter"})
431
434
  return dependencies
432
435
 
433
436
 
docket/docket.py CHANGED
@@ -743,3 +743,46 @@ class Docket:
743
743
  workers.append(WorkerInfo(worker_name, last_seen, task_names))
744
744
 
745
745
  return workers
746
+
747
+ async def clear(self) -> int:
748
+ """Clear all pending and scheduled tasks from the docket.
749
+
750
+ This removes all tasks from the stream (immediate tasks) and queue
751
+ (scheduled tasks), along with their associated parked data. Running
752
+ tasks are not affected.
753
+
754
+ Returns:
755
+ The total number of tasks that were cleared.
756
+ """
757
+ with tracer.start_as_current_span(
758
+ "docket.clear",
759
+ attributes=self.labels(),
760
+ ):
761
+ async with self.redis() as redis:
762
+ async with redis.pipeline() as pipeline:
763
+ # Get counts before clearing
764
+ pipeline.xlen(self.stream_key)
765
+ pipeline.zcard(self.queue_key)
766
+ pipeline.zrange(self.queue_key, 0, -1)
767
+
768
+ stream_count: int
769
+ queue_count: int
770
+ scheduled_keys: list[bytes]
771
+ stream_count, queue_count, scheduled_keys = await pipeline.execute()
772
+
773
+ # Clear all data
774
+ # Trim stream to 0 messages instead of deleting it to preserve consumer group
775
+ if stream_count > 0:
776
+ pipeline.xtrim(self.stream_key, maxlen=0, approximate=False)
777
+ pipeline.delete(self.queue_key)
778
+
779
+ # Clear parked task data and known task keys
780
+ for key_bytes in scheduled_keys:
781
+ key = key_bytes.decode()
782
+ pipeline.delete(self.parked_task_key(key))
783
+ pipeline.delete(self.known_task_key(key))
784
+
785
+ await pipeline.execute()
786
+
787
+ total_cleared = stream_count + queue_count
788
+ return total_cleared
docket/execution.py CHANGED
@@ -19,7 +19,7 @@ import opentelemetry.context
19
19
  from opentelemetry import propagate, trace
20
20
 
21
21
  from .annotations import Logged
22
- from .instrumentation import message_getter
22
+ from .instrumentation import CACHE_SIZE, message_getter
23
23
 
24
24
  logger: logging.Logger = logging.getLogger(__name__)
25
25
 
@@ -32,10 +32,12 @@ _signature_cache: dict[Callable[..., Any], inspect.Signature] = {}
32
32
 
33
33
  def get_signature(function: Callable[..., Any]) -> inspect.Signature:
34
34
  if function in _signature_cache:
35
+ CACHE_SIZE.set(len(_signature_cache), {"cache": "signature"})
35
36
  return _signature_cache[function]
36
37
 
37
38
  signature = inspect.signature(function)
38
39
  _signature_cache[function] = signature
40
+ CACHE_SIZE.set(len(_signature_cache), {"cache": "signature"})
39
41
  return signature
40
42
 
41
43
 
docket/instrumentation.py CHANGED
@@ -117,6 +117,12 @@ SCHEDULE_DEPTH = meter.create_gauge(
117
117
  unit="1",
118
118
  )
119
119
 
120
+ CACHE_SIZE = meter.create_gauge(
121
+ "docket_cache_size",
122
+ description="Size of internal docket caches",
123
+ unit="1",
124
+ )
125
+
120
126
  Message = dict[bytes, bytes]
121
127
 
122
128
 
docket/worker.py CHANGED
@@ -15,7 +15,7 @@ from typing import (
15
15
  )
16
16
 
17
17
  from opentelemetry import trace
18
- from opentelemetry.trace import Tracer
18
+ from opentelemetry.trace import Status, StatusCode, Tracer
19
19
  from redis.asyncio import Redis
20
20
  from redis.exceptions import ConnectionError, LockError
21
21
 
@@ -531,7 +531,7 @@ class Worker:
531
531
  "code.function.name": execution.function.__name__,
532
532
  },
533
533
  links=execution.incoming_span_links(),
534
- ):
534
+ ) as span:
535
535
  try:
536
536
  async with resolved_dependencies(self, execution) as dependencies:
537
537
  # Preemptively reschedule the perpetual task for the future, or clear
@@ -576,6 +576,8 @@ class Worker:
576
576
  duration = log_context["duration"] = time.time() - start
577
577
  TASKS_SUCCEEDED.add(1, counter_labels)
578
578
 
579
+ span.set_status(Status(StatusCode.OK))
580
+
579
581
  rescheduled = await self._perpetuate_if_requested(
580
582
  execution, dependencies, timedelta(seconds=duration)
581
583
  )
@@ -584,10 +586,13 @@ class Worker:
584
586
  logger.info(
585
587
  "%s [%s] %s", arrow, ms(duration), call, extra=log_context
586
588
  )
587
- except Exception:
589
+ except Exception as e:
588
590
  duration = log_context["duration"] = time.time() - start
589
591
  TASKS_FAILED.add(1, counter_labels)
590
592
 
593
+ span.record_exception(e)
594
+ span.set_status(Status(StatusCode.ERROR, str(e)))
595
+
591
596
  retried = await self._retry_if_requested(execution, dependencies)
592
597
  if not retried:
593
598
  retried = await self._perpetuate_if_requested(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pydocket
3
- Version: 0.7.0
3
+ Version: 0.7.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
@@ -0,0 +1,16 @@
1
+ docket/__init__.py,sha256=sY1T_NVsXQNOmOhOnfYmZ95dcE_52Ov6DSIVIMZp-1w,869
2
+ docket/__main__.py,sha256=wcCrL4PjG51r5wVKqJhcoJPTLfHW0wNbD31DrUN0MWI,28
3
+ docket/annotations.py,sha256=wttix9UOeMFMAWXAIJUfUw5GjESJZsACb4YXJCozP7Q,2348
4
+ docket/cli.py,sha256=XG_mbjcqNRO0F0hh6l3AwH9bIZv9xJofZaeaAj9nChc,21608
5
+ docket/dependencies.py,sha256=GBwyEY198JFrfm7z5GkLbd84hv7sJktKBMJXv4veWig,17007
6
+ docket/docket.py,sha256=Cw7QB1d0eDwSgwn0Rj26WjFsXSe7MJtfsUBBHGalL7A,26262
7
+ docket/execution.py,sha256=r_2RGC1qhtAcBUg7E6wewLEgftrf3hIxNbH0HnYPbek,14961
8
+ docket/instrumentation.py,sha256=ogvzrfKbWsdPGfdg4hByH3_r5d3b5AwwQkSrmXw0hRg,5492
9
+ docket/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ docket/tasks.py,sha256=RIlSM2omh-YDwVnCz6M5MtmK8T_m_s1w2OlRRxDUs6A,1437
11
+ docket/worker.py,sha256=CY5Z9p8FZw-6WUwp7Ws4A0V7IFTmonSnBmYP-Cp8Fdw,28079
12
+ pydocket-0.7.1.dist-info/METADATA,sha256=00KHm5Er2R6dmjHLTYBUF13kKAeCRPHmDTdAcv5oRcQ,5335
13
+ pydocket-0.7.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
14
+ pydocket-0.7.1.dist-info/entry_points.txt,sha256=4WOk1nUlBsUT5O3RyMci2ImuC5XFswuopElYcLHtD5k,47
15
+ pydocket-0.7.1.dist-info/licenses/LICENSE,sha256=YuVWU_ZXO0K_k2FG8xWKe5RGxV24AhJKTvQmKfqXuyk,1087
16
+ pydocket-0.7.1.dist-info/RECORD,,
@@ -1,16 +0,0 @@
1
- docket/__init__.py,sha256=sY1T_NVsXQNOmOhOnfYmZ95dcE_52Ov6DSIVIMZp-1w,869
2
- docket/__main__.py,sha256=wcCrL4PjG51r5wVKqJhcoJPTLfHW0wNbD31DrUN0MWI,28
3
- docket/annotations.py,sha256=SFBrOMbpAh7P67u8fRTH-u3MVvJQxe0qYi92WAShAsw,2173
4
- docket/cli.py,sha256=WPm_URZ54h8gHjrsHKP8SXpRzdeepmyH_FhQHai-Qus,20899
5
- docket/dependencies.py,sha256=fX4vafGjQf7s4x0YROaw7fzQPlYW7TZtCqNhu7Kxj40,16831
6
- docket/docket.py,sha256=5e101CGLZ2tWNcADo4cdewapmXab47ieMCeQr6d92YQ,24478
7
- docket/execution.py,sha256=6KozjnS96byvyCMTQ2-IkcIrPsqaPIVu2HZU0U4Be9E,14813
8
- docket/instrumentation.py,sha256=f-GG5VS6EdS2It30qxjVpzWUBOZQcTnat-3KzPwwDgQ,5367
9
- docket/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
- docket/tasks.py,sha256=RIlSM2omh-YDwVnCz6M5MtmK8T_m_s1w2OlRRxDUs6A,1437
11
- docket/worker.py,sha256=tJfk2rlHODzHaWBzpBXT8h-Lo7RDQ6gb6HU8b3T9gFA,27878
12
- pydocket-0.7.0.dist-info/METADATA,sha256=soXf7ybhgvSykxRDH56pMJX2DaXf3SJfDFUFLbebAvM,5335
13
- pydocket-0.7.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
14
- pydocket-0.7.0.dist-info/entry_points.txt,sha256=4WOk1nUlBsUT5O3RyMci2ImuC5XFswuopElYcLHtD5k,47
15
- pydocket-0.7.0.dist-info/licenses/LICENSE,sha256=YuVWU_ZXO0K_k2FG8xWKe5RGxV24AhJKTvQmKfqXuyk,1087
16
- pydocket-0.7.0.dist-info/RECORD,,