offwork 0.4.0__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.
Files changed (42) hide show
  1. offwork/__init__.py +167 -0
  2. offwork/__main__.py +770 -0
  3. offwork/_venv.py +174 -0
  4. offwork/core/__init__.py +15 -0
  5. offwork/core/errors.py +83 -0
  6. offwork/core/models.py +174 -0
  7. offwork/core/pairing.py +389 -0
  8. offwork/core/progress.py +91 -0
  9. offwork/core/signing.py +91 -0
  10. offwork/core/task.py +520 -0
  11. offwork/core/token.py +184 -0
  12. offwork/core/version.py +10 -0
  13. offwork/graph/__init__.py +5 -0
  14. offwork/graph/analyzer.py +637 -0
  15. offwork/graph/decorator.py +87 -0
  16. offwork/graph/graph.py +995 -0
  17. offwork/graph/store.py +500 -0
  18. offwork/graph/tracing.py +429 -0
  19. offwork/py.typed +0 -0
  20. offwork/typing.py +48 -0
  21. offwork/worker/__init__.py +18 -0
  22. offwork/worker/backends/__init__.py +3 -0
  23. offwork/worker/backends/base.py +149 -0
  24. offwork/worker/backends/http.py +237 -0
  25. offwork/worker/backends/local.py +452 -0
  26. offwork/worker/backends/rabbitmq.py +410 -0
  27. offwork/worker/backends/redis.py +175 -0
  28. offwork/worker/deps.py +365 -0
  29. offwork/worker/remote.py +793 -0
  30. offwork/worker/result.py +276 -0
  31. offwork/worker/sandbox/Dockerfile +24 -0
  32. offwork/worker/sandbox/__init__.py +18 -0
  33. offwork/worker/sandbox/_protocol.py +50 -0
  34. offwork/worker/sandbox/docker.py +438 -0
  35. offwork/worker/sandbox/guest_agent.py +622 -0
  36. offwork/worker/schedule.py +26 -0
  37. offwork/worker/worker.py +263 -0
  38. offwork-0.4.0.dist-info/METADATA +143 -0
  39. offwork-0.4.0.dist-info/RECORD +42 -0
  40. offwork-0.4.0.dist-info/WHEEL +4 -0
  41. offwork-0.4.0.dist-info/entry_points.txt +3 -0
  42. offwork-0.4.0.dist-info/licenses/LICENSE +661 -0
@@ -0,0 +1,87 @@
1
+ """The ``@trace`` decorator for marking functions for remote execution."""
2
+
3
+ import logging
4
+ from datetime import timedelta
5
+ from typing import TypeVar, ParamSpec, overload
6
+ from collections.abc import Callable
7
+
8
+ from offwork.typing import TraceDecorator, TracedFunction
9
+ from offwork.graph.graph import Graph
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+ _R = TypeVar("_R")
14
+ _P = ParamSpec("_P")
15
+
16
+
17
+ @overload
18
+ def trace(func: Callable[_P, _R]) -> TracedFunction[_P, _R]: ...
19
+ @overload
20
+ def trace(*, timeout: float | None = ..., retries: int = ..., retry_delay: float = ..., throttle: timedelta | float | None = ...) -> TraceDecorator: ...
21
+
22
+
23
+ def trace(
24
+ func: Callable[..., object] | None = None,
25
+ *,
26
+ timeout: float | None = None,
27
+ retries: int = 0,
28
+ retry_delay: float = 1.0,
29
+ throttle: timedelta | float | None = None,
30
+ ) -> object:
31
+ """Enable a function for serialization and remote execution.
32
+
33
+ The decorated function works normally when called directly.
34
+ Call ``func.run(...)`` to submit it to a remote worker.
35
+
36
+ Can be used with or without arguments::
37
+
38
+ @trace
39
+ def fast(x): ...
40
+
41
+ @trace(timeout=30, retries=3)
42
+ def flaky(x): ...
43
+ """
44
+ if func is not None:
45
+ return _apply_trace(func, timeout=timeout, retries=retries, retry_delay=retry_delay, throttle=throttle)
46
+
47
+ def decorator(f: Callable[_P, _R]) -> object:
48
+ return _apply_trace(f, timeout=timeout, retries=retries, retry_delay=retry_delay, throttle=throttle)
49
+
50
+ return decorator
51
+
52
+
53
+ def _apply_trace(
54
+ func: Callable[_P, _R],
55
+ *,
56
+ timeout: float | None = None,
57
+ retries: int = 0,
58
+ retry_delay: float = 1.0,
59
+ throttle: timedelta | float | None = None,
60
+ ) -> TracedFunction[_P, _R]:
61
+ if timeout is not None and timeout <= 0:
62
+ raise ValueError(f"timeout must be positive, got {timeout}")
63
+ if retries < 0:
64
+ raise ValueError(f"retries must be non-negative, got {retries}")
65
+ if retry_delay < 0:
66
+ raise ValueError(f"retry_delay must be non-negative, got {retry_delay}")
67
+
68
+ throttle_seconds: float | None = None
69
+ if throttle is not None:
70
+ if isinstance(throttle, timedelta):
71
+ throttle_seconds = throttle.total_seconds()
72
+ else:
73
+ throttle_seconds = float(throttle)
74
+ if throttle_seconds <= 0:
75
+ raise ValueError(f"throttle must be positive, got {throttle}")
76
+
77
+ logger.debug("@trace applied to %s", func.__qualname__)
78
+ graph = Graph.default()
79
+ graph.register(func)
80
+ wrapper = graph.create_wrapper(func)
81
+ wrapper.__offwork_options__ = { # type: ignore[attr-defined]
82
+ "timeout": timeout,
83
+ "retries": retries,
84
+ "retry_delay": retry_delay,
85
+ "throttle": throttle_seconds,
86
+ }
87
+ return wrapper