datashare-python 0.10.6__tar.gz → 0.10.8__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.6 → datashare_python-0.10.8}/PKG-INFO +1 -1
- {datashare_python-0.10.6 → datashare_python-0.10.8}/datashare_python/objects.py +8 -0
- {datashare_python-0.10.6 → datashare_python-0.10.8}/datashare_python/utils.py +50 -12
- datashare_python-0.10.8/datashare_python/worker-template.tar.gz +0 -0
- {datashare_python-0.10.6 → datashare_python-0.10.8}/pyproject.toml +1 -1
- datashare_python-0.10.6/datashare_python/worker-template.tar.gz +0 -0
- {datashare_python-0.10.6 → datashare_python-0.10.8}/.gitignore +0 -0
- {datashare_python-0.10.6 → datashare_python-0.10.8}/README.md +0 -0
- {datashare_python-0.10.6 → datashare_python-0.10.8}/datashare_python/.gitignore +0 -0
- {datashare_python-0.10.6 → datashare_python-0.10.8}/datashare_python/__init__.py +0 -0
- {datashare_python-0.10.6 → datashare_python-0.10.8}/datashare_python/__main__.py +0 -0
- {datashare_python-0.10.6 → datashare_python-0.10.8}/datashare_python/cli/__init__.py +0 -0
- {datashare_python-0.10.6 → datashare_python-0.10.8}/datashare_python/cli/project.py +0 -0
- {datashare_python-0.10.6 → datashare_python-0.10.8}/datashare_python/cli/task.py +0 -0
- {datashare_python-0.10.6 → datashare_python-0.10.8}/datashare_python/cli/utils.py +0 -0
- {datashare_python-0.10.6 → datashare_python-0.10.8}/datashare_python/cli/worker.py +0 -0
- {datashare_python-0.10.6 → datashare_python-0.10.8}/datashare_python/config.py +0 -0
- {datashare_python-0.10.6 → datashare_python-0.10.8}/datashare_python/conftest.py +0 -0
- {datashare_python-0.10.6 → datashare_python-0.10.8}/datashare_python/constants.py +0 -0
- {datashare_python-0.10.6 → datashare_python-0.10.8}/datashare_python/dependencies.py +0 -0
- {datashare_python-0.10.6 → datashare_python-0.10.8}/datashare_python/discovery.py +0 -0
- {datashare_python-0.10.6 → datashare_python-0.10.8}/datashare_python/exceptions.py +0 -0
- {datashare_python-0.10.6 → datashare_python-0.10.8}/datashare_python/interceptors.py +0 -0
- {datashare_python-0.10.6 → datashare_python-0.10.8}/datashare_python/logging_.py +0 -0
- {datashare_python-0.10.6 → datashare_python-0.10.8}/datashare_python/mimetypes_.py +0 -0
- {datashare_python-0.10.6 → datashare_python-0.10.8}/datashare_python/task_client.py +0 -0
- {datashare_python-0.10.6 → datashare_python-0.10.8}/datashare_python/template.py +0 -0
- {datashare_python-0.10.6 → datashare_python-0.10.8}/datashare_python/types_.py +0 -0
- {datashare_python-0.10.6 → datashare_python-0.10.8}/datashare_python/worker.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: datashare-python
|
|
3
|
-
Version: 0.10.
|
|
3
|
+
Version: 0.10.8
|
|
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,3 +1,5 @@
|
|
|
1
|
+
import hashlib
|
|
2
|
+
import json
|
|
1
3
|
import logging
|
|
2
4
|
import os
|
|
3
5
|
from abc import ABC
|
|
@@ -63,6 +65,12 @@ Predicate = Callable[[T], bool] | Callable[[T], Awaitable[bool]]
|
|
|
63
65
|
class BaseModel(_BaseModel):
|
|
64
66
|
model_config = merge_configs(icij_config(), no_enum_values_config())
|
|
65
67
|
|
|
68
|
+
def __hash__(self) -> int:
|
|
69
|
+
digest = hashlib.md5(
|
|
70
|
+
json.dumps(self.model_dump(mode="json"), sort_keys=True).encode()
|
|
71
|
+
).digest()
|
|
72
|
+
return int.from_bytes(digest[:8])
|
|
73
|
+
|
|
66
74
|
|
|
67
75
|
class DatashareModel(BaseModel):
|
|
68
76
|
model_config = merge_configs(BaseModel.model_config, lowercamel_case_config())
|
|
@@ -32,6 +32,7 @@ import temporalio
|
|
|
32
32
|
from aiofile import async_open
|
|
33
33
|
from lru import LRU
|
|
34
34
|
from pydantic import ValidationError
|
|
35
|
+
from pydantic.alias_generators import to_snake
|
|
35
36
|
from temporalio import activity, workflow
|
|
36
37
|
from temporalio.api.common.v1 import Payload
|
|
37
38
|
from temporalio.client import Client
|
|
@@ -62,6 +63,8 @@ from .objects import (
|
|
|
62
63
|
)
|
|
63
64
|
from .types_ import RawAsyncProgressHandler
|
|
64
65
|
|
|
66
|
+
_CACHE_DIR = "__cache__"
|
|
67
|
+
|
|
65
68
|
logger = logging.getLogger(__name__)
|
|
66
69
|
|
|
67
70
|
DependencyLabel = str | None
|
|
@@ -225,7 +228,7 @@ def _unpack_positional_args(
|
|
|
225
228
|
|
|
226
229
|
|
|
227
230
|
def with_retriables[**P, T](
|
|
228
|
-
retriables: set[type[Exception]] = None
|
|
231
|
+
logger: logging.Logger, retriables: set[type[Exception]] = None
|
|
229
232
|
) -> Callable[[Callable[P, T]], Callable[P, T]]:
|
|
230
233
|
if retriables is None:
|
|
231
234
|
|
|
@@ -237,6 +240,7 @@ def with_retriables[**P, T](
|
|
|
237
240
|
try:
|
|
238
241
|
return await activity_fn(*args, **kwargs)
|
|
239
242
|
except Exception as e:
|
|
243
|
+
logger.exception("failing activity due to non retriable error")
|
|
240
244
|
raise fatal_error_from_exception(e) from e
|
|
241
245
|
else:
|
|
242
246
|
|
|
@@ -245,6 +249,7 @@ def with_retriables[**P, T](
|
|
|
245
249
|
try:
|
|
246
250
|
return activity_fn(*args, **kwargs)
|
|
247
251
|
except Exception as e:
|
|
252
|
+
logger.exception("failing activity due to non retriable error")
|
|
248
253
|
raise fatal_error_from_exception(e) from e
|
|
249
254
|
|
|
250
255
|
return wrapper
|
|
@@ -259,8 +264,12 @@ def with_retriables[**P, T](
|
|
|
259
264
|
try:
|
|
260
265
|
return await activity_fn(*args, **kwargs)
|
|
261
266
|
except retriables:
|
|
267
|
+
logger.exception(
|
|
268
|
+
"retriable error occurred during activity, will retry !"
|
|
269
|
+
)
|
|
262
270
|
raise
|
|
263
271
|
except Exception as e:
|
|
272
|
+
logger.exception("failing activity due to non retriable error")
|
|
264
273
|
raise fatal_error_from_exception(e) from e
|
|
265
274
|
|
|
266
275
|
else:
|
|
@@ -270,8 +279,12 @@ def with_retriables[**P, T](
|
|
|
270
279
|
try:
|
|
271
280
|
return activity_fn(*args, **kwargs)
|
|
272
281
|
except retriables:
|
|
282
|
+
logger.exception(
|
|
283
|
+
"retriable error occurred during activity, will retry !"
|
|
284
|
+
)
|
|
273
285
|
raise
|
|
274
286
|
except Exception as e:
|
|
287
|
+
logger.exception("failing activity due to non retriable error")
|
|
275
288
|
raise fatal_error_from_exception(e) from e
|
|
276
289
|
|
|
277
290
|
return wrapper
|
|
@@ -280,12 +293,19 @@ def with_retriables[**P, T](
|
|
|
280
293
|
|
|
281
294
|
|
|
282
295
|
def activity_defn[**P, T](
|
|
283
|
-
name: str,
|
|
296
|
+
name: str,
|
|
297
|
+
retriables: set[type[Exception]] = None,
|
|
298
|
+
logger: logging.Logger | None = None,
|
|
284
299
|
) -> Callable[[Callable[P, T]], Callable[P, T]]:
|
|
285
300
|
|
|
301
|
+
if logger is None:
|
|
302
|
+
frame = inspect.stack()[1]
|
|
303
|
+
caller_module = inspect.getmodule(frame[0])
|
|
304
|
+
logger = logging.getLogger(caller_module.__name__)
|
|
305
|
+
|
|
286
306
|
def decorator(activity_fn: Callable[P, T]) -> Callable[P, T]:
|
|
287
307
|
activity_fn = positional_args_only(activity_fn)
|
|
288
|
-
activity_fn = with_retriables(retriables)(activity_fn)
|
|
308
|
+
activity_fn = with_retriables(logger, retriables)(activity_fn)
|
|
289
309
|
activity_fn = activity.defn(activity_fn, name=name)
|
|
290
310
|
|
|
291
311
|
is_async = inspect.iscoroutinefunction(activity_fn)
|
|
@@ -533,7 +553,11 @@ activity_contextual_id = contextual_id
|
|
|
533
553
|
|
|
534
554
|
|
|
535
555
|
def _contextual_path(
|
|
536
|
-
*,
|
|
556
|
+
*,
|
|
557
|
+
wf_context: bool = True,
|
|
558
|
+
act_context: bool = True,
|
|
559
|
+
run_context: bool = False,
|
|
560
|
+
caching_key: str | None = None,
|
|
537
561
|
) -> Path:
|
|
538
562
|
act_info = activity.info()
|
|
539
563
|
path = []
|
|
@@ -541,14 +565,18 @@ def _contextual_path(
|
|
|
541
565
|
raise ValueError("at least one of wf_context and act_context must be True")
|
|
542
566
|
if wf_context:
|
|
543
567
|
path = [act_info.workflow_type]
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
568
|
+
if not caching_key:
|
|
569
|
+
path.append(act_info.workflow_id)
|
|
570
|
+
if run_context:
|
|
571
|
+
path.append(act_info.workflow_run_id)
|
|
547
572
|
if act_context:
|
|
548
573
|
path.append(act_info.activity_type)
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
574
|
+
if not caching_key:
|
|
575
|
+
path.append(act_info.activity_id)
|
|
576
|
+
if run_context:
|
|
577
|
+
path.append(act_info.activity_run_id)
|
|
578
|
+
if caching_key:
|
|
579
|
+
path += [_CACHE_DIR, caching_key]
|
|
552
580
|
return Path(*path)
|
|
553
581
|
|
|
554
582
|
|
|
@@ -559,9 +587,13 @@ def activity_workdir(
|
|
|
559
587
|
wf_context: bool = True,
|
|
560
588
|
act_context: bool = True,
|
|
561
589
|
run_context: bool = False,
|
|
590
|
+
caching_key: str | None = None,
|
|
562
591
|
) -> Path:
|
|
563
592
|
ctx_path = _contextual_path(
|
|
564
|
-
wf_context=wf_context,
|
|
593
|
+
wf_context=wf_context,
|
|
594
|
+
act_context=act_context,
|
|
595
|
+
run_context=run_context,
|
|
596
|
+
caching_key=caching_key,
|
|
565
597
|
)
|
|
566
598
|
return workdir.joinpath(project, ctx_path)
|
|
567
599
|
|
|
@@ -808,4 +840,10 @@ def async_enter_cm(
|
|
|
808
840
|
|
|
809
841
|
|
|
810
842
|
def config_cache_key(config: BaseModel) -> str:
|
|
811
|
-
|
|
843
|
+
# BaseModel support deterministic hash we can call hash on it
|
|
844
|
+
config_label = (
|
|
845
|
+
to_snake(config.__class__.__name__.replace("Config", ""))
|
|
846
|
+
.lower()
|
|
847
|
+
.replace("_", "-")
|
|
848
|
+
)
|
|
849
|
+
return f"{config_label}-{str(hash(config))}"
|
|
Binary file
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|