ml-dash 0.6.12__py3-none-any.whl → 0.6.13__py3-none-any.whl
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.
- ml_dash/__init__.py +15 -13
- ml_dash/cli_commands/upload.py +1 -1
- ml_dash/run.py +65 -0
- {ml_dash-0.6.12.dist-info → ml_dash-0.6.13.dist-info}/METADATA +1 -1
- {ml_dash-0.6.12.dist-info → ml_dash-0.6.13.dist-info}/RECORD +7 -7
- {ml_dash-0.6.12.dist-info → ml_dash-0.6.13.dist-info}/WHEEL +0 -0
- {ml_dash-0.6.12.dist-info → ml_dash-0.6.13.dist-info}/entry_points.txt +0 -0
ml_dash/__init__.py
CHANGED
|
@@ -43,18 +43,19 @@ from .params import ParametersBuilder
|
|
|
43
43
|
from .run import RUN
|
|
44
44
|
from .storage import LocalStorage
|
|
45
45
|
|
|
46
|
-
__version__ = "0.6.
|
|
46
|
+
__version__ = "0.6.13"
|
|
47
47
|
|
|
48
|
-
#
|
|
49
|
-
|
|
48
|
+
# Required version - MUST match exactly (blocks all older versions)
|
|
49
|
+
# Update this with EVERY release to force users to upgrade
|
|
50
|
+
REQUIRED_VERSION = "0.6.13"
|
|
50
51
|
|
|
51
52
|
|
|
52
53
|
def _check_version_compatibility():
|
|
53
54
|
"""
|
|
54
|
-
Enforce
|
|
55
|
+
Enforce strict version requirement.
|
|
55
56
|
|
|
56
|
-
Raises ImportError if installed version
|
|
57
|
-
This ensures users
|
|
57
|
+
Raises ImportError if installed version doesn't match the required version.
|
|
58
|
+
This ensures all users are on the latest version with newest features and bug fixes.
|
|
58
59
|
"""
|
|
59
60
|
try:
|
|
60
61
|
from packaging import version
|
|
@@ -64,25 +65,26 @@ def _check_version_compatibility():
|
|
|
64
65
|
return
|
|
65
66
|
|
|
66
67
|
current = version.parse(__version__)
|
|
67
|
-
|
|
68
|
+
required = version.parse(REQUIRED_VERSION)
|
|
68
69
|
|
|
69
|
-
if current <
|
|
70
|
+
if current < required:
|
|
70
71
|
raise ImportError(
|
|
71
72
|
f"\n"
|
|
72
73
|
f"{'=' * 80}\n"
|
|
73
|
-
f"ERROR: ml-dash version {__version__} is
|
|
74
|
+
f"ERROR: ml-dash version {__version__} is outdated!\n"
|
|
74
75
|
f"{'=' * 80}\n"
|
|
75
76
|
f"\n"
|
|
76
|
-
f"
|
|
77
|
-
f"
|
|
77
|
+
f"Your installed version ({__version__}) is no longer supported.\n"
|
|
78
|
+
f"Required version: {REQUIRED_VERSION}\n"
|
|
78
79
|
f"\n"
|
|
79
80
|
f"Please upgrade to the latest version:\n"
|
|
80
81
|
f"\n"
|
|
81
82
|
f" pip install --upgrade ml-dash\n"
|
|
82
83
|
f"\n"
|
|
83
|
-
f"Or
|
|
84
|
+
f"Or with uv:\n"
|
|
84
85
|
f"\n"
|
|
85
|
-
f" pip install ml-dash
|
|
86
|
+
f" uv pip install --upgrade ml-dash\n"
|
|
87
|
+
f" uv sync --upgrade-package ml-dash\n"
|
|
86
88
|
f"\n"
|
|
87
89
|
f"{'=' * 80}\n"
|
|
88
90
|
)
|
ml_dash/cli_commands/upload.py
CHANGED
|
@@ -1217,7 +1217,7 @@ def cmd_upload(args: argparse.Namespace) -> int:
|
|
|
1217
1217
|
Exit code (0 for success, 1 for error)
|
|
1218
1218
|
"""
|
|
1219
1219
|
# Handle track upload if --tracks is specified
|
|
1220
|
-
if args
|
|
1220
|
+
if getattr(args, 'tracks', False):
|
|
1221
1221
|
return cmd_upload_track(args)
|
|
1222
1222
|
|
|
1223
1223
|
# Load config
|
ml_dash/run.py
CHANGED
|
@@ -158,6 +158,16 @@ class RUN:
|
|
|
158
158
|
now = datetime.now()
|
|
159
159
|
"""Timestamp at import time. Does not change during the session."""
|
|
160
160
|
|
|
161
|
+
@property
|
|
162
|
+
def date(self) -> str:
|
|
163
|
+
"""Date string in YYYYMMDD format."""
|
|
164
|
+
return self.now.strftime("%Y%m%d")
|
|
165
|
+
|
|
166
|
+
@property
|
|
167
|
+
def datetime_str(self) -> str:
|
|
168
|
+
"""DateTime string in YYYYMMDD.HHMMSS format."""
|
|
169
|
+
return self.now.strftime("%Y%m%d.%H%M%S")
|
|
170
|
+
|
|
161
171
|
timestamp: str = None
|
|
162
172
|
"""Timestamp created at instantiation"""
|
|
163
173
|
|
|
@@ -277,6 +287,61 @@ class RUN:
|
|
|
277
287
|
# self.name is the last segment
|
|
278
288
|
self.name = parts[-1] if len(parts) > 2 else parts[1]
|
|
279
289
|
|
|
290
|
+
def __setattr__(self, name: str, value):
|
|
291
|
+
"""
|
|
292
|
+
Intercept attribute setting to expand {EXP.attr} templates in prefix.
|
|
293
|
+
|
|
294
|
+
When prefix is set, expands any {EXP.name}, {EXP.id}, {EXP.date}, etc. templates
|
|
295
|
+
using current instance's attributes. Also syncs back to class-level RUN attributes.
|
|
296
|
+
"""
|
|
297
|
+
# Prevent prefix changes after experiment has started
|
|
298
|
+
if name == "prefix" and isinstance(value, str):
|
|
299
|
+
experiment = getattr(self, "_experiment", None)
|
|
300
|
+
if experiment is not None and getattr(experiment, "_is_open", False):
|
|
301
|
+
raise RuntimeError(
|
|
302
|
+
"Cannot change prefix after experiment has been initialized. "
|
|
303
|
+
"Set prefix before calling experiment.run.start() or entering the context manager."
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
# Expand templates if setting prefix
|
|
307
|
+
if name == "prefix" and isinstance(value, str):
|
|
308
|
+
# Check if value contains {EXP. templates
|
|
309
|
+
if "{EXP." in value:
|
|
310
|
+
import re
|
|
311
|
+
|
|
312
|
+
def replace_match(match):
|
|
313
|
+
attr_name = match.group(1)
|
|
314
|
+
# Special handling for id - generate if needed
|
|
315
|
+
if attr_name == "id" and not getattr(self, "id", None):
|
|
316
|
+
from ml_dash.snowflake import generate_id
|
|
317
|
+
object.__setattr__(self, "id", generate_id())
|
|
318
|
+
|
|
319
|
+
# Get attribute, raising error if not found
|
|
320
|
+
try:
|
|
321
|
+
attr_value = getattr(self, attr_name)
|
|
322
|
+
if attr_value is None:
|
|
323
|
+
raise AttributeError(f"Attribute '{attr_name}' is None")
|
|
324
|
+
return str(attr_value)
|
|
325
|
+
except AttributeError:
|
|
326
|
+
raise AttributeError(f"RUN has no attribute '{attr_name}'")
|
|
327
|
+
|
|
328
|
+
# Match {EXP.attr_name} pattern
|
|
329
|
+
pattern = r"\{EXP\.(\w+)\}"
|
|
330
|
+
value = re.sub(pattern, replace_match, value)
|
|
331
|
+
|
|
332
|
+
# Always update _folder_path when prefix changes
|
|
333
|
+
object.__setattr__(self, "_folder_path", value)
|
|
334
|
+
|
|
335
|
+
# Parse and update owner, project, name from new prefix
|
|
336
|
+
parts = value.strip("/").split("/")
|
|
337
|
+
if len(parts) >= 2:
|
|
338
|
+
object.__setattr__(self, "owner", parts[0])
|
|
339
|
+
object.__setattr__(self, "project", parts[1])
|
|
340
|
+
object.__setattr__(self, "name", parts[-1] if len(parts) > 2 else parts[1])
|
|
341
|
+
|
|
342
|
+
# Use object.__setattr__ to set the value
|
|
343
|
+
object.__setattr__(self, name, value)
|
|
344
|
+
|
|
280
345
|
def start(self) -> "Experiment":
|
|
281
346
|
"""
|
|
282
347
|
Start the experiment (sets status to RUNNING).
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
ml_dash/__init__.py,sha256=
|
|
1
|
+
ml_dash/__init__.py,sha256=S-Q1EK0mKvV0WAOB-QjQF7NjLvYntB3_LIAbzKA45LA,3060
|
|
2
2
|
ml_dash/auth/__init__.py,sha256=3lwM-Y8UBHPU1gFW2JNpmXlPVTnkGudWLKNFFKulQfo,1200
|
|
3
3
|
ml_dash/auth/constants.py,sha256=ku4QzQUMNjvyJwjy7AUdywMAZd59jXSxNHZxDiagUWU,280
|
|
4
4
|
ml_dash/auth/device_flow.py,sha256=DQOdPNlZCuU1umZOA_A6WXdRM3zWphnyo9IntToBl_A,7921
|
|
@@ -17,7 +17,7 @@ ml_dash/cli_commands/login.py,sha256=zX-urtUrfzg2qOGtKNYQgj6UloN9kzj4zEO6h_xwuNs
|
|
|
17
17
|
ml_dash/cli_commands/logout.py,sha256=lTUUNyRXqvo61qNkCd4KBrPUujDAHnNqsHkU6bHie0U,1332
|
|
18
18
|
ml_dash/cli_commands/profile.py,sha256=PoRO1XA4bnOINptj4AO0SyNDBADeryPJBfgC74327e4,5997
|
|
19
19
|
ml_dash/cli_commands/remove.py,sha256=AtDlUWkNeGcnZWN0Wbg6XoyYhFHkCFMPdxsGA33v38c,5325
|
|
20
|
-
ml_dash/cli_commands/upload.py,sha256=
|
|
20
|
+
ml_dash/cli_commands/upload.py,sha256=o5D1be3V_wrzoVI3QHrKzyw2fG6ocD33MSZjzRsS60w,49659
|
|
21
21
|
ml_dash/client.py,sha256=sQokhvofq56JxpmUBVUsfjjn5qKqyUXM9F3uxsUbUbA,66608
|
|
22
22
|
ml_dash/config.py,sha256=oz2xvoBh2X_xUXWr92cPD5nFxXMT5LxVNypv5B5O0fA,3116
|
|
23
23
|
ml_dash/experiment.py,sha256=JqNYsoAOsvqIRMVl6gHFodlZ79b0X2rUFGz6NRixKic,43726
|
|
@@ -27,11 +27,11 @@ ml_dash/metric.py,sha256=ghD1jnuv6dbjV1Jlo7q0mx9UEzpdto2Y1-oDWrSfg04,25809
|
|
|
27
27
|
ml_dash/params.py,sha256=pPFvknJAJX5uhckzjO1r-HNnKbQFFKDISFmOXNET5eY,9046
|
|
28
28
|
ml_dash/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
29
29
|
ml_dash/remote_auto_start.py,sha256=5fvQDHv1CWEKFb6WAa5_uyEInwV_SvotXjOO_6i6ZKE,1687
|
|
30
|
-
ml_dash/run.py,sha256=
|
|
30
|
+
ml_dash/run.py,sha256=yAKZ9HtU4cidtbWMAY1IiDPVWwluVlicD5hsmVT89U0,11361
|
|
31
31
|
ml_dash/snowflake.py,sha256=14rEpRU5YltsmmmZW0EMUy_hdv5S5ME9gWVtmdmwfiU,4917
|
|
32
32
|
ml_dash/storage.py,sha256=x1W-dK6wQY36-LVOJ4kA8Dn07ObNQuIErQWJ3b0PoGY,44910
|
|
33
33
|
ml_dash/track.py,sha256=Dfg1ZnmKZ_FlE5ZfG8Qld_wN4RIMs3nrOxrxwf3thiY,8164
|
|
34
|
-
ml_dash-0.6.
|
|
35
|
-
ml_dash-0.6.
|
|
36
|
-
ml_dash-0.6.
|
|
37
|
-
ml_dash-0.6.
|
|
34
|
+
ml_dash-0.6.13.dist-info/WHEEL,sha256=z-mOpxbJHqy3cq6SvUThBZdaLGFZzdZPtgWLcP2NKjQ,79
|
|
35
|
+
ml_dash-0.6.13.dist-info/entry_points.txt,sha256=dYs2EHX1uRNO7AQGNnVaJJpgiy0Z9q7tiy4fHSyaf3Q,46
|
|
36
|
+
ml_dash-0.6.13.dist-info/METADATA,sha256=bKRP7yW8ULvNc5kqdeO0USgqCOWhXuV3EuhViNKOZIU,9536
|
|
37
|
+
ml_dash-0.6.13.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|