datashare-python 0.10.0.dev0__tar.gz → 0.10.1__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.10.0.dev0 → datashare_python-0.10.1}/PKG-INFO +1 -1
- {datashare_python-0.10.0.dev0 → datashare_python-0.10.1}/datashare_python/config.py +1 -0
- {datashare_python-0.10.0.dev0 → datashare_python-0.10.1}/datashare_python/conftest.py +3 -2
- {datashare_python-0.10.0.dev0 → datashare_python-0.10.1}/datashare_python/interceptors.py +85 -33
- {datashare_python-0.10.0.dev0 → datashare_python-0.10.1}/datashare_python/objects.py +2 -2
- {datashare_python-0.10.0.dev0 → datashare_python-0.10.1}/datashare_python/types_.py +13 -7
- {datashare_python-0.10.0.dev0 → datashare_python-0.10.1}/datashare_python/utils.py +16 -16
- datashare_python-0.10.1/datashare_python/worker-template.tar.gz +0 -0
- {datashare_python-0.10.0.dev0 → datashare_python-0.10.1}/datashare_python/worker.py +3 -1
- {datashare_python-0.10.0.dev0 → datashare_python-0.10.1}/pyproject.toml +1 -1
- datashare_python-0.10.0.dev0/datashare_python/worker-template.tar.gz +0 -0
- {datashare_python-0.10.0.dev0 → datashare_python-0.10.1}/.gitignore +0 -0
- {datashare_python-0.10.0.dev0 → datashare_python-0.10.1}/README.md +0 -0
- {datashare_python-0.10.0.dev0 → datashare_python-0.10.1}/datashare_python/.gitignore +0 -0
- {datashare_python-0.10.0.dev0 → datashare_python-0.10.1}/datashare_python/__init__.py +0 -0
- {datashare_python-0.10.0.dev0 → datashare_python-0.10.1}/datashare_python/__main__.py +0 -0
- {datashare_python-0.10.0.dev0 → datashare_python-0.10.1}/datashare_python/cli/__init__.py +0 -0
- {datashare_python-0.10.0.dev0 → datashare_python-0.10.1}/datashare_python/cli/project.py +0 -0
- {datashare_python-0.10.0.dev0 → datashare_python-0.10.1}/datashare_python/cli/task.py +0 -0
- {datashare_python-0.10.0.dev0 → datashare_python-0.10.1}/datashare_python/cli/utils.py +0 -0
- {datashare_python-0.10.0.dev0 → datashare_python-0.10.1}/datashare_python/cli/worker.py +0 -0
- {datashare_python-0.10.0.dev0 → datashare_python-0.10.1}/datashare_python/constants.py +0 -0
- {datashare_python-0.10.0.dev0 → datashare_python-0.10.1}/datashare_python/dependencies.py +0 -0
- {datashare_python-0.10.0.dev0 → datashare_python-0.10.1}/datashare_python/discovery.py +0 -0
- {datashare_python-0.10.0.dev0 → datashare_python-0.10.1}/datashare_python/exceptions.py +0 -0
- {datashare_python-0.10.0.dev0 → datashare_python-0.10.1}/datashare_python/logging_.py +0 -0
- {datashare_python-0.10.0.dev0 → datashare_python-0.10.1}/datashare_python/mimetypes_.py +0 -0
- {datashare_python-0.10.0.dev0 → datashare_python-0.10.1}/datashare_python/task_client.py +0 -0
- {datashare_python-0.10.0.dev0 → datashare_python-0.10.1}/datashare_python/template.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: datashare-python
|
|
3
|
-
Version: 0.10.
|
|
3
|
+
Version: 0.10.1
|
|
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/
|
|
@@ -10,7 +10,6 @@ from multiprocessing import Event
|
|
|
10
10
|
from pathlib import Path
|
|
11
11
|
|
|
12
12
|
import aiohttp
|
|
13
|
-
import nest_asyncio
|
|
14
13
|
import pytest
|
|
15
14
|
from elasticsearch._async.helpers import async_streaming_bulk
|
|
16
15
|
from icij_common.es import DOC_ROOT_ID, ES_DOCUMENT_TYPE, ID, ESClient
|
|
@@ -171,7 +170,7 @@ async def test_temporal_client(
|
|
|
171
170
|
|
|
172
171
|
|
|
173
172
|
@pytest.fixture
|
|
174
|
-
async def indexed_docs(
|
|
173
|
+
async def indexed_docs( # noqa: PLR0917
|
|
175
174
|
doc_0: Document,
|
|
176
175
|
doc_1: Document,
|
|
177
176
|
doc_2: Document,
|
|
@@ -299,6 +298,8 @@ async def all_done(task_client: DatashareTaskClient, not_done: list[str]) -> boo
|
|
|
299
298
|
|
|
300
299
|
@pytest.fixture # noqa: F405
|
|
301
300
|
def typer_asyncio_patch() -> None:
|
|
301
|
+
import nest_asyncio # noqa: PLC0415
|
|
302
|
+
|
|
302
303
|
nest_asyncio.apply()
|
|
303
304
|
|
|
304
305
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
-
import contextlib
|
|
3
2
|
import dataclasses
|
|
3
|
+
import datetime
|
|
4
4
|
import secrets
|
|
5
5
|
from collections.abc import Callable, Generator, Mapping
|
|
6
6
|
from contextlib import contextmanager
|
|
7
7
|
from contextvars import ContextVar
|
|
8
8
|
from copy import deepcopy
|
|
9
|
-
from functools import
|
|
9
|
+
from functools import wraps
|
|
10
10
|
from inspect import signature
|
|
11
11
|
from types import UnionType
|
|
12
12
|
from typing import (
|
|
@@ -29,6 +29,7 @@ from temporalio.client import WorkflowHandle
|
|
|
29
29
|
from temporalio.converter import DataConverter
|
|
30
30
|
from temporalio.worker import (
|
|
31
31
|
ActivityInboundInterceptor,
|
|
32
|
+
ActivityOutboundInterceptor,
|
|
32
33
|
ContinueAsNewInput,
|
|
33
34
|
ExecuteActivityInput,
|
|
34
35
|
ExecuteWorkflowInput,
|
|
@@ -219,7 +220,7 @@ def _trace_context(headers: Mapping[str, Payload]) -> Generator[None, None, None
|
|
|
219
220
|
InputWithHeaders = TypeVar("InputWithHeaders")
|
|
220
221
|
|
|
221
222
|
|
|
222
|
-
def _with_trace_context_header(
|
|
223
|
+
def _with_trace_context_header[InputWithHeaders](
|
|
223
224
|
input_with_headers: InputWithHeaders,
|
|
224
225
|
) -> InputWithHeaders:
|
|
225
226
|
ctx = get_trace_context()
|
|
@@ -234,11 +235,14 @@ def _with_trace_context_header(
|
|
|
234
235
|
|
|
235
236
|
|
|
236
237
|
class ProgressInterceptor(Interceptor):
|
|
238
|
+
def __init__(self, min_progress_interval_s: float = 30.0):
|
|
239
|
+
self._min_progress_interval_s: float = min_progress_interval_s
|
|
240
|
+
|
|
237
241
|
def intercept_activity(
|
|
238
242
|
self,
|
|
239
243
|
next: ActivityInboundInterceptor, # noqa: A002
|
|
240
244
|
) -> ActivityInboundInterceptor:
|
|
241
|
-
return _ProgressInboundInterceptor(next)
|
|
245
|
+
return _ProgressInboundInterceptor(next, self._min_progress_interval_s)
|
|
242
246
|
|
|
243
247
|
|
|
244
248
|
def _parse_progress_weight(act_fn: Callable) -> float:
|
|
@@ -254,18 +258,41 @@ def _parse_progress_weight(act_fn: Callable) -> float:
|
|
|
254
258
|
return 1.0
|
|
255
259
|
|
|
256
260
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
261
|
+
class TemporalProgressHandler:
|
|
262
|
+
def __init__(
|
|
263
|
+
self,
|
|
264
|
+
handle: WorkflowHandle,
|
|
265
|
+
activity_id: str,
|
|
266
|
+
*,
|
|
267
|
+
run_id: str,
|
|
268
|
+
min_progress_interval_s: float = 30.0,
|
|
269
|
+
weight: float = 1.0,
|
|
270
|
+
) -> None:
|
|
271
|
+
self._handle = handle
|
|
272
|
+
self._activity_id = activity_id
|
|
273
|
+
self._run_id = run_id
|
|
274
|
+
self._weight = weight
|
|
275
|
+
self._min_progress_interval_s = min_progress_interval_s
|
|
276
|
+
self._last: datetime.datetime | None = None
|
|
277
|
+
|
|
278
|
+
async def progress(self, progress: float, *, force: bool = False) -> None:
|
|
279
|
+
# TODO: we could lock here to avoid race conditions, it's not critical though
|
|
280
|
+
now = datetime.datetime.now(datetime.UTC)
|
|
281
|
+
report_progress = (
|
|
282
|
+
force
|
|
283
|
+
or self._last is None
|
|
284
|
+
or (now - self._last).total_seconds() >= self._min_progress_interval_s
|
|
285
|
+
)
|
|
286
|
+
if not report_progress:
|
|
287
|
+
return
|
|
288
|
+
self._last = now
|
|
289
|
+
signal = ProgressSignal(
|
|
290
|
+
activity_id=self._activity_id,
|
|
291
|
+
run_id=self._run_id,
|
|
292
|
+
progress=progress,
|
|
293
|
+
weight=self._weight,
|
|
294
|
+
)
|
|
295
|
+
await self._handle.signal("update_progress", signal)
|
|
269
296
|
|
|
270
297
|
|
|
271
298
|
def supports_progress(task_fn: Callable) -> bool:
|
|
@@ -275,7 +302,9 @@ def supports_progress(task_fn: Callable) -> bool:
|
|
|
275
302
|
)
|
|
276
303
|
|
|
277
304
|
|
|
278
|
-
def _get_progress_handler(
|
|
305
|
+
def _get_progress_handler(
|
|
306
|
+
act_fn: Callable, min_progress_interval_s: float
|
|
307
|
+
) -> ProgressRateHandler:
|
|
279
308
|
act = getattr(act_fn, "__self__", None)
|
|
280
309
|
# Weirdly isinstance doesn't work here
|
|
281
310
|
if act is None or not isinstance(act, ActivityWithProgress):
|
|
@@ -291,14 +320,14 @@ def _get_progress_handler(act_fn: Callable) -> ProgressRateHandler:
|
|
|
291
320
|
activity_id = activity.info().activity_id
|
|
292
321
|
client = act._temporal_client
|
|
293
322
|
workflow_handle = client.get_workflow_handle(workflow_id, run_id=run_id)
|
|
294
|
-
handler =
|
|
295
|
-
|
|
296
|
-
|
|
323
|
+
handler = TemporalProgressHandler(
|
|
324
|
+
workflow_handle,
|
|
325
|
+
activity_id,
|
|
297
326
|
run_id=run_id,
|
|
298
|
-
activity_id=activity_id,
|
|
299
327
|
weight=weight,
|
|
328
|
+
min_progress_interval_s=min_progress_interval_s,
|
|
300
329
|
)
|
|
301
|
-
return handler
|
|
330
|
+
return handler.progress
|
|
302
331
|
|
|
303
332
|
|
|
304
333
|
def _is_progress(t: type) -> bool:
|
|
@@ -320,13 +349,23 @@ def _without_progress(arg_types: list[type] | None) -> list[type] | None:
|
|
|
320
349
|
|
|
321
350
|
|
|
322
351
|
class _ProgressInboundInterceptor(ActivityInboundInterceptor):
|
|
352
|
+
def __init__(
|
|
353
|
+
self,
|
|
354
|
+
next: ActivityInboundInterceptor, # noqa: A002
|
|
355
|
+
min_progress_interval_s: float,
|
|
356
|
+
) -> None:
|
|
357
|
+
super().__init__(next)
|
|
358
|
+
self._min_progress_interval_s = min_progress_interval_s
|
|
359
|
+
|
|
323
360
|
async def execute_activity(self, input: ExecuteActivityInput) -> Any: # noqa: A002
|
|
324
361
|
if not supports_progress(input.fn):
|
|
325
362
|
return await super().execute_activity(input)
|
|
326
363
|
# The progress args breaks trigger a bypass of the dataloader:
|
|
327
364
|
# https://github.com/temporalio/sdk-python/blob/631ebaf0e20fb214b16589b45627b358048a5d77/temporalio/worker/_activity.py#L600
|
|
328
365
|
# we have to force it here again
|
|
329
|
-
progress_handler = _get_progress_handler(
|
|
366
|
+
progress_handler = _get_progress_handler(
|
|
367
|
+
input.fn, self._min_progress_interval_s
|
|
368
|
+
)
|
|
330
369
|
new_args = []
|
|
331
370
|
act_definition = _Definition.must_from_callable(input.fn)
|
|
332
371
|
if input.args:
|
|
@@ -343,9 +382,9 @@ class _ProgressInboundInterceptor(ActivityInboundInterceptor):
|
|
|
343
382
|
)
|
|
344
383
|
new_args.append(injected_progress)
|
|
345
384
|
new_input = dataclasses.replace(input, args=new_args)
|
|
346
|
-
await progress_handler(0.0)
|
|
385
|
+
await progress_handler(0.0, force=True)
|
|
347
386
|
res = await super().execute_activity(new_input)
|
|
348
|
-
await progress_handler(1.0)
|
|
387
|
+
await progress_handler(1.0, force=True)
|
|
349
388
|
return res
|
|
350
389
|
|
|
351
390
|
|
|
@@ -360,13 +399,13 @@ class HeartbeatInterceptor(Interceptor):
|
|
|
360
399
|
return _HeartbeatInboundInterceptor(next, self._n_missed_before_timeout)
|
|
361
400
|
|
|
362
401
|
|
|
363
|
-
async def _heartbeat_every(
|
|
364
|
-
|
|
365
|
-
|
|
402
|
+
async def _heartbeat_every(
|
|
403
|
+
heartbeat_fn: Callable[[Any], None], period: float, *details: Any
|
|
404
|
+
) -> None:
|
|
405
|
+
heartbeat_fn(*details)
|
|
366
406
|
while True:
|
|
367
407
|
await asyncio.sleep(period)
|
|
368
|
-
|
|
369
|
-
activity.heartbeat(*details)
|
|
408
|
+
heartbeat_fn(*details)
|
|
370
409
|
|
|
371
410
|
|
|
372
411
|
class _HeartbeatInboundInterceptor(ActivityInboundInterceptor):
|
|
@@ -377,13 +416,24 @@ class _HeartbeatInboundInterceptor(ActivityInboundInterceptor):
|
|
|
377
416
|
) -> None:
|
|
378
417
|
super().__init__(next)
|
|
379
418
|
self._n_missed_before_timeout = n_missed_before_timeout
|
|
419
|
+
self._outbound: ActivityOutboundInterceptor | None = None
|
|
420
|
+
|
|
421
|
+
def init(self, outbound: ActivityOutboundInterceptor) -> None:
|
|
422
|
+
super().init(outbound)
|
|
423
|
+
self._outbound = outbound
|
|
380
424
|
|
|
381
425
|
async def execute_activity(self, input: ExecuteActivityInput) -> Any: # noqa: A002
|
|
426
|
+
# We need to capture the output interceptor to access the outbound hearbeat
|
|
427
|
+
# function. This inbound interceptor runs on the temporal event loop side.
|
|
428
|
+
# We want the heartbeat to run on the worker thread, on on the temporal event
|
|
429
|
+
# loop !
|
|
382
430
|
heartbeat_timeout = activity.info().heartbeat_timeout
|
|
383
431
|
heartbeat_task = None
|
|
384
432
|
if heartbeat_timeout:
|
|
385
433
|
period = heartbeat_timeout.total_seconds() / self._n_missed_before_timeout
|
|
386
|
-
heartbeat_task = asyncio.create_task(
|
|
434
|
+
heartbeat_task = asyncio.create_task(
|
|
435
|
+
_heartbeat_every(self._outbound.heartbeat, period)
|
|
436
|
+
)
|
|
387
437
|
try:
|
|
388
438
|
activity.heartbeat()
|
|
389
439
|
return await super().execute_activity(input)
|
|
@@ -397,9 +447,11 @@ def _sync_progress(
|
|
|
397
447
|
progress_handler: AsyncProgressRateHandler,
|
|
398
448
|
) -> SyncProgressRateHandler:
|
|
399
449
|
@wraps(progress_handler)
|
|
400
|
-
def p(
|
|
450
|
+
def p(
|
|
451
|
+
progress: float, event_loop: asyncio.AbstractEventLoop, *, force: bool = False
|
|
452
|
+
) -> None:
|
|
401
453
|
asyncio.run_coroutine_threadsafe(
|
|
402
|
-
progress_handler(progress), event_loop
|
|
454
|
+
progress_handler(progress, force=force), event_loop
|
|
403
455
|
).result()
|
|
404
456
|
|
|
405
457
|
return p
|
|
@@ -7,7 +7,7 @@ from datetime import UTC, datetime
|
|
|
7
7
|
from enum import StrEnum, unique
|
|
8
8
|
from io import BytesIO
|
|
9
9
|
from pathlib import Path
|
|
10
|
-
from typing import Annotated, Any, ClassVar,
|
|
10
|
+
from typing import Annotated, Any, ClassVar, Literal, Self, TypeVar, cast
|
|
11
11
|
|
|
12
12
|
import langcodes
|
|
13
13
|
from icij_common.registrable import Registrable
|
|
@@ -323,7 +323,7 @@ class TaskArgs(DatashareModel, ABC):
|
|
|
323
323
|
A = TypeVar("A", bound=TaskArgs)
|
|
324
324
|
|
|
325
325
|
|
|
326
|
-
class ManifestEntry
|
|
326
|
+
class ManifestEntry[A](DatashareModel, ABC):
|
|
327
327
|
status: ManifestEntryStatus
|
|
328
328
|
label: str | None = None
|
|
329
329
|
input: Annotated[
|
|
@@ -10,15 +10,17 @@ TemporalClient = Client
|
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class AsyncProgressRateHandler(Protocol):
|
|
13
|
-
async def __call__(self, progress_rate: float) -> None:
|
|
14
|
-
pass
|
|
13
|
+
async def __call__(self, progress_rate: float, *, force: bool = False) -> None: ...
|
|
15
14
|
|
|
16
15
|
|
|
17
16
|
class SyncProgressRateHandler(Protocol):
|
|
18
17
|
def __call__(
|
|
19
|
-
self,
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
self,
|
|
19
|
+
progress_rate: float,
|
|
20
|
+
event_loop: asyncio.AbstractEventLoop,
|
|
21
|
+
*,
|
|
22
|
+
force: bool = False,
|
|
23
|
+
) -> None: ...
|
|
22
24
|
|
|
23
25
|
|
|
24
26
|
ProgressRateHandler = SyncProgressRateHandler | AsyncProgressRateHandler
|
|
@@ -30,12 +32,16 @@ class Weight:
|
|
|
30
32
|
|
|
31
33
|
|
|
32
34
|
class RawAsyncProgressHandler(Protocol):
|
|
33
|
-
async def __call__(self, iteration: int) -> None: ...
|
|
35
|
+
async def __call__(self, iteration: int, *, force: bool = False) -> None: ...
|
|
34
36
|
|
|
35
37
|
|
|
36
38
|
class RawSyncProgressHandler(Protocol):
|
|
37
39
|
async def __call__(
|
|
38
|
-
self,
|
|
40
|
+
self,
|
|
41
|
+
iteration: int,
|
|
42
|
+
event_loop: asyncio.AbstractEventLoop,
|
|
43
|
+
*,
|
|
44
|
+
force: bool = False,
|
|
39
45
|
) -> None: ...
|
|
40
46
|
|
|
41
47
|
|
|
@@ -25,7 +25,7 @@ from functools import cache, wraps
|
|
|
25
25
|
from hashlib import sha256
|
|
26
26
|
from io import BytesIO
|
|
27
27
|
from pathlib import Path
|
|
28
|
-
from typing import Any,
|
|
28
|
+
from typing import Any, Self, TypeVar
|
|
29
29
|
from uuid import uuid4
|
|
30
30
|
|
|
31
31
|
import temporalio
|
|
@@ -70,8 +70,6 @@ DependencyAsyncSetup = Callable[..., Coroutine[None, None, None]]
|
|
|
70
70
|
|
|
71
71
|
PROGRESS_HANDLER_ARG = "progress"
|
|
72
72
|
|
|
73
|
-
P = ParamSpec("P")
|
|
74
|
-
T = TypeVar("T")
|
|
75
73
|
|
|
76
74
|
_NEVER_RETRIABLES = {
|
|
77
75
|
"ValidationError",
|
|
@@ -178,7 +176,7 @@ async def execute_activity(
|
|
|
178
176
|
)
|
|
179
177
|
|
|
180
178
|
|
|
181
|
-
def positional_args_only(activity_fn: Callable[P, T]) -> Callable[P, T]:
|
|
179
|
+
def positional_args_only[**P, T](activity_fn: Callable[P, T]) -> Callable[P, T]:
|
|
182
180
|
sig = inspect.signature(activity_fn)
|
|
183
181
|
|
|
184
182
|
# Keep track of kwargs-only
|
|
@@ -226,7 +224,7 @@ def _unpack_positional_args(
|
|
|
226
224
|
return new_args, new_kwargs
|
|
227
225
|
|
|
228
226
|
|
|
229
|
-
def with_retriables(
|
|
227
|
+
def with_retriables[**P, T](
|
|
230
228
|
retriables: set[type[Exception]] = None,
|
|
231
229
|
) -> Callable[[Callable[P, T]], Callable[P, T]]:
|
|
232
230
|
if retriables is None:
|
|
@@ -281,7 +279,7 @@ def with_retriables(
|
|
|
281
279
|
return decorator
|
|
282
280
|
|
|
283
281
|
|
|
284
|
-
def activity_defn(
|
|
282
|
+
def activity_defn[**P, T](
|
|
285
283
|
name: str, retriables: set[type[Exception]] = None
|
|
286
284
|
) -> Callable[[Callable[P, T]], Callable[P, T]]:
|
|
287
285
|
|
|
@@ -319,8 +317,8 @@ def to_raw_async_progress(
|
|
|
319
317
|
if not max_progress > 0:
|
|
320
318
|
raise ValueError("max_progress must be > 0")
|
|
321
319
|
|
|
322
|
-
async def raw(p: int) -> None:
|
|
323
|
-
await progress(p / max_progress)
|
|
320
|
+
async def raw(p: int, *, force: bool = False) -> None:
|
|
321
|
+
await progress(p / max_progress, force=force)
|
|
324
322
|
|
|
325
323
|
return raw
|
|
326
324
|
|
|
@@ -331,10 +329,10 @@ def to_incremental_async_progress(
|
|
|
331
329
|
|
|
332
330
|
offset = 0
|
|
333
331
|
|
|
334
|
-
async def incremental(p: int) -> None:
|
|
332
|
+
async def incremental(p: int, *, force: bool = False) -> None:
|
|
335
333
|
nonlocal offset
|
|
336
334
|
offset += p
|
|
337
|
-
await progress(offset)
|
|
335
|
+
await progress(offset, force=force)
|
|
338
336
|
|
|
339
337
|
return incremental
|
|
340
338
|
|
|
@@ -345,8 +343,10 @@ def to_raw_sync_progress(
|
|
|
345
343
|
if not max_progress > 0:
|
|
346
344
|
raise ValueError("max_progress must be > 0")
|
|
347
345
|
|
|
348
|
-
def raw(
|
|
349
|
-
|
|
346
|
+
def raw(
|
|
347
|
+
iteration: int, event_loop: asyncio.AbstractEventLoop, *, force: bool = False
|
|
348
|
+
) -> None:
|
|
349
|
+
progress(iteration / max_progress, event_loop, force=force)
|
|
350
350
|
|
|
351
351
|
return raw
|
|
352
352
|
|
|
@@ -359,8 +359,8 @@ def to_scaled_async_progress(
|
|
|
359
359
|
if not start < end <= 1.0:
|
|
360
360
|
raise ValueError("end must be ]start, 1.0]")
|
|
361
361
|
|
|
362
|
-
async def _scaled(p: float) -> None:
|
|
363
|
-
await progress(start + p * (end - start))
|
|
362
|
+
async def _scaled(p: float, *, force: bool = False) -> None:
|
|
363
|
+
await progress(start + p * (end - start), force=force)
|
|
364
364
|
|
|
365
365
|
return _scaled
|
|
366
366
|
|
|
@@ -615,11 +615,11 @@ def _read_jsonl(path: Path) -> Iterable[dict]:
|
|
|
615
615
|
M = TypeVar("M", bound=BaseModel)
|
|
616
616
|
|
|
617
617
|
|
|
618
|
-
def read_jsonl_as(path: Path, cls: type[M]) -> Iterable[M]:
|
|
618
|
+
def read_jsonl_as[M](path: Path, cls: type[M]) -> Iterable[M]:
|
|
619
619
|
return (cls.model_validate(d) for d in _read_jsonl(path))
|
|
620
620
|
|
|
621
621
|
|
|
622
|
-
async def async_read_jsonl_as(
|
|
622
|
+
async def async_read_jsonl_as[M](
|
|
623
623
|
path: Path, processed_file_cls: type[M]
|
|
624
624
|
) -> AsyncIterable[M]:
|
|
625
625
|
async with async_open(path, "r") as f:
|
|
Binary file
|
|
@@ -83,6 +83,7 @@ def datashare_worker(
|
|
|
83
83
|
# at a time
|
|
84
84
|
max_concurrent_activities: int = 1,
|
|
85
85
|
max_activities_per_second: float = 20.0,
|
|
86
|
+
min_progress_interval_s: float = 30.0,
|
|
86
87
|
sandboxed: bool = True,
|
|
87
88
|
) -> DatashareWorker:
|
|
88
89
|
if workflows is None:
|
|
@@ -108,7 +109,7 @@ def datashare_worker(
|
|
|
108
109
|
logger.warning(_SEPARATE_IO_AND_CPU_WORKERS)
|
|
109
110
|
interceptors = [
|
|
110
111
|
TraceContextInterceptor(),
|
|
111
|
-
ProgressInterceptor(),
|
|
112
|
+
ProgressInterceptor(min_progress_interval_s=min_progress_interval_s),
|
|
112
113
|
HeartbeatInterceptor(),
|
|
113
114
|
]
|
|
114
115
|
wf_runner = SandboxedWorkflowRunner() if sandboxed else UnsandboxedWorkflowRunner()
|
|
@@ -216,6 +217,7 @@ async def worker_context(
|
|
|
216
217
|
activities=acts,
|
|
217
218
|
task_queue=task_queue,
|
|
218
219
|
max_concurrent_activities=worker_config.max_concurrent_activities,
|
|
220
|
+
min_progress_interval_s=worker_config.min_progress_interval_s,
|
|
219
221
|
sandboxed=sandboxed,
|
|
220
222
|
)
|
|
221
223
|
async with worker:
|
|
Binary file
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|