datashare-python 0.9.8__tar.gz → 0.10.0__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.9.8 → datashare_python-0.10.0}/PKG-INFO +4 -3
- {datashare_python-0.9.8 → datashare_python-0.10.0}/datashare_python/cli/task.py +2 -2
- {datashare_python-0.9.8 → datashare_python-0.10.0}/datashare_python/cli/worker.py +1 -1
- {datashare_python-0.9.8 → datashare_python-0.10.0}/datashare_python/config.py +17 -7
- {datashare_python-0.9.8 → datashare_python-0.10.0}/datashare_python/conftest.py +178 -45
- {datashare_python-0.9.8 → datashare_python-0.10.0}/datashare_python/constants.py +1 -2
- {datashare_python-0.9.8 → datashare_python-0.10.0}/datashare_python/dependencies.py +0 -16
- {datashare_python-0.9.8 → datashare_python-0.10.0}/datashare_python/interceptors.py +1 -1
- {datashare_python-0.9.8 → datashare_python-0.10.0}/datashare_python/logging_.py +1 -1
- datashare_python-0.10.0/datashare_python/mimetypes_.py +1105 -0
- {datashare_python-0.9.8 → datashare_python-0.10.0}/datashare_python/objects.py +197 -68
- {datashare_python-0.9.8 → datashare_python-0.10.0}/datashare_python/utils.py +350 -47
- datashare_python-0.10.0/datashare_python/worker-template.tar.gz +0 -0
- {datashare_python-0.9.8 → datashare_python-0.10.0}/datashare_python/worker.py +14 -3
- {datashare_python-0.9.8 → datashare_python-0.10.0}/pyproject.toml +10 -5
- datashare_python-0.9.8/datashare_python/worker-template.tar.gz +0 -0
- {datashare_python-0.9.8 → datashare_python-0.10.0}/.gitignore +0 -0
- {datashare_python-0.9.8 → datashare_python-0.10.0}/README.md +0 -0
- {datashare_python-0.9.8 → datashare_python-0.10.0}/datashare_python/.gitignore +0 -0
- {datashare_python-0.9.8 → datashare_python-0.10.0}/datashare_python/__init__.py +0 -0
- {datashare_python-0.9.8 → datashare_python-0.10.0}/datashare_python/__main__.py +0 -0
- {datashare_python-0.9.8 → datashare_python-0.10.0}/datashare_python/cli/__init__.py +0 -0
- {datashare_python-0.9.8 → datashare_python-0.10.0}/datashare_python/cli/project.py +0 -0
- {datashare_python-0.9.8 → datashare_python-0.10.0}/datashare_python/cli/utils.py +0 -0
- {datashare_python-0.9.8 → datashare_python-0.10.0}/datashare_python/discovery.py +0 -0
- {datashare_python-0.9.8 → datashare_python-0.10.0}/datashare_python/exceptions.py +0 -0
- {datashare_python-0.9.8 → datashare_python-0.10.0}/datashare_python/task_client.py +0 -0
- {datashare_python-0.9.8 → datashare_python-0.10.0}/datashare_python/template.py +0 -0
- {datashare_python-0.9.8 → datashare_python-0.10.0}/datashare_python/types_.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: datashare-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.10.0
|
|
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/
|
|
@@ -8,15 +8,16 @@ Project-URL: Repository, https://github.com/ICIJ/datashare-python
|
|
|
8
8
|
Project-URL: Issues, https://github.com/ICIJ/datashare-python/issues
|
|
9
9
|
Author-email: Clément Doumouro <cdoumouro@icij.org>, Clément Doumouro <clement.doumouro@gmail.com>, Lion Summerbell <lsummerbell@icij.org>
|
|
10
10
|
Requires-Python: <4,>=3.11
|
|
11
|
+
Requires-Dist: aiofile~=3.11
|
|
11
12
|
Requires-Dist: aiohttp~=3.11
|
|
12
13
|
Requires-Dist: alive-progress~=3.2
|
|
13
14
|
Requires-Dist: hatchling~=1.27
|
|
14
|
-
Requires-Dist: icij-common[elasticsearch]~=0.8.
|
|
15
|
+
Requires-Dist: icij-common[elasticsearch]~=0.8.3
|
|
15
16
|
Requires-Dist: langcodes~=3.5
|
|
16
17
|
Requires-Dist: lru-dict~=1.4
|
|
17
18
|
Requires-Dist: pydantic-extra-types[pycountry]>=2.11.1
|
|
18
19
|
Requires-Dist: python-json-logger~=4.0
|
|
19
20
|
Requires-Dist: pyyaml~=6.0
|
|
20
|
-
Requires-Dist: temporalio~=1.
|
|
21
|
+
Requires-Dist: temporalio~=1.31
|
|
21
22
|
Requires-Dist: tomlkit~=0.14.0
|
|
22
23
|
Requires-Dist: typer<0.25.1,>=0.15.4
|
|
@@ -28,7 +28,7 @@ _START_HELP = "creates a new task and start it"
|
|
|
28
28
|
_TASK_ID_HELP = "task ID"
|
|
29
29
|
_WATCH_HELP = "watch a task until it's complete"
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
StrTaskArgs = str
|
|
32
32
|
|
|
33
33
|
task_app = AsyncTyper(name="task")
|
|
34
34
|
|
|
@@ -36,7 +36,7 @@ task_app = AsyncTyper(name="task")
|
|
|
36
36
|
@task_app.async_command(help=_START_HELP)
|
|
37
37
|
async def start(
|
|
38
38
|
name: Annotated[str, typer.Argument(help=_NAME_HELP)],
|
|
39
|
-
args: Annotated[
|
|
39
|
+
args: Annotated[StrTaskArgs, typer.Argument(help=_ARGS_HELP)] = None,
|
|
40
40
|
group: Annotated[
|
|
41
41
|
str | None,
|
|
42
42
|
typer.Option("--group", "-g", help=_GROUP_HELP),
|
|
@@ -78,7 +78,7 @@ to the documentation to learn how to do so."""
|
|
|
78
78
|
|
|
79
79
|
|
|
80
80
|
@worker_app.async_command(help=_START_WORKER_HELP)
|
|
81
|
-
async def start(
|
|
81
|
+
async def start( # noqa: PLR0917
|
|
82
82
|
queue: Annotated[str, typer.Option("--queue", "-q", help=_WORKER_QUEUE_HELP)],
|
|
83
83
|
workflows: Annotated[
|
|
84
84
|
list[str] | None,
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
from enum import StrEnum
|
|
2
|
-
from pathlib import Path
|
|
3
2
|
from typing import Literal
|
|
4
3
|
|
|
5
4
|
from icij_common.es import ESClient
|
|
@@ -10,10 +9,10 @@ from temporalio.runtime import PrometheusConfig, Runtime, TelemetryConfig
|
|
|
10
9
|
|
|
11
10
|
import datashare_python
|
|
12
11
|
|
|
13
|
-
from .objects import BaseModel
|
|
12
|
+
from .objects import BaseModel, WorkerPaths
|
|
14
13
|
from .task_client import DatashareTaskClient
|
|
15
14
|
from .types_ import TemporalClient
|
|
16
|
-
from .utils import PYDANTIC_DATA_CONVERTER
|
|
15
|
+
from .utils import PYDANTIC_DATA_CONVERTER, SharedResources, close_cm_callback
|
|
17
16
|
|
|
18
17
|
_ALL_LOGGERS = [datashare_python.__name__]
|
|
19
18
|
|
|
@@ -94,6 +93,19 @@ class LoggingConfig(BaseModel):
|
|
|
94
93
|
loggers: dict[str, LogLevel]
|
|
95
94
|
|
|
96
95
|
|
|
96
|
+
class ResourceCacheConfig(BaseModel):
|
|
97
|
+
size: int = 1
|
|
98
|
+
exit_context_managers: bool = True
|
|
99
|
+
|
|
100
|
+
def to_resource_cache(self) -> SharedResources:
|
|
101
|
+
eviction_callback = None
|
|
102
|
+
if self.exit_context_managers:
|
|
103
|
+
eviction_callback = close_cm_callback
|
|
104
|
+
return SharedResources(
|
|
105
|
+
cache_size=self.size, eviction_callback=eviction_callback
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
|
|
97
109
|
_DEFAULT_LOGGERS = {datashare_python.__name__: "INFO"}
|
|
98
110
|
_DEFAULT_LOGGING_CONFIG = LoggingConfig(
|
|
99
111
|
format=LogFormat.DEFAULT, loggers=_DEFAULT_LOGGERS
|
|
@@ -109,11 +121,9 @@ class WorkerConfig(ICIJSettings, BaseModel):
|
|
|
109
121
|
elasticsearch: ESClientConfig = ESClientConfig()
|
|
110
122
|
temporal: TemporalClientConfig = TemporalClientConfig()
|
|
111
123
|
|
|
112
|
-
|
|
124
|
+
max_concurrent_activities: int = 5
|
|
113
125
|
|
|
114
|
-
|
|
115
|
-
artifacts_root: Path | None = None
|
|
116
|
-
workdir: Path | None = None
|
|
126
|
+
paths: WorkerPaths | None = None
|
|
117
127
|
|
|
118
128
|
def to_es_client(self) -> ESClient:
|
|
119
129
|
return self.elasticsearch.to_es_client(self.datashare.api_key)
|
|
@@ -1,15 +1,22 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import faulthandler
|
|
3
|
+
import logging
|
|
4
|
+
import multiprocessing
|
|
1
5
|
import shutil
|
|
6
|
+
from asyncio import AbstractEventLoop
|
|
2
7
|
from collections.abc import AsyncGenerator, Generator, Sequence
|
|
8
|
+
from contextlib import AbstractAsyncContextManager, asynccontextmanager
|
|
9
|
+
from multiprocessing import Event
|
|
3
10
|
from pathlib import Path
|
|
4
11
|
|
|
5
12
|
import aiohttp
|
|
6
|
-
import nest_asyncio
|
|
7
13
|
import pytest
|
|
8
14
|
from elasticsearch._async.helpers import async_streaming_bulk
|
|
9
15
|
from icij_common.es import DOC_ROOT_ID, ES_DOCUMENT_TYPE, ID, ESClient
|
|
10
16
|
from icij_common.test_utils import reset_env # noqa: F401
|
|
11
17
|
from pytest_asyncio import is_async_test
|
|
12
18
|
from temporalio import workflow
|
|
19
|
+
from temporalio.runtime import PrometheusConfig, Runtime, TelemetryConfig
|
|
13
20
|
from temporalio.service import RPCError, RPCStatusCode
|
|
14
21
|
|
|
15
22
|
from datashare_python.config import (
|
|
@@ -19,17 +26,14 @@ from datashare_python.config import (
|
|
|
19
26
|
TemporalClientConfig,
|
|
20
27
|
WorkerConfig,
|
|
21
28
|
)
|
|
22
|
-
from datashare_python.
|
|
23
|
-
lifespan_es_client,
|
|
24
|
-
lifespan_task_client,
|
|
25
|
-
set_es_client,
|
|
26
|
-
set_task_client,
|
|
27
|
-
with_dependencies,
|
|
28
|
-
)
|
|
29
|
+
from datashare_python.discovery import discover
|
|
29
30
|
from datashare_python.objects import Document, TaskState
|
|
30
31
|
from datashare_python.task_client import DatashareTaskClient
|
|
31
|
-
from datashare_python.types_ import
|
|
32
|
-
from datashare_python.utils import activity_defn
|
|
32
|
+
from datashare_python.types_ import TemporalClient
|
|
33
|
+
from datashare_python.utils import PYDANTIC_DATA_CONVERTER, activity_defn
|
|
34
|
+
from datashare_python.worker import create_worker_id, worker_context
|
|
35
|
+
|
|
36
|
+
logger = logging.getLogger(__name__)
|
|
33
37
|
|
|
34
38
|
RABBITMQ_TEST_PORT = 5672
|
|
35
39
|
RABBITMQ_TEST_HOST = "localhost"
|
|
@@ -83,11 +87,6 @@ class MockedWorkflow:
|
|
|
83
87
|
return None
|
|
84
88
|
|
|
85
89
|
|
|
86
|
-
@pytest.fixture(scope="session")
|
|
87
|
-
def test_deps() -> list[ContextManagerFactory]:
|
|
88
|
-
return [set_es_client, set_task_client]
|
|
89
|
-
|
|
90
|
-
|
|
91
90
|
@pytest.fixture(scope="session")
|
|
92
91
|
def test_worker_config() -> WorkerConfig:
|
|
93
92
|
logging_config = LoggingConfig(
|
|
@@ -113,25 +112,8 @@ def test_worker_config_path(test_worker_config: WorkerConfig, tmpdir: Path) -> P
|
|
|
113
112
|
|
|
114
113
|
|
|
115
114
|
@pytest.fixture(scope="session")
|
|
116
|
-
async def
|
|
117
|
-
|
|
118
|
-
) -> AsyncGenerator[None, None]:
|
|
119
|
-
worker_id = "test-worker-id"
|
|
120
|
-
ctx = "test application"
|
|
121
|
-
async with with_dependencies(
|
|
122
|
-
test_deps,
|
|
123
|
-
ctx=ctx,
|
|
124
|
-
worker_id=worker_id,
|
|
125
|
-
worker_config=test_worker_config,
|
|
126
|
-
):
|
|
127
|
-
yield
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
@pytest.fixture(scope="session")
|
|
131
|
-
async def test_es_client_session(
|
|
132
|
-
worker_lifetime_deps, # noqa: ANN001, ARG001
|
|
133
|
-
) -> ESClient:
|
|
134
|
-
es = lifespan_es_client()
|
|
115
|
+
async def test_es_client_session(test_worker_config: WorkerConfig) -> ESClient:
|
|
116
|
+
es = test_worker_config.to_es_client()
|
|
135
117
|
await es.indices.delete(index="_all")
|
|
136
118
|
await es.indices.create(index=TEST_PROJECT, body=_INDEX_BODY)
|
|
137
119
|
return es
|
|
@@ -147,9 +129,9 @@ async def test_es_client(test_es_client_session: ESClient) -> ESClient:
|
|
|
147
129
|
|
|
148
130
|
@pytest.fixture(scope="session")
|
|
149
131
|
async def test_task_client_session(
|
|
150
|
-
|
|
132
|
+
test_worker_config: WorkerConfig,
|
|
151
133
|
) -> AsyncGenerator[DatashareTaskClient, None]:
|
|
152
|
-
task_client =
|
|
134
|
+
task_client = test_worker_config.to_task_client()
|
|
153
135
|
async with task_client:
|
|
154
136
|
user, key = await task_client.create_api_key()
|
|
155
137
|
task_client.authenticate(user, key)
|
|
@@ -188,17 +170,24 @@ async def test_temporal_client(
|
|
|
188
170
|
|
|
189
171
|
|
|
190
172
|
@pytest.fixture
|
|
191
|
-
async def
|
|
192
|
-
test_es_client: ESClient,
|
|
173
|
+
async def indexed_docs( # noqa: PLR0917
|
|
193
174
|
doc_0: Document,
|
|
194
175
|
doc_1: Document,
|
|
195
176
|
doc_2: Document,
|
|
196
177
|
doc_3: Document,
|
|
197
178
|
) -> list[Document]:
|
|
198
|
-
|
|
199
|
-
|
|
179
|
+
return [doc_0, doc_1, doc_2, doc_3]
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
@pytest.fixture
|
|
183
|
+
async def populate_es(
|
|
184
|
+
test_es_client: ESClient, indexed_docs: list[Document]
|
|
185
|
+
) -> list[Document]:
|
|
186
|
+
async for _ in index_docs(
|
|
187
|
+
test_es_client, docs=indexed_docs, index_name=TEST_PROJECT
|
|
188
|
+
):
|
|
200
189
|
pass
|
|
201
|
-
return
|
|
190
|
+
return indexed_docs
|
|
202
191
|
|
|
203
192
|
|
|
204
193
|
def index_docs_ops(
|
|
@@ -309,11 +298,155 @@ async def all_done(task_client: DatashareTaskClient, not_done: list[str]) -> boo
|
|
|
309
298
|
|
|
310
299
|
@pytest.fixture # noqa: F405
|
|
311
300
|
def typer_asyncio_patch() -> None:
|
|
301
|
+
import nest_asyncio # noqa: PLC0415
|
|
302
|
+
|
|
312
303
|
nest_asyncio.apply()
|
|
313
304
|
|
|
314
305
|
|
|
315
306
|
def clear_dirs(config: WorkerConfig) -> None:
|
|
316
|
-
shutil.rmtree(str(config.
|
|
317
|
-
config.
|
|
318
|
-
shutil.rmtree(str(config.
|
|
319
|
-
config.
|
|
307
|
+
shutil.rmtree(str(config.paths.filesystem))
|
|
308
|
+
config.paths.filesystem.mkdir(parents=True, exist_ok=True)
|
|
309
|
+
shutil.rmtree(str(config.paths.artifacts))
|
|
310
|
+
config.paths.artifacts.mkdir(parents=True, exist_ok=True)
|
|
311
|
+
shutil.rmtree(str(config.paths.workdir))
|
|
312
|
+
config.paths.workdir.mkdir(parents=True, exist_ok=True)
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def dev_worker_context(
|
|
316
|
+
worker_id_prefix: str,
|
|
317
|
+
*,
|
|
318
|
+
is_async: bool,
|
|
319
|
+
client: TemporalClient,
|
|
320
|
+
activities: list[str] | None = None,
|
|
321
|
+
workflows: list[str] | None = None,
|
|
322
|
+
worker_config: WorkerConfig,
|
|
323
|
+
event_loop: AbstractEventLoop | None = None,
|
|
324
|
+
task_queue: str,
|
|
325
|
+
dependencies: str | None = None,
|
|
326
|
+
sandboxed: bool = True,
|
|
327
|
+
) -> AbstractAsyncContextManager[None]:
|
|
328
|
+
worker_id = create_worker_id(worker_id_prefix)
|
|
329
|
+
if not is_async:
|
|
330
|
+
return _run_worker_in_separate_process(
|
|
331
|
+
worker_id=worker_id,
|
|
332
|
+
activities=activities,
|
|
333
|
+
workflows=workflows,
|
|
334
|
+
worker_config=worker_config.model_dump(),
|
|
335
|
+
task_queue=task_queue,
|
|
336
|
+
dependencies=dependencies,
|
|
337
|
+
sandboxed=sandboxed,
|
|
338
|
+
)
|
|
339
|
+
registered_wfs, registered_acts, registered_deps, worker_config_cls = discover(
|
|
340
|
+
workflows,
|
|
341
|
+
act_names=activities,
|
|
342
|
+
deps_name=dependencies,
|
|
343
|
+
)
|
|
344
|
+
cm = _erase_type(
|
|
345
|
+
worker_context(
|
|
346
|
+
worker_id,
|
|
347
|
+
activities=registered_acts,
|
|
348
|
+
workflows=registered_wfs,
|
|
349
|
+
dependencies=registered_deps,
|
|
350
|
+
worker_config=worker_config,
|
|
351
|
+
client=client,
|
|
352
|
+
event_loop=event_loop,
|
|
353
|
+
task_queue=task_queue,
|
|
354
|
+
)
|
|
355
|
+
)
|
|
356
|
+
return cm
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
@asynccontextmanager
|
|
360
|
+
async def _erase_type(cm: AbstractAsyncContextManager) -> AsyncGenerator[None, None]:
|
|
361
|
+
async with cm:
|
|
362
|
+
yield
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
@asynccontextmanager
|
|
366
|
+
async def _run_worker_in_separate_process(
|
|
367
|
+
worker_id: str,
|
|
368
|
+
*,
|
|
369
|
+
activities: list[str] | None = None,
|
|
370
|
+
workflows: list[str] | None = None,
|
|
371
|
+
worker_config: dict,
|
|
372
|
+
task_queue: str,
|
|
373
|
+
dependencies: str | None = None,
|
|
374
|
+
sandboxed: bool = True,
|
|
375
|
+
) -> AsyncGenerator[None, None]:
|
|
376
|
+
ctx = multiprocessing.get_context("spawn")
|
|
377
|
+
shutdown_event = ctx.Event()
|
|
378
|
+
faulthandler.enable()
|
|
379
|
+
kwargs = {
|
|
380
|
+
"worker_id": worker_id,
|
|
381
|
+
"activities": activities,
|
|
382
|
+
"workflows": workflows,
|
|
383
|
+
"worker_config": worker_config,
|
|
384
|
+
"task_queue": task_queue,
|
|
385
|
+
"dependencies": dependencies,
|
|
386
|
+
"sandboxed": sandboxed,
|
|
387
|
+
"shutdown_event": shutdown_event,
|
|
388
|
+
}
|
|
389
|
+
p = ctx.Process(target=_run_worker_process, kwargs=kwargs)
|
|
390
|
+
p.start()
|
|
391
|
+
try:
|
|
392
|
+
yield
|
|
393
|
+
finally:
|
|
394
|
+
shutdown_event.set()
|
|
395
|
+
p.join()
|
|
396
|
+
if p.is_alive():
|
|
397
|
+
p.terminate()
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
def _run_worker_process(
|
|
401
|
+
worker_id: str,
|
|
402
|
+
*,
|
|
403
|
+
activities: list[str] | None = None,
|
|
404
|
+
workflows: list[str] | None = None,
|
|
405
|
+
worker_config: dict,
|
|
406
|
+
event_loop: AbstractEventLoop | None = None,
|
|
407
|
+
task_queue: str,
|
|
408
|
+
dependencies: str | None = None,
|
|
409
|
+
sandboxed: bool = True,
|
|
410
|
+
shutdown_event: Event,
|
|
411
|
+
) -> None:
|
|
412
|
+
async def _run() -> None:
|
|
413
|
+
registered_wfs, registered_acts, registered_deps, worker_config_cls = discover(
|
|
414
|
+
workflows,
|
|
415
|
+
act_names=activities,
|
|
416
|
+
deps_name=dependencies,
|
|
417
|
+
)
|
|
418
|
+
loaded_config = worker_config_cls.model_validate(worker_config)
|
|
419
|
+
temporal_config = loaded_config.temporal
|
|
420
|
+
runtime = Runtime(telemetry=TelemetryConfig())
|
|
421
|
+
# We don't use config.to_temporal_client since we get a problem with the
|
|
422
|
+
# temporal runtime not being properly recreated, we create it explicitely
|
|
423
|
+
if temporal_config.prometheus_host is not None:
|
|
424
|
+
telemetry_config = TelemetryConfig(
|
|
425
|
+
metrics=PrometheusConfig(bind_address="0.0.0.0:9000")
|
|
426
|
+
)
|
|
427
|
+
runtime = Runtime(telemetry=telemetry_config)
|
|
428
|
+
client = await TemporalClient.connect(
|
|
429
|
+
target_host=temporal_config.host,
|
|
430
|
+
namespace=temporal_config.namespace,
|
|
431
|
+
runtime=runtime,
|
|
432
|
+
data_converter=PYDANTIC_DATA_CONVERTER,
|
|
433
|
+
)
|
|
434
|
+
worker_ctx = worker_context(
|
|
435
|
+
worker_id,
|
|
436
|
+
activities=registered_acts,
|
|
437
|
+
workflows=registered_wfs,
|
|
438
|
+
worker_config=loaded_config,
|
|
439
|
+
client=client,
|
|
440
|
+
event_loop=event_loop,
|
|
441
|
+
task_queue=task_queue,
|
|
442
|
+
dependencies=registered_deps,
|
|
443
|
+
sandboxed=sandboxed,
|
|
444
|
+
)
|
|
445
|
+
async with worker_ctx as worker:
|
|
446
|
+
aws = [
|
|
447
|
+
asyncio.create_task(asyncio.to_thread(shutdown_event.wait)),
|
|
448
|
+
asyncio.create_task(worker.is_done()),
|
|
449
|
+
]
|
|
450
|
+
await asyncio.wait(aws, return_when=asyncio.FIRST_COMPLETED)
|
|
451
|
+
|
|
452
|
+
asyncio.run(_run())
|
|
@@ -11,7 +11,6 @@ DEFAULT_DS_ADDRESS = "http://localhost:8080"
|
|
|
11
11
|
DEFAULT_NAMESPACE = "datashare-default"
|
|
12
12
|
|
|
13
13
|
METADATA_JSON = "metadata.json"
|
|
14
|
+
MANIFEST_JSON = "manifest.json"
|
|
14
15
|
|
|
15
16
|
TIKA_METADATA_RESOURCENAME = "tika_metadata_resourcename"
|
|
16
|
-
|
|
17
|
-
DEFAULT_SHARED_RESOURCES_SIZE = 1
|
|
@@ -12,7 +12,6 @@ from icij_common.es import ESClient
|
|
|
12
12
|
from .config import LogLevel, WorkerConfig
|
|
13
13
|
from .exceptions import DependencyInjectionError
|
|
14
14
|
from .logging_ import setup_worker_loggers
|
|
15
|
-
from .objects import Shared
|
|
16
15
|
from .task_client import DatashareTaskClient
|
|
17
16
|
from .types_ import ContextManagerFactory, TemporalClient
|
|
18
17
|
|
|
@@ -24,7 +23,6 @@ ES_CLIENT: ContextVar[ESClient] = ContextVar("es_client")
|
|
|
24
23
|
TASK_CLIENT: ContextVar[DatashareTaskClient] = ContextVar("task_client")
|
|
25
24
|
TEMPORAL_CLIENT: ContextVar[TemporalClient] = ContextVar("temporal_client")
|
|
26
25
|
WORKER_CONFIG: ContextVar[WorkerConfig] = ContextVar("worker_config")
|
|
27
|
-
SHARED: ContextVar[Shared] = ContextVar("shared")
|
|
28
26
|
|
|
29
27
|
|
|
30
28
|
def set_event_loop(event_loop: AbstractEventLoop) -> None:
|
|
@@ -101,20 +99,6 @@ def lifespan_temporal_client() -> TemporalClient:
|
|
|
101
99
|
raise DependencyInjectionError("temporal client") from e
|
|
102
100
|
|
|
103
101
|
|
|
104
|
-
# Setup shared resources
|
|
105
|
-
async def set_shared_resources(shared: Shared) -> Shared:
|
|
106
|
-
SHARED.set(shared)
|
|
107
|
-
return shared
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
# Return shared resources
|
|
111
|
-
def lifespan_shared_resources() -> Shared:
|
|
112
|
-
try:
|
|
113
|
-
return SHARED.get()
|
|
114
|
-
except LookupError as e:
|
|
115
|
-
raise DependencyInjectionError("shared resources") from e
|
|
116
|
-
|
|
117
|
-
|
|
118
102
|
@asynccontextmanager
|
|
119
103
|
async def with_dependencies(
|
|
120
104
|
dependencies: list[ContextManagerFactory], **kwargs
|
|
@@ -219,7 +219,7 @@ def _trace_context(headers: Mapping[str, Payload]) -> Generator[None, None, None
|
|
|
219
219
|
InputWithHeaders = TypeVar("InputWithHeaders")
|
|
220
220
|
|
|
221
221
|
|
|
222
|
-
def _with_trace_context_header(
|
|
222
|
+
def _with_trace_context_header[InputWithHeaders](
|
|
223
223
|
input_with_headers: InputWithHeaders,
|
|
224
224
|
) -> InputWithHeaders:
|
|
225
225
|
ctx = get_trace_context()
|
|
@@ -132,7 +132,7 @@ def _encode_value(value: Any) -> str:
|
|
|
132
132
|
return "true" if value else "false"
|
|
133
133
|
if isinstance(value, numbers.Number):
|
|
134
134
|
return str(value)
|
|
135
|
-
return json.dumps(value)
|
|
135
|
+
return json.dumps(value)
|
|
136
136
|
|
|
137
137
|
|
|
138
138
|
def _json_formatter(datefmt: str) -> BaseJsonFormatter:
|