pydocket 0.1.0__tar.gz → 0.1.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.
Potentially problematic release.
This version of pydocket might be problematic. Click here for more details.
- pydocket-0.1.0/.cursorrules → pydocket-0.1.2/.cursor/rules/general.mdc +7 -17
- pydocket-0.1.2/.cursor/rules/python-style.mdc +22 -0
- {pydocket-0.1.0 → pydocket-0.1.2}/.github/workflows/ci.yml +1 -0
- {pydocket-0.1.0 → pydocket-0.1.2}/PKG-INFO +2 -2
- {pydocket-0.1.0 → pydocket-0.1.2}/pyproject.toml +3 -1
- {pydocket-0.1.0 → pydocket-0.1.2}/src/docket/cli.py +1 -1
- {pydocket-0.1.0 → pydocket-0.1.2}/src/docket/worker.py +1 -1
- pydocket-0.1.2/tests/cli/test_module.py +22 -0
- {pydocket-0.1.0 → pydocket-0.1.2}/tests/cli/test_snapshot.py +69 -99
- pydocket-0.1.2/tests/cli/test_striking.py +232 -0
- pydocket-0.1.2/tests/cli/test_worker.py +179 -0
- pydocket-0.1.2/tests/cli/test_workers.py +65 -0
- {pydocket-0.1.0 → pydocket-0.1.2}/tests/conftest.py +18 -4
- {pydocket-0.1.0 → pydocket-0.1.2}/uv.lock +2 -1
- pydocket-0.1.0/tests/cli/test_module.py +0 -14
- pydocket-0.1.0/tests/cli/test_striking.py +0 -201
- pydocket-0.1.0/tests/cli/test_worker.py +0 -177
- pydocket-0.1.0/tests/cli/test_workers.py +0 -79
- {pydocket-0.1.0 → pydocket-0.1.2}/.github/codecov.yml +0 -0
- {pydocket-0.1.0 → pydocket-0.1.2}/.github/workflows/chaos.yml +0 -0
- {pydocket-0.1.0 → pydocket-0.1.2}/.github/workflows/publish.yml +0 -0
- {pydocket-0.1.0 → pydocket-0.1.2}/.gitignore +0 -0
- {pydocket-0.1.0 → pydocket-0.1.2}/.pre-commit-config.yaml +0 -0
- {pydocket-0.1.0 → pydocket-0.1.2}/LICENSE +0 -0
- {pydocket-0.1.0 → pydocket-0.1.2}/README.md +0 -0
- {pydocket-0.1.0 → pydocket-0.1.2}/chaos/README.md +0 -0
- {pydocket-0.1.0 → pydocket-0.1.2}/chaos/__init__.py +0 -0
- {pydocket-0.1.0 → pydocket-0.1.2}/chaos/driver.py +0 -0
- {pydocket-0.1.0 → pydocket-0.1.2}/chaos/producer.py +0 -0
- {pydocket-0.1.0 → pydocket-0.1.2}/chaos/run +0 -0
- {pydocket-0.1.0 → pydocket-0.1.2}/chaos/tasks.py +0 -0
- {pydocket-0.1.0 → pydocket-0.1.2}/src/docket/__init__.py +0 -0
- {pydocket-0.1.0 → pydocket-0.1.2}/src/docket/__main__.py +0 -0
- {pydocket-0.1.0 → pydocket-0.1.2}/src/docket/annotations.py +0 -0
- {pydocket-0.1.0 → pydocket-0.1.2}/src/docket/dependencies.py +0 -0
- {pydocket-0.1.0 → pydocket-0.1.2}/src/docket/docket.py +0 -0
- {pydocket-0.1.0 → pydocket-0.1.2}/src/docket/execution.py +0 -0
- {pydocket-0.1.0 → pydocket-0.1.2}/src/docket/instrumentation.py +0 -0
- {pydocket-0.1.0 → pydocket-0.1.2}/src/docket/py.typed +0 -0
- {pydocket-0.1.0 → pydocket-0.1.2}/src/docket/tasks.py +0 -0
- {pydocket-0.1.0 → pydocket-0.1.2}/telemetry/.gitignore +0 -0
- {pydocket-0.1.0 → pydocket-0.1.2}/telemetry/start +0 -0
- {pydocket-0.1.0 → pydocket-0.1.2}/telemetry/stop +0 -0
- {pydocket-0.1.0 → pydocket-0.1.2}/tests/__init__.py +0 -0
- {pydocket-0.1.0 → pydocket-0.1.2}/tests/cli/__init__.py +0 -0
- {pydocket-0.1.0 → pydocket-0.1.2}/tests/cli/conftest.py +0 -0
- {pydocket-0.1.0 → pydocket-0.1.2}/tests/cli/test_parsing.py +0 -0
- {pydocket-0.1.0 → pydocket-0.1.2}/tests/cli/test_tasks.py +0 -0
- {pydocket-0.1.0 → pydocket-0.1.2}/tests/cli/test_version.py +0 -0
- {pydocket-0.1.0 → pydocket-0.1.2}/tests/test_dependencies.py +0 -0
- {pydocket-0.1.0 → pydocket-0.1.2}/tests/test_docket.py +0 -0
- {pydocket-0.1.0 → pydocket-0.1.2}/tests/test_fundamentals.py +0 -0
- {pydocket-0.1.0 → pydocket-0.1.2}/tests/test_instrumentation.py +0 -0
- {pydocket-0.1.0 → pydocket-0.1.2}/tests/test_striking.py +0 -0
- {pydocket-0.1.0 → pydocket-0.1.2}/tests/test_worker.py +0 -0
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
description:
|
|
3
|
+
globs:
|
|
4
|
+
alwaysApply: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# about docket
|
|
1
8
|
docket is a distributed background task system for Python functions with a focus
|
|
2
9
|
on the scheduling of future work as seamlessly and efficiency as immediate work.
|
|
3
10
|
|
|
@@ -22,20 +29,3 @@ idempotency of an execution.
|
|
|
22
29
|
|
|
23
30
|
A docket worker should be as easily usable in code as it is from the command line,
|
|
24
31
|
and should be a breeze to use with test suites.
|
|
25
|
-
|
|
26
|
-
# Code style
|
|
27
|
-
|
|
28
|
-
When generating production code, always use full parameter and return type hints
|
|
29
|
-
for every function. Never generate useless inline comments that just reiterate
|
|
30
|
-
what the code is doing. It's okay to include comments in the rare case there is
|
|
31
|
-
something tricky going on.
|
|
32
|
-
|
|
33
|
-
When generating tests, always use parameter type hints, but never include the
|
|
34
|
-
`-> None` return type hint for a test function. For `pytest` fixtures, always
|
|
35
|
-
generate both the parameter and return type hints.
|
|
36
|
-
|
|
37
|
-
When generating tests, favor smaller, focused tests that use fixtures for reuse.
|
|
38
|
-
Don't include extraneous comments in the test code unless something needs more
|
|
39
|
-
clarity. Always generate a docstring using "should" language to describe the
|
|
40
|
-
aspect of the system the test is checking. Use simple direct language and avoid
|
|
41
|
-
sounding stuffy, but make these complete sentences.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: how to write python
|
|
3
|
+
globs: *.py
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Code style
|
|
8
|
+
|
|
9
|
+
When generating production code, always use full parameter and return type hints
|
|
10
|
+
for every function. Never generate useless inline comments that just reiterate
|
|
11
|
+
what the code is doing. It's okay to include comments in the rare case there is
|
|
12
|
+
something tricky going on.
|
|
13
|
+
|
|
14
|
+
When generating tests, always use parameter type hints, but never include the
|
|
15
|
+
`-> None` return type hint for a test function. For `pytest` fixtures, always
|
|
16
|
+
generate both the parameter and return type hints.
|
|
17
|
+
|
|
18
|
+
When generating tests, favor smaller, focused tests that use fixtures for reuse.
|
|
19
|
+
Don't include extraneous comments in the test code unless something needs more
|
|
20
|
+
clarity. Always generate a docstring using "should" language to describe the
|
|
21
|
+
aspect of the system the test is checking. Use simple direct language and avoid
|
|
22
|
+
sounding stuffy, but make these complete sentences.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pydocket
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
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
|
|
@@ -28,7 +28,7 @@ Requires-Dist: opentelemetry-api>=1.30.0
|
|
|
28
28
|
Requires-Dist: opentelemetry-exporter-prometheus>=0.51b0
|
|
29
29
|
Requires-Dist: prometheus-client>=0.21.1
|
|
30
30
|
Requires-Dist: python-json-logger>=3.2.1
|
|
31
|
-
Requires-Dist: redis>=
|
|
31
|
+
Requires-Dist: redis>=4.6
|
|
32
32
|
Requires-Dist: rich>=13.9.4
|
|
33
33
|
Requires-Dist: typer>=0.15.1
|
|
34
34
|
Description-Content-Type: text/markdown
|
|
@@ -25,7 +25,7 @@ dependencies = [
|
|
|
25
25
|
"opentelemetry-exporter-prometheus>=0.51b0",
|
|
26
26
|
"prometheus-client>=0.21.1",
|
|
27
27
|
"python-json-logger>=3.2.1",
|
|
28
|
-
"redis>=
|
|
28
|
+
"redis>=4.6",
|
|
29
29
|
"rich>=13.9.4",
|
|
30
30
|
"typer>=0.15.1",
|
|
31
31
|
]
|
|
@@ -68,6 +68,8 @@ packages = ["src/docket"]
|
|
|
68
68
|
addopts = "--cov=src/docket --cov=tests --cov-report=term-missing --cov-branch"
|
|
69
69
|
asyncio_mode = "auto"
|
|
70
70
|
asyncio_default_fixture_loop_scope = "session"
|
|
71
|
+
filterwarnings = ["error"]
|
|
72
|
+
|
|
71
73
|
|
|
72
74
|
[tool.pyright]
|
|
73
75
|
include = ["src", "tests"]
|
|
@@ -365,7 +365,7 @@ def restore(
|
|
|
365
365
|
value_ = interpret_python_value(value)
|
|
366
366
|
if parameter:
|
|
367
367
|
function_name = f"{function or '(all tasks)'}"
|
|
368
|
-
print(f"
|
|
368
|
+
print(f"Restoring {function_name} {parameter} {operator} {value_!r}")
|
|
369
369
|
else:
|
|
370
370
|
print(f"Restoring {function}")
|
|
371
371
|
|
|
@@ -262,7 +262,7 @@ class Worker:
|
|
|
262
262
|
)
|
|
263
263
|
|
|
264
264
|
redeliveries: RedisMessages
|
|
265
|
-
_, redeliveries, _ = await redis.xautoclaim(
|
|
265
|
+
_, redeliveries, *_ = await redis.xautoclaim(
|
|
266
266
|
name=self.docket.stream_key,
|
|
267
267
|
groupname=self.docket.worker_group_name,
|
|
268
268
|
consumername=self.name,
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import subprocess
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
import docket
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
async def test_module_invocation_as_cli_entrypoint():
|
|
9
|
+
"""Should allow invoking docket as a module with python -m docket."""
|
|
10
|
+
process = await asyncio.create_subprocess_exec(
|
|
11
|
+
sys.executable,
|
|
12
|
+
"-m",
|
|
13
|
+
"docket",
|
|
14
|
+
"version",
|
|
15
|
+
stdout=subprocess.PIPE,
|
|
16
|
+
stderr=subprocess.PIPE,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
stdout, stderr = await process.communicate()
|
|
20
|
+
|
|
21
|
+
assert process.returncode == 0, stderr.decode()
|
|
22
|
+
assert stdout.decode().strip() == docket.__version__
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
-
import sys
|
|
3
2
|
from datetime import datetime, timedelta, timezone
|
|
4
3
|
|
|
5
4
|
import pytest
|
|
6
5
|
from pytest import MonkeyPatch
|
|
6
|
+
from typer.testing import CliRunner
|
|
7
7
|
|
|
8
8
|
from docket import tasks
|
|
9
|
-
from docket.cli import relative_time
|
|
9
|
+
from docket.cli import app, relative_time
|
|
10
10
|
from docket.docket import Docket
|
|
11
11
|
from docket.worker import Worker
|
|
12
12
|
|
|
@@ -19,105 +19,83 @@ async def empty_docket(docket: Docket):
|
|
|
19
19
|
await docket.cancel("initial")
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
async def test_snapshot_empty_docket(docket: Docket):
|
|
22
|
+
async def test_snapshot_empty_docket(docket: Docket, runner: CliRunner):
|
|
23
23
|
"""Should show an empty snapshot when no tasks are scheduled"""
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
24
|
+
result = await asyncio.get_running_loop().run_in_executor(
|
|
25
|
+
None,
|
|
26
|
+
runner.invoke,
|
|
27
|
+
app,
|
|
28
|
+
[
|
|
29
|
+
"snapshot",
|
|
30
|
+
"--url",
|
|
31
|
+
docket.url,
|
|
32
|
+
"--docket",
|
|
33
|
+
docket.name,
|
|
34
|
+
],
|
|
35
35
|
)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
assert process.stderr
|
|
39
|
-
stderr = await process.stderr.read()
|
|
40
|
-
assert process.returncode == 0, stderr.decode()
|
|
36
|
+
assert result.exit_code == 0, result.output
|
|
41
37
|
|
|
42
|
-
assert
|
|
43
|
-
output = await process.stdout.read()
|
|
44
|
-
output_text = output.decode()
|
|
38
|
+
assert "0 workers, 0/0 running" in result.output
|
|
45
39
|
|
|
46
|
-
assert "0 workers, 0/0 running" in output_text
|
|
47
40
|
|
|
48
|
-
|
|
49
|
-
async def test_snapshot_with_scheduled_tasks(docket: Docket):
|
|
41
|
+
async def test_snapshot_with_scheduled_tasks(docket: Docket, runner: CliRunner):
|
|
50
42
|
"""Should show scheduled tasks in the snapshot"""
|
|
51
43
|
when = datetime.now(timezone.utc) + timedelta(seconds=5)
|
|
52
44
|
await docket.add(tasks.trace, when=when, key="future-task")("hiya!")
|
|
53
45
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
46
|
+
result = await asyncio.get_running_loop().run_in_executor(
|
|
47
|
+
None,
|
|
48
|
+
runner.invoke,
|
|
49
|
+
app,
|
|
50
|
+
[
|
|
51
|
+
"snapshot",
|
|
52
|
+
"--url",
|
|
53
|
+
docket.url,
|
|
54
|
+
"--docket",
|
|
55
|
+
docket.name,
|
|
56
|
+
],
|
|
65
57
|
)
|
|
66
|
-
|
|
58
|
+
assert result.exit_code == 0, result.output
|
|
67
59
|
|
|
68
|
-
assert
|
|
69
|
-
|
|
70
|
-
assert process.returncode == 0, stderr.decode()
|
|
60
|
+
assert "0 workers, 0/1 running" in result.output
|
|
61
|
+
assert "future-task" in result.output
|
|
71
62
|
|
|
72
|
-
assert process.stdout
|
|
73
|
-
output = await process.stdout.read()
|
|
74
|
-
output_text = output.decode()
|
|
75
63
|
|
|
76
|
-
|
|
77
|
-
assert "future-task" in output_text
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
async def test_snapshot_with_running_tasks(docket: Docket):
|
|
64
|
+
async def test_snapshot_with_running_tasks(docket: Docket, runner: CliRunner):
|
|
81
65
|
"""Should show running tasks in the snapshot"""
|
|
82
66
|
heartbeat = timedelta(milliseconds=20)
|
|
83
67
|
docket.heartbeat_interval = heartbeat
|
|
84
68
|
|
|
85
|
-
await docket.add(tasks.sleep)(
|
|
69
|
+
await docket.add(tasks.sleep)(1)
|
|
86
70
|
|
|
87
71
|
async with Worker(docket, name="test-worker") as worker:
|
|
88
72
|
worker_running = asyncio.create_task(worker.run_until_finished())
|
|
89
73
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
74
|
+
await asyncio.sleep(0.1)
|
|
75
|
+
|
|
76
|
+
result = await asyncio.get_running_loop().run_in_executor(
|
|
77
|
+
None,
|
|
78
|
+
runner.invoke,
|
|
79
|
+
app,
|
|
80
|
+
[
|
|
81
|
+
"snapshot",
|
|
82
|
+
"--url",
|
|
83
|
+
docket.url,
|
|
84
|
+
"--docket",
|
|
85
|
+
docket.name,
|
|
86
|
+
],
|
|
101
87
|
)
|
|
102
|
-
|
|
88
|
+
assert result.exit_code == 0, result.output
|
|
103
89
|
|
|
104
|
-
assert
|
|
105
|
-
|
|
106
|
-
assert
|
|
107
|
-
|
|
108
|
-
assert process.stdout
|
|
109
|
-
output = await process.stdout.read()
|
|
110
|
-
output_text = output.decode()
|
|
111
|
-
|
|
112
|
-
assert "1 workers, 1/1 running" in output_text
|
|
113
|
-
assert "sleep" in output_text
|
|
114
|
-
assert "test-worker" in output_text
|
|
90
|
+
assert "1 workers, 1/1 running" in result.output
|
|
91
|
+
assert "sleep" in result.output
|
|
92
|
+
assert "test-worker" in result.output
|
|
115
93
|
|
|
116
94
|
worker_running.cancel()
|
|
117
95
|
await worker_running
|
|
118
96
|
|
|
119
97
|
|
|
120
|
-
async def test_snapshot_with_mixed_tasks(docket: Docket):
|
|
98
|
+
async def test_snapshot_with_mixed_tasks(docket: Docket, runner: CliRunner):
|
|
121
99
|
"""Should show both running and scheduled tasks in the snapshot"""
|
|
122
100
|
heartbeat = timedelta(milliseconds=20)
|
|
123
101
|
docket.heartbeat_interval = heartbeat
|
|
@@ -130,34 +108,26 @@ async def test_snapshot_with_mixed_tasks(docket: Docket):
|
|
|
130
108
|
async with Worker(docket, name="test-worker", concurrency=2) as worker:
|
|
131
109
|
worker_running = asyncio.create_task(worker.run_until_finished())
|
|
132
110
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
111
|
+
await asyncio.sleep(0.1)
|
|
112
|
+
|
|
113
|
+
result = await asyncio.get_running_loop().run_in_executor(
|
|
114
|
+
None,
|
|
115
|
+
runner.invoke,
|
|
116
|
+
app,
|
|
117
|
+
[
|
|
118
|
+
"snapshot",
|
|
119
|
+
"--url",
|
|
120
|
+
docket.url,
|
|
121
|
+
"--docket",
|
|
122
|
+
docket.name,
|
|
123
|
+
],
|
|
144
124
|
)
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
assert process.stderr
|
|
148
|
-
stderr = await process.stderr.read()
|
|
149
|
-
assert process.returncode == 0, stderr.decode()
|
|
150
|
-
|
|
151
|
-
assert process.stdout
|
|
152
|
-
output = await process.stdout.read()
|
|
153
|
-
output_text = output.decode()
|
|
154
|
-
|
|
155
|
-
print(output_text)
|
|
125
|
+
assert result.exit_code == 0, result.output
|
|
156
126
|
|
|
157
|
-
assert "1 workers, 2/6 running" in
|
|
158
|
-
assert "sleep" in
|
|
159
|
-
assert "test-worker" in
|
|
160
|
-
assert "trace" in
|
|
127
|
+
assert "1 workers, 2/6 running" in result.output
|
|
128
|
+
assert "sleep" in result.output
|
|
129
|
+
assert "test-worker" in result.output
|
|
130
|
+
assert "trace" in result.output
|
|
161
131
|
|
|
162
132
|
worker_running.cancel()
|
|
163
133
|
await worker_running
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import decimal
|
|
3
|
+
from datetime import timedelta
|
|
4
|
+
from typing import Any
|
|
5
|
+
from uuid import UUID, uuid4
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
from typer.testing import CliRunner
|
|
9
|
+
|
|
10
|
+
from docket.cli import app, interpret_python_value
|
|
11
|
+
from docket.docket import Docket
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
async def test_strike(runner: CliRunner, redis_url: str):
|
|
15
|
+
"""Should strike a task"""
|
|
16
|
+
async with Docket(name=f"test-docket-{uuid4()}", url=redis_url) as docket:
|
|
17
|
+
result = await asyncio.get_running_loop().run_in_executor(
|
|
18
|
+
None,
|
|
19
|
+
runner.invoke,
|
|
20
|
+
app,
|
|
21
|
+
[
|
|
22
|
+
"strike",
|
|
23
|
+
"--url",
|
|
24
|
+
docket.url,
|
|
25
|
+
"--docket",
|
|
26
|
+
docket.name,
|
|
27
|
+
"example_task",
|
|
28
|
+
"some_parameter",
|
|
29
|
+
"==",
|
|
30
|
+
"some_value",
|
|
31
|
+
],
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
assert result.exit_code == 0, result.output
|
|
35
|
+
|
|
36
|
+
assert "Striking example_task some_parameter == 'some_value'" in result.output
|
|
37
|
+
|
|
38
|
+
await asyncio.sleep(0.25)
|
|
39
|
+
|
|
40
|
+
assert "example_task" in docket.strike_list.task_strikes
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
async def test_restore(runner: CliRunner, redis_url: str):
|
|
44
|
+
"""Should restore a task"""
|
|
45
|
+
async with Docket(name=f"test-docket-{uuid4()}", url=redis_url) as docket:
|
|
46
|
+
await docket.strike("example_task", "some_parameter", "==", "some_value")
|
|
47
|
+
assert "example_task" in docket.strike_list.task_strikes
|
|
48
|
+
|
|
49
|
+
result = await asyncio.get_running_loop().run_in_executor(
|
|
50
|
+
None,
|
|
51
|
+
runner.invoke,
|
|
52
|
+
app,
|
|
53
|
+
[
|
|
54
|
+
"restore",
|
|
55
|
+
"--url",
|
|
56
|
+
docket.url,
|
|
57
|
+
"--docket",
|
|
58
|
+
docket.name,
|
|
59
|
+
"example_task",
|
|
60
|
+
"some_parameter",
|
|
61
|
+
"==",
|
|
62
|
+
"some_value",
|
|
63
|
+
],
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
assert result.exit_code == 0, result.output
|
|
67
|
+
|
|
68
|
+
assert "Restoring example_task some_parameter == 'some_value'" in result.output
|
|
69
|
+
|
|
70
|
+
await asyncio.sleep(0.25)
|
|
71
|
+
|
|
72
|
+
assert "example_task" not in docket.strike_list.task_strikes
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
async def test_task_only_strike(runner: CliRunner, redis_url: str):
|
|
76
|
+
"""Should strike a task without specifying parameter conditions"""
|
|
77
|
+
async with Docket(name=f"test-docket-{uuid4()}", url=redis_url) as docket:
|
|
78
|
+
result = await asyncio.get_running_loop().run_in_executor(
|
|
79
|
+
None,
|
|
80
|
+
runner.invoke,
|
|
81
|
+
app,
|
|
82
|
+
[
|
|
83
|
+
"strike",
|
|
84
|
+
"--url",
|
|
85
|
+
docket.url,
|
|
86
|
+
"--docket",
|
|
87
|
+
docket.name,
|
|
88
|
+
"example_task",
|
|
89
|
+
],
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
assert result.exit_code == 0, result.output
|
|
93
|
+
assert "Striking example_task" in result.output
|
|
94
|
+
|
|
95
|
+
await asyncio.sleep(0.25)
|
|
96
|
+
|
|
97
|
+
assert "example_task" in docket.strike_list.task_strikes
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
async def test_task_only_restore(runner: CliRunner, redis_url: str):
|
|
101
|
+
"""Should restore a task without specifying parameter conditions"""
|
|
102
|
+
async with Docket(name=f"test-docket-{uuid4()}", url=redis_url) as docket:
|
|
103
|
+
await docket.strike("example_task")
|
|
104
|
+
|
|
105
|
+
async with Docket(name=f"test-docket-{uuid4()}", url=redis_url) as docket:
|
|
106
|
+
result = await asyncio.get_running_loop().run_in_executor(
|
|
107
|
+
None,
|
|
108
|
+
runner.invoke,
|
|
109
|
+
app,
|
|
110
|
+
[
|
|
111
|
+
"restore",
|
|
112
|
+
"--url",
|
|
113
|
+
docket.url,
|
|
114
|
+
"--docket",
|
|
115
|
+
docket.name,
|
|
116
|
+
"example_task",
|
|
117
|
+
],
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
assert result.exit_code == 0, result.output
|
|
121
|
+
assert "Restoring example_task" in result.output
|
|
122
|
+
|
|
123
|
+
await asyncio.sleep(0.25)
|
|
124
|
+
|
|
125
|
+
assert "example_task" not in docket.strike_list.task_strikes
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
async def test_parameter_only_strike(runner: CliRunner, redis_url: str):
|
|
129
|
+
"""Should strike tasks with matching parameter conditions regardless of task name"""
|
|
130
|
+
async with Docket(name=f"test-docket-{uuid4()}", url=redis_url) as docket:
|
|
131
|
+
result = await asyncio.get_running_loop().run_in_executor(
|
|
132
|
+
None,
|
|
133
|
+
runner.invoke,
|
|
134
|
+
app,
|
|
135
|
+
[
|
|
136
|
+
"strike",
|
|
137
|
+
"--url",
|
|
138
|
+
docket.url,
|
|
139
|
+
"--docket",
|
|
140
|
+
docket.name,
|
|
141
|
+
"",
|
|
142
|
+
"some_parameter",
|
|
143
|
+
"==",
|
|
144
|
+
"some_value",
|
|
145
|
+
],
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
assert result.exit_code == 0, result.output
|
|
149
|
+
assert "Striking (all tasks) some_parameter == 'some_value'" in result.output
|
|
150
|
+
|
|
151
|
+
await asyncio.sleep(0.25)
|
|
152
|
+
|
|
153
|
+
assert "some_parameter" in docket.strike_list.parameter_strikes
|
|
154
|
+
parameter_strikes = docket.strike_list.parameter_strikes["some_parameter"]
|
|
155
|
+
assert ("==", "some_value") in parameter_strikes
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
async def test_parameter_only_restore(runner: CliRunner, redis_url: str):
|
|
159
|
+
"""Should restore tasks with matching parameter conditions regardless of task
|
|
160
|
+
name"""
|
|
161
|
+
async with Docket(name=f"test-docket-{uuid4()}", url=redis_url) as docket:
|
|
162
|
+
await docket.strike("", "some_parameter", "==", "some_value")
|
|
163
|
+
|
|
164
|
+
result = await asyncio.get_running_loop().run_in_executor(
|
|
165
|
+
None,
|
|
166
|
+
runner.invoke,
|
|
167
|
+
app,
|
|
168
|
+
[
|
|
169
|
+
"restore",
|
|
170
|
+
"--url",
|
|
171
|
+
docket.url,
|
|
172
|
+
"--docket",
|
|
173
|
+
docket.name,
|
|
174
|
+
"",
|
|
175
|
+
"some_parameter",
|
|
176
|
+
"==",
|
|
177
|
+
"some_value",
|
|
178
|
+
],
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
assert result.exit_code == 0, result.output
|
|
182
|
+
assert "Restoring (all tasks) some_parameter == 'some_value'" in result.output
|
|
183
|
+
|
|
184
|
+
await asyncio.sleep(0.25)
|
|
185
|
+
|
|
186
|
+
assert "some_parameter" not in docket.strike_list.parameter_strikes
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
@pytest.mark.parametrize("operation", ["strike", "restore"])
|
|
190
|
+
async def test_strike_with_no_function_or_parameter(
|
|
191
|
+
runner: CliRunner, redis_url: str, operation: str
|
|
192
|
+
):
|
|
193
|
+
"""Should fail when neither function nor parameter is provided"""
|
|
194
|
+
async with Docket(name=f"test-docket-{uuid4()}", url=redis_url) as docket:
|
|
195
|
+
result = await asyncio.get_running_loop().run_in_executor(
|
|
196
|
+
None,
|
|
197
|
+
runner.invoke,
|
|
198
|
+
app,
|
|
199
|
+
[
|
|
200
|
+
operation,
|
|
201
|
+
"--url",
|
|
202
|
+
docket.url,
|
|
203
|
+
"--docket",
|
|
204
|
+
docket.name,
|
|
205
|
+
"",
|
|
206
|
+
],
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
assert result.exit_code != 0, result.output
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
@pytest.mark.parametrize(
|
|
213
|
+
"input_value,expected_result",
|
|
214
|
+
[
|
|
215
|
+
(None, None),
|
|
216
|
+
("hello", "hello"),
|
|
217
|
+
("int:42", 42),
|
|
218
|
+
("float:3.14", 3.14),
|
|
219
|
+
("decimal.Decimal:3.14", decimal.Decimal("3.14")),
|
|
220
|
+
("bool:True", True),
|
|
221
|
+
("bool:False", False),
|
|
222
|
+
("datetime.timedelta:10", timedelta(seconds=10)),
|
|
223
|
+
(
|
|
224
|
+
"uuid.UUID:123e4567-e89b-12d3-a456-426614174000",
|
|
225
|
+
UUID("123e4567-e89b-12d3-a456-426614174000"),
|
|
226
|
+
),
|
|
227
|
+
],
|
|
228
|
+
)
|
|
229
|
+
async def test_interpret_python_value(input_value: str | None, expected_result: Any):
|
|
230
|
+
"""Should interpret Python values correctly from strings"""
|
|
231
|
+
result = interpret_python_value(input_value)
|
|
232
|
+
assert result == expected_result
|