datashare-python 0.2.13__tar.gz → 0.2.14__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.
- {datashare_python-0.2.13 → datashare_python-0.2.14}/PKG-INFO +1 -1
- {datashare_python-0.2.13 → datashare_python-0.2.14}/pyproject.toml +4 -2
- datashare_python-0.2.13/tests/__init__.py +0 -0
- datashare_python-0.2.13/tests/cli/test_project.py +0 -23
- datashare_python-0.2.13/tests/cli/test_tasks.py +0 -241
- datashare_python-0.2.13/tests/cli/test_worker.py +0 -46
- datashare_python-0.2.13/tests/conftest.py +0 -14
- datashare_python-0.2.13/tests/test_discovery.py +0 -44
- datashare_python-0.2.13/tests/test_object.py +0 -28
- datashare_python-0.2.13/tests/test_task_client.py +0 -242
- datashare_python-0.2.13/tests/test_template.py +0 -40
- datashare_python-0.2.13/tests/test_utils.py +0 -17
- datashare_python-0.2.13/tests/test_worker.py +0 -68
- datashare_python-0.2.13/uv.lock +0 -2381
- {datashare_python-0.2.13 → datashare_python-0.2.14}/.gitignore +0 -0
- {datashare_python-0.2.13 → datashare_python-0.2.14}/README.md +0 -0
- {datashare_python-0.2.13 → datashare_python-0.2.14}/datashare_python/.gitignore +0 -0
- {datashare_python-0.2.13 → datashare_python-0.2.14}/datashare_python/__init__.py +0 -0
- {datashare_python-0.2.13 → datashare_python-0.2.14}/datashare_python/__main__.py +0 -0
- {datashare_python-0.2.13 → datashare_python-0.2.14}/datashare_python/cli/__init__.py +0 -0
- {datashare_python-0.2.13 → datashare_python-0.2.14}/datashare_python/cli/local.py +0 -0
- {datashare_python-0.2.13 → datashare_python-0.2.14}/datashare_python/cli/project.py +0 -0
- {datashare_python-0.2.13 → datashare_python-0.2.14}/datashare_python/cli/task.py +0 -0
- {datashare_python-0.2.13 → datashare_python-0.2.14}/datashare_python/cli/utils.py +0 -0
- {datashare_python-0.2.13 → datashare_python-0.2.14}/datashare_python/cli/worker.py +0 -0
- {datashare_python-0.2.13 → datashare_python-0.2.14}/datashare_python/config.py +0 -0
- {datashare_python-0.2.13 → datashare_python-0.2.14}/datashare_python/conftest.py +0 -0
- {datashare_python-0.2.13 → datashare_python-0.2.14}/datashare_python/constants.py +0 -0
- {datashare_python-0.2.13 → datashare_python-0.2.14}/datashare_python/dependencies.py +0 -0
- {datashare_python-0.2.13 → datashare_python-0.2.14}/datashare_python/discovery.py +0 -0
- {datashare_python-0.2.13 → datashare_python-0.2.14}/datashare_python/exceptions.py +0 -0
- {datashare_python-0.2.13 → datashare_python-0.2.14}/datashare_python/local_client.py +0 -0
- {datashare_python-0.2.13 → datashare_python-0.2.14}/datashare_python/objects.py +0 -0
- {datashare_python-0.2.13 → datashare_python-0.2.14}/datashare_python/task_client.py +0 -0
- {datashare_python-0.2.13 → datashare_python-0.2.14}/datashare_python/template.py +0 -0
- {datashare_python-0.2.13 → datashare_python-0.2.14}/datashare_python/types_.py +0 -0
- {datashare_python-0.2.13 → datashare_python-0.2.14}/datashare_python/utils.py +0 -0
- {datashare_python-0.2.13 → datashare_python-0.2.14}/datashare_python/worker.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: datashare-python
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.14
|
|
4
4
|
Summary: Manage Python tasks and local resources in Datashare
|
|
5
5
|
Project-URL: Homepage, https://icij.github.io/datashare-python/
|
|
6
6
|
Project-URL: Documentation, https://icij.github.io/datashare-python/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "datashare-python"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.14"
|
|
4
4
|
description = "Manage Python tasks and local resources in Datashare"
|
|
5
5
|
authors = [
|
|
6
6
|
{ name = "Clément Doumouro", email = "cdoumouro@icij.org" },
|
|
@@ -35,13 +35,15 @@ datashare-python = "datashare_python.__main__:main"
|
|
|
35
35
|
[build-system]
|
|
36
36
|
requires = ["hatchling"]
|
|
37
37
|
build-backend = "hatchling.build"
|
|
38
|
-
package = ["datashare_python"]
|
|
39
38
|
|
|
40
39
|
[tool.hatch.build.targets.wheel]
|
|
41
40
|
artifacts = [
|
|
42
41
|
"worker-template.tar.gz"
|
|
43
42
|
]
|
|
44
43
|
|
|
44
|
+
[tool.hatch.build.targets.sdist]
|
|
45
|
+
only-include = ["datashare_python"]
|
|
46
|
+
|
|
45
47
|
[tool.uv]
|
|
46
48
|
package = true
|
|
47
49
|
|
|
File without changes
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
from pathlib import Path
|
|
2
|
-
|
|
3
|
-
from datashare_python.cli import cli_app
|
|
4
|
-
from typer.testing import CliRunner
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
async def test_init_project(
|
|
8
|
-
typer_asyncio_patch, # noqa: ANN001, ARG001
|
|
9
|
-
local_template_build, # noqa: ANN001, ARG001
|
|
10
|
-
tmp_path: Path,
|
|
11
|
-
) -> None:
|
|
12
|
-
# Given
|
|
13
|
-
runner = CliRunner(mix_stderr=False)
|
|
14
|
-
test_worker_project = "test-project"
|
|
15
|
-
# When
|
|
16
|
-
args = ["project", "init", str(test_worker_project), "-p", str(tmp_path)]
|
|
17
|
-
result = runner.invoke(
|
|
18
|
-
cli_app,
|
|
19
|
-
args,
|
|
20
|
-
catch_exceptions=False,
|
|
21
|
-
)
|
|
22
|
-
# Then
|
|
23
|
-
assert result.exit_code == 0
|
|
@@ -1,241 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
from contextlib import AbstractAsyncContextManager
|
|
3
|
-
from datetime import UTC, datetime
|
|
4
|
-
from typing import Any, Self
|
|
5
|
-
from unittest.mock import AsyncMock, MagicMock
|
|
6
|
-
|
|
7
|
-
import click
|
|
8
|
-
from _pytest.logging import LogCaptureFixture
|
|
9
|
-
from _pytest.monkeypatch import MonkeyPatch
|
|
10
|
-
from datashare_python.cli import cli_app
|
|
11
|
-
from datashare_python.cli import task as cli_task
|
|
12
|
-
from datashare_python.objects import StacktraceItem, Task, TaskError, TaskState
|
|
13
|
-
from packaging.version import Version
|
|
14
|
-
from typer.testing import CliRunner
|
|
15
|
-
|
|
16
|
-
BUGGED_CLICK_VERSION = Version("8.1.8")
|
|
17
|
-
CLICK_VERSION = Version(click.__version__)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
async def test_task_create_task(
|
|
21
|
-
monkeypatch: MonkeyPatch,
|
|
22
|
-
typer_asyncio_patch, # noqa: ANN001, ARG001
|
|
23
|
-
caplog: LogCaptureFixture,
|
|
24
|
-
) -> None:
|
|
25
|
-
# Given
|
|
26
|
-
mock_client_fn = MagicMock()
|
|
27
|
-
mock_client = AsyncMock()
|
|
28
|
-
task_id = "hello_world-some-id"
|
|
29
|
-
mock_client.create_task.return_value = task_id
|
|
30
|
-
mock_client_fn.return_value = mock_client
|
|
31
|
-
|
|
32
|
-
class MockedClient:
|
|
33
|
-
def __init__(self, datashare_url: str, api_key: str | None = None) -> None: # noqa: ARG002
|
|
34
|
-
...
|
|
35
|
-
|
|
36
|
-
async def __aenter__(self) -> Self:
|
|
37
|
-
return self
|
|
38
|
-
|
|
39
|
-
async def __aexit__(self, exc_type, exc_val, exc_tb): ... # noqa: ANN001
|
|
40
|
-
|
|
41
|
-
async def create_task(
|
|
42
|
-
self, # noqa: ANN001, ARG001
|
|
43
|
-
name: str, # noqa: ARG001, ARG002
|
|
44
|
-
args: dict[str, Any], # noqa: ARG001, ARG002
|
|
45
|
-
*,
|
|
46
|
-
id_: str | None = None, # noqa: ARG001, ARG002
|
|
47
|
-
group: str | None = None, # noqa: ARG001, ARG002
|
|
48
|
-
) -> str:
|
|
49
|
-
return task_id
|
|
50
|
-
|
|
51
|
-
monkeypatch.setattr(cli_task, "DatashareTaskClient", MockedClient)
|
|
52
|
-
# When
|
|
53
|
-
runner = CliRunner(mix_stderr=False)
|
|
54
|
-
with caplog.at_level(logging.INFO):
|
|
55
|
-
result = runner.invoke(
|
|
56
|
-
cli_app, ["task", "start", "hello_world"], catch_exceptions=False
|
|
57
|
-
)
|
|
58
|
-
# Then
|
|
59
|
-
assert result.exit_code == 0
|
|
60
|
-
assert result.stdout == task_id + "\n"
|
|
61
|
-
assert ("Task(hello_world-some-id) started" in r for r in caplog.records)
|
|
62
|
-
assert ("Task(hello_world-some-id) 🛫" in r for r in caplog.records)
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
async def test_task_watch(
|
|
66
|
-
monkeypatch: MonkeyPatch,
|
|
67
|
-
typer_asyncio_patch, # noqa: ANN001, ARG001
|
|
68
|
-
) -> None:
|
|
69
|
-
# Given
|
|
70
|
-
task_id = "hello_world-some-id"
|
|
71
|
-
created_at = datetime.now(UTC)
|
|
72
|
-
|
|
73
|
-
states = [
|
|
74
|
-
Task(
|
|
75
|
-
id=task_id,
|
|
76
|
-
name="hello_world",
|
|
77
|
-
progress=0,
|
|
78
|
-
state=TaskState.CREATED,
|
|
79
|
-
created_at=created_at,
|
|
80
|
-
),
|
|
81
|
-
Task(
|
|
82
|
-
id=task_id,
|
|
83
|
-
name="hello_world",
|
|
84
|
-
progress=0,
|
|
85
|
-
state=TaskState.QUEUED,
|
|
86
|
-
created_at=created_at,
|
|
87
|
-
),
|
|
88
|
-
Task(
|
|
89
|
-
id=task_id,
|
|
90
|
-
name="hello_world",
|
|
91
|
-
progress=0,
|
|
92
|
-
state=TaskState.RUNNING,
|
|
93
|
-
created_at=created_at,
|
|
94
|
-
),
|
|
95
|
-
Task(
|
|
96
|
-
id=task_id,
|
|
97
|
-
name="hello_world",
|
|
98
|
-
progress=0.5,
|
|
99
|
-
state=TaskState.RUNNING,
|
|
100
|
-
created_at=created_at,
|
|
101
|
-
),
|
|
102
|
-
Task(
|
|
103
|
-
id=task_id,
|
|
104
|
-
name="hello_world",
|
|
105
|
-
progress=0.99,
|
|
106
|
-
state=TaskState.RUNNING,
|
|
107
|
-
created_at=created_at,
|
|
108
|
-
),
|
|
109
|
-
Task(
|
|
110
|
-
id=task_id,
|
|
111
|
-
name="hello_world",
|
|
112
|
-
progress=1.0,
|
|
113
|
-
state=TaskState.DONE,
|
|
114
|
-
completed_at=datetime.now(UTC),
|
|
115
|
-
created_at=created_at,
|
|
116
|
-
),
|
|
117
|
-
]
|
|
118
|
-
|
|
119
|
-
class MockedClient(AbstractAsyncContextManager):
|
|
120
|
-
def __init__(self, datashare_url: str, api_key: str | None = None) -> None: # noqa: ARG002
|
|
121
|
-
self._state_it = None
|
|
122
|
-
|
|
123
|
-
async def get_task(self, task_id: str) -> Task: # noqa: ARG002
|
|
124
|
-
return next(self._state_it)
|
|
125
|
-
|
|
126
|
-
async def __aenter__(self) -> Self:
|
|
127
|
-
self._state_it = iter(states)
|
|
128
|
-
return self
|
|
129
|
-
|
|
130
|
-
async def __aexit__(self, exc_type, exc_val, exc_tb): ... # noqa: ANN001
|
|
131
|
-
|
|
132
|
-
monkeypatch.setattr(cli_task, "DatashareTaskClient", MockedClient)
|
|
133
|
-
# When
|
|
134
|
-
runner = CliRunner(mix_stderr=False)
|
|
135
|
-
result = runner.invoke(
|
|
136
|
-
cli_app, ["task", "watch", task_id, "-p", 0.001], catch_exceptions=False
|
|
137
|
-
)
|
|
138
|
-
# Then
|
|
139
|
-
assert result.exit_code == 0
|
|
140
|
-
assert result.stdout.endswith(task_id + "\n")
|
|
141
|
-
assert "Task(hello_world-some-id) 🛫" in result.stderr
|
|
142
|
-
if CLICK_VERSION > BUGGED_CLICK_VERSION:
|
|
143
|
-
assert "Task(hello_world-some-id) 🛬" in result.stderr
|
|
144
|
-
assert "Task(hello_world-some-id) ✅" in result.stderr
|
|
145
|
-
assert "1.0" in result.stderr
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
async def test_task_watch_error(
|
|
149
|
-
monkeypatch: MonkeyPatch,
|
|
150
|
-
typer_asyncio_patch, # noqa: ANN001, ARG001
|
|
151
|
-
) -> None:
|
|
152
|
-
# Given
|
|
153
|
-
mock_client_fn = MagicMock()
|
|
154
|
-
mock_client = AsyncMock()
|
|
155
|
-
task_id = "hello_world-some-id"
|
|
156
|
-
mock_client_fn.return_value = mock_client
|
|
157
|
-
created_at = datetime.now(UTC)
|
|
158
|
-
|
|
159
|
-
class MockedClient(AbstractAsyncContextManager):
|
|
160
|
-
def __init__(self, datashare_url: str, api_key: str | None = None) -> None: # noqa: ARG002
|
|
161
|
-
self._state_it = None
|
|
162
|
-
|
|
163
|
-
async def __aenter__(self) -> Self:
|
|
164
|
-
return self
|
|
165
|
-
|
|
166
|
-
async def __aexit__(self, exc_type, exc_val, exc_tb): ... # noqa: ANN001
|
|
167
|
-
|
|
168
|
-
async def get_task(self, task_id: str) -> Task: # noqa: ARG002
|
|
169
|
-
return Task(
|
|
170
|
-
id=task_id,
|
|
171
|
-
name="hello_world",
|
|
172
|
-
progress=0,
|
|
173
|
-
state=TaskState.ERROR,
|
|
174
|
-
created_at=created_at,
|
|
175
|
-
)
|
|
176
|
-
|
|
177
|
-
async def get_task_error(self, task_id: str) -> TaskError: # noqa: ARG002
|
|
178
|
-
return TaskError(
|
|
179
|
-
name="SomeError",
|
|
180
|
-
message="some error occurred",
|
|
181
|
-
stacktrace=[
|
|
182
|
-
StacktraceItem(name="some_func", file="some_file.py", lineno=666)
|
|
183
|
-
],
|
|
184
|
-
)
|
|
185
|
-
|
|
186
|
-
monkeypatch.setattr(cli_task, "DatashareTaskClient", MockedClient)
|
|
187
|
-
# When
|
|
188
|
-
runner = CliRunner(mix_stderr=False)
|
|
189
|
-
result = runner.invoke(
|
|
190
|
-
cli_app, ["task", "watch", task_id, "-p", 0.001], catch_exceptions=False
|
|
191
|
-
)
|
|
192
|
-
# Then
|
|
193
|
-
assert result.exit_code == 1
|
|
194
|
-
if CLICK_VERSION > BUGGED_CLICK_VERSION:
|
|
195
|
-
assert (
|
|
196
|
-
"Task(hello_world-some-id) failed with the following error:"
|
|
197
|
-
in result.stderr
|
|
198
|
-
)
|
|
199
|
-
assert "Task(hello_world-some-id) ❌" in result.stderr
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
async def test_task_watch_cancelled(
|
|
203
|
-
monkeypatch: MonkeyPatch,
|
|
204
|
-
typer_asyncio_patch, # noqa: ANN001, ARG001
|
|
205
|
-
) -> None:
|
|
206
|
-
# Given
|
|
207
|
-
mock_client_fn = MagicMock()
|
|
208
|
-
mock_client = AsyncMock()
|
|
209
|
-
task_id = "hello_world-some-id"
|
|
210
|
-
mock_client_fn.return_value = mock_client
|
|
211
|
-
created_at = datetime.now(UTC)
|
|
212
|
-
|
|
213
|
-
class MockedClient(AbstractAsyncContextManager):
|
|
214
|
-
def __init__(self, datashare_url: str, api_key: str | None = None) -> None: # noqa: ARG002
|
|
215
|
-
self._state_it = None
|
|
216
|
-
|
|
217
|
-
async def __aenter__(self) -> Self:
|
|
218
|
-
return self
|
|
219
|
-
|
|
220
|
-
async def __aexit__(self, exc_type, exc_val, exc_tb): ... # noqa: ANN001
|
|
221
|
-
|
|
222
|
-
async def get_task(self, task_id: str) -> Task: # noqa: ARG002
|
|
223
|
-
return Task(
|
|
224
|
-
id=task_id,
|
|
225
|
-
name="hello_world",
|
|
226
|
-
progress=0,
|
|
227
|
-
state=TaskState.CANCELLED,
|
|
228
|
-
created_at=created_at,
|
|
229
|
-
)
|
|
230
|
-
|
|
231
|
-
monkeypatch.setattr(cli_task, "DatashareTaskClient", MockedClient)
|
|
232
|
-
# When
|
|
233
|
-
runner = CliRunner(mix_stderr=False)
|
|
234
|
-
result = runner.invoke(
|
|
235
|
-
cli_app, ["task", "watch", task_id, "-p", 0.001], catch_exceptions=False
|
|
236
|
-
)
|
|
237
|
-
# Then
|
|
238
|
-
assert result.exit_code == 1
|
|
239
|
-
if CLICK_VERSION > BUGGED_CLICK_VERSION:
|
|
240
|
-
assert "Task(hello_world-some-id) was cancelled" in result.stderr
|
|
241
|
-
assert "Task(hello_world-some-id) 🛑" in result.stderr
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
from _pytest.capture import CaptureFixture
|
|
2
|
-
from _pytest.monkeypatch import MonkeyPatch
|
|
3
|
-
from datashare_python.cli import cli_app
|
|
4
|
-
from temporalio.worker import Worker
|
|
5
|
-
from typer.testing import CliRunner
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
async def _mock_worker_run(self) -> None: # noqa: ANN001
|
|
9
|
-
pass
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
async def test_start_workers(
|
|
13
|
-
worker_lifetime_deps, # noqa: ANN001, ARG001
|
|
14
|
-
typer_asyncio_patch, # noqa: ANN001, ARG001
|
|
15
|
-
monkeypatch: MonkeyPatch,
|
|
16
|
-
capsys: CaptureFixture[str],
|
|
17
|
-
) -> None:
|
|
18
|
-
# Given
|
|
19
|
-
runner = CliRunner(mix_stderr=False)
|
|
20
|
-
monkeypatch.setattr(Worker, "run", _mock_worker_run)
|
|
21
|
-
with capsys.disabled():
|
|
22
|
-
# When
|
|
23
|
-
result = runner.invoke(
|
|
24
|
-
cli_app,
|
|
25
|
-
[
|
|
26
|
-
"worker",
|
|
27
|
-
"start",
|
|
28
|
-
"--queue",
|
|
29
|
-
"cpu",
|
|
30
|
-
"--activities",
|
|
31
|
-
"ping",
|
|
32
|
-
"--activities",
|
|
33
|
-
"create-translation-batches",
|
|
34
|
-
"--workflows",
|
|
35
|
-
"ping",
|
|
36
|
-
"--temporal-address",
|
|
37
|
-
"localhost:7233",
|
|
38
|
-
],
|
|
39
|
-
catch_exceptions=False,
|
|
40
|
-
)
|
|
41
|
-
# Then
|
|
42
|
-
assert result.exit_code == 0
|
|
43
|
-
expected = """Starting datashare worker running:
|
|
44
|
-
- 1 workflow: ping
|
|
45
|
-
- 1 activity: create-translation-batches"""
|
|
46
|
-
assert expected in result.stderr
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import pytest # noqa: I001
|
|
2
|
-
import nest_asyncio
|
|
3
|
-
from datashare_python.conftest import * # noqa: F403
|
|
4
|
-
from datashare_python.template import build_template_tarball
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
@pytest.fixture # noqa: F405
|
|
8
|
-
def typer_asyncio_patch() -> None:
|
|
9
|
-
nest_asyncio.apply()
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
@pytest.fixture(scope="session")
|
|
13
|
-
def local_template_build() -> None:
|
|
14
|
-
build_template_tarball()
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import pytest
|
|
2
|
-
from datashare_python.discovery import discover_activities, discover_workflows
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
@pytest.mark.parametrize(
|
|
6
|
-
("names", "expected_workflows"),
|
|
7
|
-
[
|
|
8
|
-
([], {"ping", "translate-and-classify"}),
|
|
9
|
-
(["ping", "translate-and-classify"], {"ping", "translate-and-classify"}),
|
|
10
|
-
(["ping"], {"ping"}),
|
|
11
|
-
(["pi.*"], {"ping"}),
|
|
12
|
-
(["pong"], set()),
|
|
13
|
-
],
|
|
14
|
-
)
|
|
15
|
-
def test_discover_workflows(names: list[str], expected_workflows: set[str]) -> None:
|
|
16
|
-
# When
|
|
17
|
-
workflows = {wf_name for wf_name, _ in discover_workflows(names)}
|
|
18
|
-
# Then
|
|
19
|
-
assert workflows == expected_workflows
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
@pytest.mark.parametrize(
|
|
23
|
-
("names", "expected_activities"),
|
|
24
|
-
[
|
|
25
|
-
(
|
|
26
|
-
[],
|
|
27
|
-
{
|
|
28
|
-
"classify-docs",
|
|
29
|
-
"create-classification-batches",
|
|
30
|
-
"create-translation-batches",
|
|
31
|
-
"pong",
|
|
32
|
-
"translate-docs",
|
|
33
|
-
},
|
|
34
|
-
),
|
|
35
|
-
(["translate-docs"], {"translate-docs"}),
|
|
36
|
-
([".*transl.*"], {"create-translation-batches", "translate-docs"}),
|
|
37
|
-
(["idontexist"], set()),
|
|
38
|
-
],
|
|
39
|
-
)
|
|
40
|
-
def test_discover_activities(names: list[str], expected_activities: set[str]) -> None:
|
|
41
|
-
# When
|
|
42
|
-
activities = {act_name for act_name, _ in discover_activities(names)}
|
|
43
|
-
# Then
|
|
44
|
-
assert activities == expected_activities
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
from datetime import datetime
|
|
2
|
-
|
|
3
|
-
from datashare_python.objects import Task, TaskState
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
def test_task_ser() -> None:
|
|
7
|
-
# Given
|
|
8
|
-
task = Task(id="some_id", name="some_name", args=dict())
|
|
9
|
-
|
|
10
|
-
# When
|
|
11
|
-
serialized = task.model_dump()
|
|
12
|
-
|
|
13
|
-
# Then
|
|
14
|
-
assert isinstance(serialized.pop("createdAt"), datetime)
|
|
15
|
-
expected = {
|
|
16
|
-
"@type": "Task",
|
|
17
|
-
"args": {},
|
|
18
|
-
"completedAt": None,
|
|
19
|
-
"error": None,
|
|
20
|
-
"id": "some_id",
|
|
21
|
-
"maxRetries": None,
|
|
22
|
-
"name": "some_name",
|
|
23
|
-
"progress": None,
|
|
24
|
-
"result": None,
|
|
25
|
-
"retriesLeft": None,
|
|
26
|
-
"state": TaskState.CREATED,
|
|
27
|
-
}
|
|
28
|
-
assert serialized == expected
|
|
@@ -1,242 +0,0 @@
|
|
|
1
|
-
import uuid
|
|
2
|
-
from collections.abc import AsyncGenerator
|
|
3
|
-
from contextlib import asynccontextmanager
|
|
4
|
-
from datetime import UTC, datetime
|
|
5
|
-
from typing import Any
|
|
6
|
-
from unittest.mock import AsyncMock
|
|
7
|
-
|
|
8
|
-
from _pytest.monkeypatch import MonkeyPatch
|
|
9
|
-
from aiohttp.typedefs import StrOrURL
|
|
10
|
-
from datashare_python.objects import (
|
|
11
|
-
StacktraceItem,
|
|
12
|
-
Task,
|
|
13
|
-
TaskError,
|
|
14
|
-
TaskResult,
|
|
15
|
-
TaskState,
|
|
16
|
-
User,
|
|
17
|
-
)
|
|
18
|
-
from datashare_python.task_client import AiohttpClient, DatashareTaskClient
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
async def test_task_client_create_task(monkeypatch: MonkeyPatch) -> None:
|
|
22
|
-
# Given
|
|
23
|
-
datashare_url = "http://some-url"
|
|
24
|
-
user = User(id="user")
|
|
25
|
-
api_key = "some-api-key"
|
|
26
|
-
auth = (user, api_key)
|
|
27
|
-
task_name = "hello"
|
|
28
|
-
task_id = f"{task_name}-{uuid.uuid4()}"
|
|
29
|
-
args = {"greeted": "world"}
|
|
30
|
-
group = "PYTHON"
|
|
31
|
-
|
|
32
|
-
@asynccontextmanager
|
|
33
|
-
async def _put_and_assert(
|
|
34
|
-
_, # noqa: ANN001
|
|
35
|
-
url: StrOrURL,
|
|
36
|
-
*,
|
|
37
|
-
data: Any = None,
|
|
38
|
-
**kwargs: Any,
|
|
39
|
-
) -> AsyncGenerator[AsyncMock, None]:
|
|
40
|
-
assert url == f"/api/task/{task_id}?group={group}"
|
|
41
|
-
expected_task = {
|
|
42
|
-
"@type": "Task",
|
|
43
|
-
"id": task_id,
|
|
44
|
-
"state": "CREATED",
|
|
45
|
-
"name": "hello",
|
|
46
|
-
"args": {
|
|
47
|
-
"greeted": "world",
|
|
48
|
-
"user": {
|
|
49
|
-
"@type": "org.icij.datashare.user.User",
|
|
50
|
-
"details": {},
|
|
51
|
-
"id": "user",
|
|
52
|
-
},
|
|
53
|
-
},
|
|
54
|
-
}
|
|
55
|
-
assert data is None
|
|
56
|
-
json_data = kwargs.pop("json")
|
|
57
|
-
assert not kwargs
|
|
58
|
-
assert isinstance(json_data.pop("createdAt", None), str)
|
|
59
|
-
assert json_data == expected_task
|
|
60
|
-
mocked_res = AsyncMock()
|
|
61
|
-
mocked_res.json.return_value = {"taskId": task_id}
|
|
62
|
-
yield mocked_res
|
|
63
|
-
|
|
64
|
-
monkeypatch.setattr(AiohttpClient, "_put", _put_and_assert)
|
|
65
|
-
|
|
66
|
-
task_client = DatashareTaskClient(datashare_url, auth=auth)
|
|
67
|
-
async with task_client:
|
|
68
|
-
# When
|
|
69
|
-
t_id = await task_client.create_task(task_name, args, id_=task_id, group=group)
|
|
70
|
-
assert t_id == task_id
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
async def test_task_client_get_task(monkeypatch: MonkeyPatch) -> None:
|
|
74
|
-
# Given
|
|
75
|
-
datashare_url = "http://some-url"
|
|
76
|
-
user = User(id="user")
|
|
77
|
-
api_key = "some-api-key"
|
|
78
|
-
auth = (user, api_key)
|
|
79
|
-
task_name = "hello"
|
|
80
|
-
task_id = f"{task_name}-{uuid.uuid4()}"
|
|
81
|
-
|
|
82
|
-
@asynccontextmanager
|
|
83
|
-
async def _get_and_assert(
|
|
84
|
-
_, # noqa: ANN001
|
|
85
|
-
url: StrOrURL,
|
|
86
|
-
*,
|
|
87
|
-
allow_redirects: bool = True,
|
|
88
|
-
**kwargs: Any,
|
|
89
|
-
) -> AsyncGenerator[AsyncMock, None]:
|
|
90
|
-
assert url == f"/api/task/{task_id}"
|
|
91
|
-
task_ = {
|
|
92
|
-
"@type": "Task",
|
|
93
|
-
"id": task_id,
|
|
94
|
-
"state": "CREATED",
|
|
95
|
-
"createdAt": datetime.now(UTC),
|
|
96
|
-
"name": "hello",
|
|
97
|
-
"args": {"greeted": "world"},
|
|
98
|
-
}
|
|
99
|
-
assert allow_redirects
|
|
100
|
-
assert not kwargs
|
|
101
|
-
mocked_res = AsyncMock()
|
|
102
|
-
mocked_res.json.return_value = task_
|
|
103
|
-
yield mocked_res
|
|
104
|
-
|
|
105
|
-
monkeypatch.setattr(AiohttpClient, "_get", _get_and_assert)
|
|
106
|
-
|
|
107
|
-
task_client = DatashareTaskClient(datashare_url, auth=auth)
|
|
108
|
-
async with task_client:
|
|
109
|
-
# When
|
|
110
|
-
task = await task_client.get_task(task_id)
|
|
111
|
-
assert isinstance(task, Task)
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
async def test_task_client_get_task_state(monkeypatch: MonkeyPatch) -> None:
|
|
115
|
-
# Given
|
|
116
|
-
datashare_url = "http://some-url"
|
|
117
|
-
user = User(id="user")
|
|
118
|
-
api_key = "some-api-key"
|
|
119
|
-
auth = (user, api_key)
|
|
120
|
-
task_name = "hello"
|
|
121
|
-
task_id = f"{task_name}-{uuid.uuid4()}"
|
|
122
|
-
|
|
123
|
-
@asynccontextmanager
|
|
124
|
-
async def _get_and_assert(
|
|
125
|
-
_, # noqa: ANN001
|
|
126
|
-
url: StrOrURL,
|
|
127
|
-
*,
|
|
128
|
-
allow_redirects: bool = True,
|
|
129
|
-
**kwargs: Any,
|
|
130
|
-
) -> AsyncGenerator[AsyncMock, None]:
|
|
131
|
-
assert url == f"/api/task/{task_id}"
|
|
132
|
-
task = {
|
|
133
|
-
"@type": "Task",
|
|
134
|
-
"id": task_id,
|
|
135
|
-
"state": "DONE",
|
|
136
|
-
"createdAt": datetime.now(UTC),
|
|
137
|
-
"completedAt": datetime.now(UTC),
|
|
138
|
-
"name": "hello",
|
|
139
|
-
"args": {"greeted": "world"},
|
|
140
|
-
"result": TaskResult(value="hellow"),
|
|
141
|
-
}
|
|
142
|
-
assert allow_redirects
|
|
143
|
-
assert not kwargs
|
|
144
|
-
mocked_res = AsyncMock()
|
|
145
|
-
mocked_res.json.return_value = task
|
|
146
|
-
yield mocked_res
|
|
147
|
-
|
|
148
|
-
monkeypatch.setattr(AiohttpClient, "_get", _get_and_assert)
|
|
149
|
-
|
|
150
|
-
task_client = DatashareTaskClient(datashare_url, auth=auth)
|
|
151
|
-
async with task_client:
|
|
152
|
-
# When
|
|
153
|
-
res = await task_client.get_task_state(task_id)
|
|
154
|
-
assert res == TaskState.DONE
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
async def test_task_client_get_task_result(monkeypatch: MonkeyPatch) -> None:
|
|
158
|
-
# Given
|
|
159
|
-
datashare_url = "http://some-url"
|
|
160
|
-
user = User(id="user")
|
|
161
|
-
api_key = "some-api-key"
|
|
162
|
-
auth = (user, api_key)
|
|
163
|
-
task_name = "hello"
|
|
164
|
-
task_id = f"{task_name}-{uuid.uuid4()}"
|
|
165
|
-
|
|
166
|
-
@asynccontextmanager
|
|
167
|
-
async def _get_and_assert(
|
|
168
|
-
_, # noqa: ANN001
|
|
169
|
-
url: StrOrURL,
|
|
170
|
-
*,
|
|
171
|
-
allow_redirects: bool = True,
|
|
172
|
-
**kwargs: Any,
|
|
173
|
-
) -> AsyncGenerator[AsyncMock, None]:
|
|
174
|
-
assert url == f"/api/task/{task_id}/result"
|
|
175
|
-
assert allow_redirects
|
|
176
|
-
assert not kwargs
|
|
177
|
-
mocked_res = AsyncMock()
|
|
178
|
-
mocked_res.json.return_value = "hellow world"
|
|
179
|
-
yield mocked_res
|
|
180
|
-
|
|
181
|
-
monkeypatch.setattr(AiohttpClient, "_get", _get_and_assert)
|
|
182
|
-
|
|
183
|
-
task_client = DatashareTaskClient(datashare_url, auth=auth)
|
|
184
|
-
async with task_client:
|
|
185
|
-
# When
|
|
186
|
-
res = await task_client.get_task_result(task_id)
|
|
187
|
-
assert res == "hellow world"
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
async def test_task_client_get_task_error(monkeypatch: MonkeyPatch) -> None:
|
|
191
|
-
# Given
|
|
192
|
-
datashare_url = "http://some-url"
|
|
193
|
-
user = User(id="user")
|
|
194
|
-
api_key = "some-api-key"
|
|
195
|
-
auth = (user, api_key)
|
|
196
|
-
task_name = "hello"
|
|
197
|
-
task_id = f"{task_name}-{uuid.uuid4()}"
|
|
198
|
-
|
|
199
|
-
@asynccontextmanager
|
|
200
|
-
async def _get_and_assert(
|
|
201
|
-
_, # noqa: ANN001
|
|
202
|
-
url: StrOrURL,
|
|
203
|
-
*,
|
|
204
|
-
allow_redirects: bool = True,
|
|
205
|
-
**kwargs: Any,
|
|
206
|
-
) -> AsyncGenerator[AsyncMock, None]:
|
|
207
|
-
assert url == f"/api/task/{task_id}"
|
|
208
|
-
task = {
|
|
209
|
-
"@type": "Task",
|
|
210
|
-
"id": task_id,
|
|
211
|
-
"state": "ERROR",
|
|
212
|
-
"createdAt": datetime.now(UTC),
|
|
213
|
-
"completedAt": datetime.now(UTC),
|
|
214
|
-
"name": "hello",
|
|
215
|
-
"args": {"greeted": "world"},
|
|
216
|
-
"error": {
|
|
217
|
-
"@type": "TaskError",
|
|
218
|
-
"name": "SomeError",
|
|
219
|
-
"message": "some error found",
|
|
220
|
-
"cause": "i'm the culprit",
|
|
221
|
-
"stacktrace": [{"lineno": 666, "file": "some_file.py", "name": "err"}],
|
|
222
|
-
},
|
|
223
|
-
}
|
|
224
|
-
assert allow_redirects
|
|
225
|
-
assert not kwargs
|
|
226
|
-
mocked_res = AsyncMock()
|
|
227
|
-
mocked_res.json.return_value = task
|
|
228
|
-
yield mocked_res
|
|
229
|
-
|
|
230
|
-
monkeypatch.setattr(AiohttpClient, "_get", _get_and_assert)
|
|
231
|
-
|
|
232
|
-
task_client = DatashareTaskClient(datashare_url, auth=auth)
|
|
233
|
-
async with task_client:
|
|
234
|
-
# When
|
|
235
|
-
error = await task_client.get_task_error(task_id)
|
|
236
|
-
expected_error = TaskError(
|
|
237
|
-
name="SomeError",
|
|
238
|
-
message="some error found",
|
|
239
|
-
cause="i'm the culprit",
|
|
240
|
-
stacktrace=[StacktraceItem(name="err", file="some_file.py", lineno=666)],
|
|
241
|
-
)
|
|
242
|
-
assert error == expected_error
|