pluto-ml 0.0.22__tar.gz → 0.0.23__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.
- {pluto_ml-0.0.22 → pluto_ml-0.0.23}/PKG-INFO +1 -1
- {pluto_ml-0.0.22 → pluto_ml-0.0.23}/pluto/__init__.py +2 -2
- {pluto_ml-0.0.22 → pluto_ml-0.0.23}/pluto/api.py +7 -3
- {pluto_ml-0.0.22 → pluto_ml-0.0.23}/pluto/compat/wandb.py +3 -1
- {pluto_ml-0.0.22 → pluto_ml-0.0.23}/pluto/init.py +8 -2
- {pluto_ml-0.0.22 → pluto_ml-0.0.23}/pluto/op.py +9 -1
- {pluto_ml-0.0.22 → pluto_ml-0.0.23}/pluto/util.py +78 -0
- {pluto_ml-0.0.22 → pluto_ml-0.0.23}/pyproject.toml +2 -1
- {pluto_ml-0.0.22 → pluto_ml-0.0.23}/LICENSE +0 -0
- {pluto_ml-0.0.22 → pluto_ml-0.0.23}/README.md +0 -0
- {pluto_ml-0.0.22 → pluto_ml-0.0.23}/mlop/__init__.py +0 -0
- {pluto_ml-0.0.22 → pluto_ml-0.0.23}/mlop/__main__.py +0 -0
- {pluto_ml-0.0.22 → pluto_ml-0.0.23}/mlop/compat/__init__.py +0 -0
- {pluto_ml-0.0.22 → pluto_ml-0.0.23}/mlop/compat/lightning.py +0 -0
- {pluto_ml-0.0.22 → pluto_ml-0.0.23}/mlop/compat/neptune.py +0 -0
- {pluto_ml-0.0.22 → pluto_ml-0.0.23}/mlop/compat/torch.py +0 -0
- {pluto_ml-0.0.22 → pluto_ml-0.0.23}/mlop/compat/transformers.py +0 -0
- {pluto_ml-0.0.22 → pluto_ml-0.0.23}/pluto/__main__.py +0 -0
- {pluto_ml-0.0.22 → pluto_ml-0.0.23}/pluto/_wandb_hook.py +0 -0
- {pluto_ml-0.0.22 → pluto_ml-0.0.23}/pluto/auth.py +0 -0
- {pluto_ml-0.0.22 → pluto_ml-0.0.23}/pluto/compat/__init__.py +0 -0
- {pluto_ml-0.0.22 → pluto_ml-0.0.23}/pluto/compat/_utils.py +0 -0
- {pluto_ml-0.0.22 → pluto_ml-0.0.23}/pluto/compat/lightning.py +0 -0
- {pluto_ml-0.0.22 → pluto_ml-0.0.23}/pluto/compat/neptune.py +0 -0
- {pluto_ml-0.0.22 → pluto_ml-0.0.23}/pluto/compat/neptune_query/__init__.py +0 -0
- {pluto_ml-0.0.22 → pluto_ml-0.0.23}/pluto/compat/neptune_query/filters.py +0 -0
- {pluto_ml-0.0.22 → pluto_ml-0.0.23}/pluto/compat/neptune_query/runs.py +0 -0
- {pluto_ml-0.0.22 → pluto_ml-0.0.23}/pluto/compat/torch.py +0 -0
- {pluto_ml-0.0.22 → pluto_ml-0.0.23}/pluto/compat/transformers.py +0 -0
- {pluto_ml-0.0.22 → pluto_ml-0.0.23}/pluto/data.py +0 -0
- {pluto_ml-0.0.22 → pluto_ml-0.0.23}/pluto/file.py +0 -0
- {pluto_ml-0.0.22 → pluto_ml-0.0.23}/pluto/iface.py +0 -0
- {pluto_ml-0.0.22 → pluto_ml-0.0.23}/pluto/log.py +0 -0
- {pluto_ml-0.0.22 → pluto_ml-0.0.23}/pluto/query.py +0 -0
- {pluto_ml-0.0.22 → pluto_ml-0.0.23}/pluto/sanitize.py +0 -0
- {pluto_ml-0.0.22 → pluto_ml-0.0.23}/pluto/sentry.py +0 -0
- {pluto_ml-0.0.22 → pluto_ml-0.0.23}/pluto/sets.py +0 -0
- {pluto_ml-0.0.22 → pluto_ml-0.0.23}/pluto/store.py +0 -0
- {pluto_ml-0.0.22 → pluto_ml-0.0.23}/pluto/sync/__init__.py +0 -0
- {pluto_ml-0.0.22 → pluto_ml-0.0.23}/pluto/sync/__main__.py +0 -0
- {pluto_ml-0.0.22 → pluto_ml-0.0.23}/pluto/sync/process.py +0 -0
- {pluto_ml-0.0.22 → pluto_ml-0.0.23}/pluto/sync/retry.py +0 -0
- {pluto_ml-0.0.22 → pluto_ml-0.0.23}/pluto/sync/store.py +0 -0
- {pluto_ml-0.0.22 → pluto_ml-0.0.23}/pluto/sys.py +0 -0
- {pluto_ml-0.0.22 → pluto_ml-0.0.23}/zzzz_pluto_wandb_hook.pth +0 -0
|
@@ -41,11 +41,11 @@ __all__ = (
|
|
|
41
41
|
'generate_run_id',
|
|
42
42
|
)
|
|
43
43
|
|
|
44
|
-
__version__ = '0.0.
|
|
44
|
+
__version__ = '0.0.23'
|
|
45
45
|
|
|
46
46
|
|
|
47
47
|
# Replaced with the current commit when building the wheels.
|
|
48
|
-
_PLUTO_COMMIT_SHA = '
|
|
48
|
+
_PLUTO_COMMIT_SHA = 'b279ceb6e63cc782fe7f3d6a3513cc97924deefe'
|
|
49
49
|
|
|
50
50
|
|
|
51
51
|
def _get_git_commit():
|
|
@@ -4,7 +4,7 @@ import re
|
|
|
4
4
|
import signal
|
|
5
5
|
from datetime import datetime
|
|
6
6
|
|
|
7
|
-
from .util import clean_dict, find_node
|
|
7
|
+
from .util import clean_dict, config_json_default, find_node
|
|
8
8
|
|
|
9
9
|
logger = logging.getLogger(f'{__name__.split(".")[0]}')
|
|
10
10
|
tag = 'API'
|
|
@@ -40,7 +40,9 @@ def make_compat_start_v1(config, settings, info, tags=None):
|
|
|
40
40
|
'runName': settings._op_name,
|
|
41
41
|
'projectName': settings.project,
|
|
42
42
|
'externalId': settings._external_id, # User-provided run ID for multi-node
|
|
43
|
-
'config': json.dumps(config)
|
|
43
|
+
'config': json.dumps(config, default=config_json_default)
|
|
44
|
+
if config is not None
|
|
45
|
+
else None,
|
|
44
46
|
'loggerSettings': json.dumps(clean_dict(settings.to_dict())),
|
|
45
47
|
'systemMetadata': json.dumps(info) if info is not None else None,
|
|
46
48
|
'tags': tags if tags else None,
|
|
@@ -97,7 +99,9 @@ def make_compat_update_config_v1(settings, config):
|
|
|
97
99
|
return json.dumps(
|
|
98
100
|
{
|
|
99
101
|
'runId': settings._op_id,
|
|
100
|
-
'config': json.dumps(config)
|
|
102
|
+
'config': json.dumps(config, default=config_json_default)
|
|
103
|
+
if config
|
|
104
|
+
else None,
|
|
101
105
|
}
|
|
102
106
|
).encode()
|
|
103
107
|
|
|
@@ -851,7 +851,9 @@ def _make_patched_init(original_init, wandb_module):
|
|
|
851
851
|
f'wandb will continue to work normally, but NO DATA will be '
|
|
852
852
|
f'sent to Pluto. To fix, resolve the error above and retry.'
|
|
853
853
|
)
|
|
854
|
-
|
|
854
|
+
# exc_info=True attaches the traceback so the log points at the
|
|
855
|
+
# raise site (e.g. the failing json.dumps), not just this handler.
|
|
856
|
+
logger.error(_msg, exc_info=True)
|
|
855
857
|
# Also print to stderr so it shows up even if logging is not configured
|
|
856
858
|
import sys
|
|
857
859
|
|
|
@@ -7,7 +7,7 @@ import pluto
|
|
|
7
7
|
from . import sentry as _sentry
|
|
8
8
|
from .op import Op
|
|
9
9
|
from .sets import Settings, _classify_run_id, _is_display_id, setup
|
|
10
|
-
from .util import deep_merge, gen_id, get_char
|
|
10
|
+
from .util import deep_merge, gen_id, get_char, to_native_config
|
|
11
11
|
|
|
12
12
|
logger = logging.getLogger(f'{__name__.split(".")[0]}')
|
|
13
13
|
tag = 'Init'
|
|
@@ -194,6 +194,12 @@ def init(
|
|
|
194
194
|
if inherit_tags is not None:
|
|
195
195
|
settings._inherit_tags = inherit_tags
|
|
196
196
|
|
|
197
|
+
# Normalize the config to JSON-native types up front (e.g. OmegaConf
|
|
198
|
+
# DictConfig -> dict, resolving interpolations). Done before the fork
|
|
199
|
+
# deep-merge below so its `isinstance(config, dict)` check works, and so
|
|
200
|
+
# everything downstream (storage, serialization) sees clean native data.
|
|
201
|
+
config = to_native_config(config)
|
|
202
|
+
|
|
197
203
|
# Deep-merge inherited parent config with user config (client-side).
|
|
198
204
|
# The server only does a shallow merge, so we fetch the parent config,
|
|
199
205
|
# deep-merge locally, and disable server-side inheritance.
|
|
@@ -240,7 +246,7 @@ def init(
|
|
|
240
246
|
return op
|
|
241
247
|
except Exception as e:
|
|
242
248
|
_sentry.capture_exception(e)
|
|
243
|
-
logger.critical('%s: failed, %s', tag, e) # add early logger
|
|
249
|
+
logger.critical('%s: failed, %s', tag, e, exc_info=True) # add early logger
|
|
244
250
|
raise e
|
|
245
251
|
|
|
246
252
|
|
|
@@ -32,7 +32,14 @@ from .store import DataStore
|
|
|
32
32
|
from .sync import SyncProcessManager
|
|
33
33
|
from .sync.store import HEALTH_METRIC_KEYS
|
|
34
34
|
from .sys import System
|
|
35
|
-
from .util import
|
|
35
|
+
from .util import (
|
|
36
|
+
deep_merge,
|
|
37
|
+
get_char,
|
|
38
|
+
get_val,
|
|
39
|
+
print_url,
|
|
40
|
+
to_json,
|
|
41
|
+
to_native_config,
|
|
42
|
+
)
|
|
36
43
|
|
|
37
44
|
logger = logging.getLogger(f'{__name__.split(".")[0]}')
|
|
38
45
|
tag = 'Operation'
|
|
@@ -888,6 +895,7 @@ class Op:
|
|
|
888
895
|
run.update_config({'epochs': 100})
|
|
889
896
|
run.update_config({'lr': 0.01, 'model': 'resnet50'})
|
|
890
897
|
"""
|
|
898
|
+
config = to_native_config(config)
|
|
891
899
|
if self.config is None:
|
|
892
900
|
self.config = {}
|
|
893
901
|
self.config = deep_merge(self.config, config)
|
|
@@ -17,6 +17,11 @@ import numpy as np
|
|
|
17
17
|
|
|
18
18
|
from .sets import get_console
|
|
19
19
|
|
|
20
|
+
try:
|
|
21
|
+
from omegaconf import OmegaConf
|
|
22
|
+
except ImportError: # optional dependency; degrade gracefully when absent
|
|
23
|
+
OmegaConf = None # type: ignore[misc, assignment]
|
|
24
|
+
|
|
20
25
|
logger = logging.getLogger(f'{__name__.split(".")[0]}')
|
|
21
26
|
tag = 'Util'
|
|
22
27
|
|
|
@@ -419,6 +424,79 @@ def clean_dict(d):
|
|
|
419
424
|
return c
|
|
420
425
|
|
|
421
426
|
|
|
427
|
+
def _is_omegaconf(obj: Any) -> bool:
|
|
428
|
+
"""True if ``obj`` is an OmegaConf node (DictConfig/ListConfig).
|
|
429
|
+
|
|
430
|
+
Returns False when omegaconf isn't installed. Any failure (broken install)
|
|
431
|
+
is also treated as "not omegaconf" so a degraded dependency can never crash
|
|
432
|
+
a caller that doesn't even use OmegaConf.
|
|
433
|
+
"""
|
|
434
|
+
if OmegaConf is None:
|
|
435
|
+
return False
|
|
436
|
+
try:
|
|
437
|
+
return OmegaConf.is_config(obj)
|
|
438
|
+
except Exception:
|
|
439
|
+
return False
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
def _omegaconf_to_container(obj: Any) -> Any:
|
|
443
|
+
"""Convert an OmegaConf node to a native container, degrading on failure.
|
|
444
|
+
|
|
445
|
+
Prefer fully-resolved values, but ``resolve=True`` raises on unresolvable
|
|
446
|
+
or cyclic interpolations (e.g. an unset ``${oc.env:VAR}``). Rather than let
|
|
447
|
+
that crash init or disable logging, fall back to the unresolved structure
|
|
448
|
+
(interpolations kept as literal ``${...}`` strings), and finally to a plain
|
|
449
|
+
string if even that fails. Callers must already have checked
|
|
450
|
+
:func:`_is_omegaconf`.
|
|
451
|
+
"""
|
|
452
|
+
if OmegaConf is None: # pragma: no cover - guarded by _is_omegaconf
|
|
453
|
+
return str(obj)
|
|
454
|
+
try:
|
|
455
|
+
return OmegaConf.to_container(obj, resolve=True)
|
|
456
|
+
except Exception:
|
|
457
|
+
try:
|
|
458
|
+
return OmegaConf.to_container(obj, resolve=False)
|
|
459
|
+
except Exception:
|
|
460
|
+
return str(obj)
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
def to_native_config(obj: Any) -> Any:
|
|
464
|
+
"""Recursively convert a run config into JSON-native Python types.
|
|
465
|
+
|
|
466
|
+
Handles OmegaConf ``DictConfig`` / ``ListConfig`` at any nesting depth,
|
|
467
|
+
including the common ``dict(cfg)`` shape where the top-level is a plain
|
|
468
|
+
``dict`` but nested values are still OmegaConf nodes. OmegaConf nodes are
|
|
469
|
+
deep-converted, resolving interpolations (``${...}``) where possible and
|
|
470
|
+
degrading gracefully when they can't be resolved (see
|
|
471
|
+
:func:`_omegaconf_to_container`). Plain data (dicts, lists, scalars,
|
|
472
|
+
``None``) passes through unchanged. Non-string dict keys are coerced to
|
|
473
|
+
``str`` (note: distinct keys with the same string form collapse, matching
|
|
474
|
+
JSON's own key semantics).
|
|
475
|
+
"""
|
|
476
|
+
if _is_omegaconf(obj):
|
|
477
|
+
# _omegaconf_to_container already does a deep conversion to native
|
|
478
|
+
# types, so return its result rather than re-walking the whole tree.
|
|
479
|
+
return _omegaconf_to_container(obj)
|
|
480
|
+
if isinstance(obj, dict):
|
|
481
|
+
return {str(k): to_native_config(v) for k, v in obj.items()}
|
|
482
|
+
if isinstance(obj, (list, tuple)):
|
|
483
|
+
return [to_native_config(v) for v in obj]
|
|
484
|
+
return obj
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
def config_json_default(o: Any) -> Any:
|
|
488
|
+
"""``json.dumps(default=...)`` hook for config payloads.
|
|
489
|
+
|
|
490
|
+
Last line of defense so a single non-serializable config value can never
|
|
491
|
+
raise and silently disable logging. OmegaConf nodes are deep-converted
|
|
492
|
+
(resolving interpolations where possible, see
|
|
493
|
+
:func:`_omegaconf_to_container`); anything else degrades to its string form.
|
|
494
|
+
"""
|
|
495
|
+
if _is_omegaconf(o):
|
|
496
|
+
return _omegaconf_to_container(o)
|
|
497
|
+
return str(o)
|
|
498
|
+
|
|
499
|
+
|
|
422
500
|
def dict_to_json(data: Dict[str, Any]) -> Dict[str, Any]:
|
|
423
501
|
for key in list(data): # avoid RuntimeError if dict size changes
|
|
424
502
|
val = data[key]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "pluto-ml"
|
|
3
|
-
version = "0.0.
|
|
3
|
+
version = "0.0.23"
|
|
4
4
|
description = "Pluto ML - Machine Learning Operations Framework"
|
|
5
5
|
packages = [
|
|
6
6
|
{include = "pluto"},
|
|
@@ -48,6 +48,7 @@ matplotlib = "^3.10"
|
|
|
48
48
|
torchvision = "*"
|
|
49
49
|
imageio = "^2.37.0"
|
|
50
50
|
moviepy = "^1.0.3"
|
|
51
|
+
omegaconf = "^2.3"
|
|
51
52
|
|
|
52
53
|
|
|
53
54
|
[tool.poetry.extras]
|
|
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
|
|
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
|