liblaf-cherries 0.0.14__tar.gz → 0.1.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.
Files changed (54) hide show
  1. {liblaf_cherries-0.0.14 → liblaf_cherries-0.1.1}/.gitignore +13 -8
  2. {liblaf_cherries-0.0.14 → liblaf_cherries-0.1.1}/PKG-INFO +5 -5
  3. {liblaf_cherries-0.0.14 → liblaf_cherries-0.1.1}/pyproject.toml +7 -21
  4. liblaf_cherries-0.1.1/src/liblaf/cherries/__init__.pyi +116 -0
  5. liblaf_cherries-0.1.1/src/liblaf/cherries/_run.py +51 -0
  6. {liblaf_cherries-0.0.14 → liblaf_cherries-0.1.1}/src/liblaf/cherries/_version.py +2 -2
  7. liblaf_cherries-0.1.1/src/liblaf/cherries/config/__init__.pyi +23 -0
  8. liblaf_cherries-0.1.1/src/liblaf/cherries/config/_asset.py +72 -0
  9. liblaf_cherries-0.1.1/src/liblaf/cherries/config/_config.py +6 -0
  10. liblaf_cherries-0.1.1/src/liblaf/cherries/integration/__init__.pyi +66 -0
  11. liblaf_cherries-0.1.1/src/liblaf/cherries/integration/_abc.py +144 -0
  12. liblaf_cherries-0.1.1/src/liblaf/cherries/integration/_exp.py +97 -0
  13. liblaf_cherries-0.1.1/src/liblaf/cherries/integration/comet.py +142 -0
  14. liblaf_cherries-0.1.1/src/liblaf/cherries/integration/dvc.py +51 -0
  15. liblaf_cherries-0.1.1/src/liblaf/cherries/integration/git.py +44 -0
  16. liblaf_cherries-0.1.1/src/liblaf/cherries/integration/logging.py +45 -0
  17. {liblaf_cherries-0.0.14/src/liblaf/cherries/info → liblaf_cherries-0.1.1/src/liblaf/cherries/meta}/__init__.pyi +2 -2
  18. liblaf_cherries-0.1.1/src/liblaf/cherries/meta/_name.py +25 -0
  19. {liblaf_cherries-0.0.14 → liblaf_cherries-0.1.1}/src/liblaf/cherries/pathutils/__init__.pyi +2 -2
  20. {liblaf_cherries-0.0.14 → liblaf_cherries-0.1.1}/src/liblaf/cherries/pathutils/_convert.py +0 -4
  21. {liblaf_cherries-0.0.14 → liblaf_cherries-0.1.1}/src/liblaf/cherries/pathutils/_path.py +6 -6
  22. {liblaf_cherries-0.0.14 → liblaf_cherries-0.1.1}/src/liblaf/cherries/pathutils/_special.py +2 -2
  23. liblaf_cherries-0.1.1/src/liblaf/cherries/presets/__init__.pyi +5 -0
  24. liblaf_cherries-0.1.1/src/liblaf/cherries/presets/_default.py +46 -0
  25. liblaf_cherries-0.1.1/src/liblaf/cherries/presets/_playground.py +11 -0
  26. liblaf_cherries-0.1.1/src/liblaf/cherries/presets/typed.py +5 -0
  27. liblaf_cherries-0.1.1/src/liblaf/cherries/utils/__init__.py +3 -0
  28. liblaf_cherries-0.0.14/src/liblaf/cherries/__init__.pyi +0 -89
  29. liblaf_cherries-0.0.14/src/liblaf/cherries/_config.py +0 -22
  30. liblaf_cherries-0.0.14/src/liblaf/cherries/_run.py +0 -39
  31. liblaf_cherries-0.0.14/src/liblaf/cherries/info/_name.py +0 -30
  32. liblaf_cherries-0.0.14/src/liblaf/cherries/plugin/__init__.pyi +0 -77
  33. liblaf_cherries-0.0.14/src/liblaf/cherries/plugin/_abc.py +0 -131
  34. liblaf_cherries-0.0.14/src/liblaf/cherries/plugin/_dvc.py +0 -63
  35. liblaf_cherries-0.0.14/src/liblaf/cherries/plugin/_git.py +0 -46
  36. liblaf_cherries-0.0.14/src/liblaf/cherries/plugin/_logging.py +0 -31
  37. liblaf_cherries-0.0.14/src/liblaf/cherries/plugin/_mlflow.py +0 -88
  38. liblaf_cherries-0.0.14/src/liblaf/cherries/plugin/_run.py +0 -105
  39. liblaf_cherries-0.0.14/src/liblaf/cherries/presets/__init__.pyi +0 -3
  40. liblaf_cherries-0.0.14/src/liblaf/cherries/presets/_default.py +0 -32
  41. {liblaf_cherries-0.0.14 → liblaf_cherries-0.1.1}/LICENSE +0 -0
  42. {liblaf_cherries-0.0.14 → liblaf_cherries-0.1.1}/docs/README.md +0 -0
  43. {liblaf_cherries-0.0.14 → liblaf_cherries-0.1.1}/src/liblaf/cherries/__init__.py +0 -0
  44. {liblaf_cherries-0.0.14 → liblaf_cherries-0.1.1}/src/liblaf/cherries/_version.pyi +0 -0
  45. {liblaf_cherries-0.0.14/src/liblaf/cherries/info → liblaf_cherries-0.1.1/src/liblaf/cherries/config}/__init__.py +0 -0
  46. {liblaf_cherries-0.0.14/src/liblaf/cherries/pathutils → liblaf_cherries-0.1.1/src/liblaf/cherries/integration}/__init__.py +0 -0
  47. {liblaf_cherries-0.0.14/src/liblaf/cherries/plugin → liblaf_cherries-0.1.1/src/liblaf/cherries/meta}/__init__.py +0 -0
  48. {liblaf_cherries-0.0.14/src/liblaf/cherries/info → liblaf_cherries-0.1.1/src/liblaf/cherries/meta}/_git.py +0 -0
  49. {liblaf_cherries-0.0.14/src/liblaf/cherries/presets → liblaf_cherries-0.1.1/src/liblaf/cherries/pathutils}/__init__.py +0 -0
  50. {liblaf_cherries-0.0.14/src/liblaf/cherries/utils → liblaf_cherries-0.1.1/src/liblaf/cherries/presets}/__init__.py +0 -0
  51. {liblaf_cherries-0.0.14 → liblaf_cherries-0.1.1}/src/liblaf/cherries/py.typed +0 -0
  52. {liblaf_cherries-0.0.14 → liblaf_cherries-0.1.1}/src/liblaf/cherries/typed.py +0 -0
  53. {liblaf_cherries-0.0.14 → liblaf_cherries-0.1.1}/src/liblaf/cherries/utils/__init__.pyi +0 -0
  54. {liblaf_cherries-0.0.14 → liblaf_cherries-0.1.1}/src/liblaf/cherries/utils/_functools.py +0 -0
@@ -175,18 +175,23 @@ poetry.toml
175
175
 
176
176
  # End of https://www.toptal.com/developers/gitignore/api/python
177
177
 
178
- # pytest-codspeed
179
- .codspeed/
180
-
181
- # pytest-benchmark
182
- .benchmarks/
183
-
184
- _version.py
185
178
  *.log
186
179
  *.log.gz
187
180
  *.log.jsonl
188
181
  *.log.jsonl.gz
189
- junit.xml
190
182
  playground/
191
183
 
184
+ # hatch-vcs
185
+ _version.py
186
+
187
+ # pytest
188
+ junit.xml
189
+
190
+ # pytest-codspeed
191
+ .codspeed/
192
+
193
+ # pytest-benchmark
194
+ .benchmarks/
195
+
196
+ # MLflow
192
197
  mlruns/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: liblaf-cherries
3
- Version: 0.0.14
3
+ Version: 0.1.1
4
4
  Summary: Add your description here
5
5
  Project-URL: Changelog, https://github.com/liblaf/cherries/blob/main/CHANGELOG.md
6
6
  Project-URL: Documentation, https://liblaf.github.io/cherries/
@@ -31,15 +31,15 @@ Classifier: Topic :: System :: Logging
31
31
  Classifier: Topic :: Utilities
32
32
  Classifier: Typing :: Typed
33
33
  Requires-Python: >=3.12
34
+ Requires-Dist: comet-ml<4,>=3.49.10
34
35
  Requires-Dist: dvc[webdav]<4,>=3.59.2
35
- Requires-Dist: environs<15,>=14.1.1
36
+ Requires-Dist: environs<15,>=14.2.0
36
37
  Requires-Dist: gitpython<4,>=3.1.44
37
38
  Requires-Dist: lazy-loader<0.5,>=0.4
38
- Requires-Dist: liblaf-grapes<0.2,>=0.1.25
39
+ Requires-Dist: liblaf-grapes<0.2,>=0.1.26
39
40
  Requires-Dist: loguru<0.8,>=0.7.3
40
- Requires-Dist: mlflow<3,>=2.22.0
41
41
  Requires-Dist: pydantic-settings<3,>=2.9.1
42
- Requires-Dist: pydantic<3,>=2.11.4
42
+ Requires-Dist: pydantic<3,>=2.11.5
43
43
  Requires-Dist: rich<15,>=14.0.0
44
44
  Description-Content-Type: text/markdown
45
45
 
@@ -11,7 +11,6 @@ dev = ["icecream"]
11
11
  docs = [
12
12
  "gitpython",
13
13
  "mdx-truly-sane-lists",
14
- "mike",
15
14
  "mkdocs",
16
15
  "mkdocs-autorefs",
17
16
  "mkdocs-gen-files",
@@ -60,15 +59,15 @@ classifiers = [
60
59
  "Typing :: Typed",
61
60
  ]
62
61
  dependencies = [
62
+ "comet-ml>=3.49.10,<4",
63
63
  "dvc[webdav]>=3.59.2,<4",
64
- "environs>=14.1.1,<15",
64
+ "environs>=14.2.0,<15",
65
65
  "gitpython>=3.1.44,<4",
66
66
  "lazy-loader>=0.4,<0.5",
67
- "liblaf-grapes>=0.1.25,<0.2",
67
+ "liblaf-grapes>=0.1.26,<0.2",
68
68
  "loguru>=0.7.3,<0.8",
69
- "mlflow>=2.22.0,<3",
70
69
  "pydantic-settings>=2.9.1,<3",
71
- "pydantic>=2.11.4,<3",
70
+ "pydantic>=2.11.5,<3",
72
71
  "rich>=14.0.0,<15",
73
72
  ]
74
73
  description = "Add your description here"
@@ -106,22 +105,9 @@ packages = ["src/liblaf/"]
106
105
  [tool.hatch.version]
107
106
  source = "vcs"
108
107
 
109
- [tool.pixi.environments]
110
- default = { features = ["build", "dev", "docs", "test"] }
111
-
112
- [tool.pixi.project]
113
- channel-priority = "disabled"
114
- channels = ["conda-forge"]
115
- platforms = ["linux-64"]
116
-
117
- [tool.pixi.pypi-dependencies]
118
- "liblaf-cherries" = { editable = true, path = "." }
119
-
120
- [tool.pixi.system-requirements]
121
- cuda = "12"
122
- libc = { family = "glibc", version = "2.41" }
123
- linux = "6.13"
124
-
125
108
  [tool.pytest.ini_options]
126
109
  addopts = ["--showlocals", "--strict-config", "--strict-markers"]
127
110
  testpaths = ["benches/", "tests/"]
111
+
112
+ [tool.uv]
113
+ default-groups = "all"
@@ -0,0 +1,116 @@
1
+ from . import config, integration, meta, pathutils, presets, utils
2
+ from ._run import end, run, start
3
+ from .config import (
4
+ AssetKind,
5
+ BaseConfig,
6
+ MetaAsset,
7
+ PathProvider,
8
+ get_assets,
9
+ get_inputs,
10
+ get_outputs,
11
+ input, # noqa: A004
12
+ output,
13
+ )
14
+ from .integration import (
15
+ AddTag,
16
+ AddTags,
17
+ End,
18
+ Experiment,
19
+ LogAsset,
20
+ LogCode,
21
+ LogMetric,
22
+ LogMetrics,
23
+ LogOther,
24
+ LogOthers,
25
+ LogParam,
26
+ LogParams,
27
+ Plugin,
28
+ Start,
29
+ add_tag,
30
+ add_tags,
31
+ log_asset,
32
+ log_code,
33
+ log_metric,
34
+ log_metrics,
35
+ log_other,
36
+ log_others,
37
+ log_param,
38
+ log_params,
39
+ )
40
+ from .pathutils import (
41
+ as_os_path,
42
+ as_path,
43
+ as_posix,
44
+ data,
45
+ entrypoint,
46
+ exp_dir,
47
+ git_root,
48
+ git_root_safe,
49
+ inputs,
50
+ outputs,
51
+ params,
52
+ path,
53
+ src,
54
+ )
55
+
56
+ __all__ = [
57
+ "AddTag",
58
+ "AddTags",
59
+ "AssetKind",
60
+ "BaseConfig",
61
+ "End",
62
+ "Experiment",
63
+ "LogAsset",
64
+ "LogCode",
65
+ "LogMetric",
66
+ "LogMetrics",
67
+ "LogOther",
68
+ "LogOthers",
69
+ "LogParam",
70
+ "LogParams",
71
+ "MetaAsset",
72
+ "PathProvider",
73
+ "Plugin",
74
+ "Start",
75
+ "add_tag",
76
+ "add_tags",
77
+ "as_os_path",
78
+ "as_path",
79
+ "as_posix",
80
+ "config",
81
+ "data",
82
+ "end",
83
+ "end",
84
+ "end",
85
+ "entrypoint",
86
+ "exp_dir",
87
+ "get_assets",
88
+ "get_inputs",
89
+ "get_outputs",
90
+ "git_root",
91
+ "git_root_safe",
92
+ "input",
93
+ "inputs",
94
+ "integration",
95
+ "log_asset",
96
+ "log_code",
97
+ "log_metric",
98
+ "log_metrics",
99
+ "log_other",
100
+ "log_others",
101
+ "log_param",
102
+ "log_params",
103
+ "meta",
104
+ "output",
105
+ "outputs",
106
+ "params",
107
+ "path",
108
+ "pathutils",
109
+ "presets",
110
+ "run",
111
+ "run",
112
+ "src",
113
+ "start",
114
+ "start",
115
+ "utils",
116
+ ]
@@ -0,0 +1,51 @@
1
+ from collections.abc import Callable
2
+ from typing import get_type_hints
3
+
4
+ import pydantic
5
+
6
+ from liblaf.cherries import config, integration, presets
7
+ from liblaf.cherries import pathutils as _path
8
+
9
+
10
+ def run[C: pydantic.BaseModel, T](
11
+ main: Callable[[], T] | Callable[[C], T],
12
+ *,
13
+ play: bool = False,
14
+ preset: presets.Preset = presets.default,
15
+ ) -> T:
16
+ exp: integration.Experiment = start(preset=preset, play=play)
17
+ type_hints: dict[str, type[C]] = get_type_hints(main)
18
+ del type_hints["return"]
19
+ args: list[C] = []
20
+ if len(type_hints) == 1:
21
+ cls: type[C] = next(iter(type_hints.values()))
22
+ cfg: C = cls()
23
+ args.append(cfg)
24
+ for path in config.get_inputs(cfg):
25
+ exp.log_input(path)
26
+ try:
27
+ result: T = main(*args)
28
+ finally:
29
+ if len(type_hints) == 1:
30
+ cfg: C = args[0]
31
+ for path in config.get_outputs(cfg):
32
+ exp.log_output(path)
33
+ exp.end()
34
+ return result
35
+
36
+
37
+ def start(
38
+ preset: presets.Preset = presets.default, *, play: bool = False
39
+ ) -> integration.Experiment:
40
+ if play:
41
+ preset = presets.playground
42
+ exp: integration.Experiment = preset(integration.exp)
43
+ exp.start()
44
+ exp.log_other("cherries.entrypoint", _path.entrypoint(absolute=False))
45
+ exp.log_other("cherries.exp-dir", _path.exp_dir(absolute=False))
46
+ exp.log_other("cherries.start-time", exp.start_time)
47
+ return exp
48
+
49
+
50
+ def end() -> None:
51
+ integration.exp.end()
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '0.0.14'
21
- __version_tuple__ = version_tuple = (0, 0, 14)
20
+ __version__ = version = '0.1.1'
21
+ __version_tuple__ = version_tuple = (0, 1, 1)
@@ -0,0 +1,23 @@
1
+ from ._asset import (
2
+ AssetKind,
3
+ MetaAsset,
4
+ PathProvider,
5
+ get_assets,
6
+ get_inputs,
7
+ get_outputs,
8
+ input, # noqa: A004
9
+ output,
10
+ )
11
+ from ._config import BaseConfig
12
+
13
+ __all__ = [
14
+ "AssetKind",
15
+ "BaseConfig",
16
+ "MetaAsset",
17
+ "PathProvider",
18
+ "get_assets",
19
+ "get_inputs",
20
+ "get_outputs",
21
+ "input",
22
+ "output",
23
+ ]
@@ -0,0 +1,72 @@
1
+ import enum
2
+ from collections.abc import Callable, Iterable
3
+ from pathlib import Path
4
+ from typing import Any
5
+
6
+ import pydantic
7
+
8
+ from liblaf import grapes
9
+ from liblaf.cherries import pathutils as _path
10
+ from liblaf.cherries.typed import PathLike
11
+
12
+
13
+ class AssetKind(enum.StrEnum):
14
+ INPUT = enum.auto()
15
+ OUTPUT = enum.auto()
16
+
17
+
18
+ type PathProvider = (
19
+ PathLike | Iterable[PathLike] | Callable[[PathLike], PathLike | Iterable[PathLike]]
20
+ )
21
+
22
+
23
+ class MetaAsset:
24
+ kind: AssetKind
25
+ _extra: PathProvider | None = None
26
+
27
+ def __init__(self, kind: AssetKind, extra: PathProvider | None = None) -> None:
28
+ self.kind = kind
29
+ self._extra = extra
30
+
31
+ def get_extra(self, value: Path) -> list[Path]:
32
+ if self._extra is None:
33
+ return []
34
+ if callable(self._extra):
35
+ extra: Iterable[PathLike] = grapes.as_iterable(self._extra(value))
36
+ return [Path(p) for p in extra]
37
+ extra: Iterable[PathLike] = grapes.as_iterable(self._extra)
38
+ return [Path(p) for p in extra]
39
+
40
+
41
+ def get_assets(cfg: pydantic.BaseModel, kind: AssetKind) -> list[Path]:
42
+ assets: list[Path] = []
43
+ for name, info in type(cfg).model_fields.items():
44
+ value: Any = getattr(cfg, name)
45
+ if isinstance(value, pydantic.BaseModel):
46
+ assets.extend(get_assets(value, kind))
47
+ for meta in info.metadata:
48
+ if isinstance(meta, MetaAsset) and meta.kind == kind:
49
+ value: Path = Path(value)
50
+ assets.append(value)
51
+ assets.extend(meta.get_extra(value))
52
+ return assets
53
+
54
+
55
+ def get_inputs(cfg: pydantic.BaseModel) -> list[Path]:
56
+ return get_assets(cfg, AssetKind.INPUT)
57
+
58
+
59
+ def get_outputs(cfg: pydantic.BaseModel) -> list[Path]:
60
+ return get_assets(cfg, AssetKind.OUTPUT)
61
+
62
+
63
+ def input(path: PathLike, extra: PathProvider | None = None, **kwargs) -> Path: # noqa: A001
64
+ field_info: pydantic.fields.FieldInfo = pydantic.Field(_path.data(path), **kwargs) # pyright: ignore[reportAssignmentType]
65
+ field_info.metadata.append(MetaAsset(kind=AssetKind.INPUT, extra=extra))
66
+ return field_info # pyright: ignore[reportReturnType]
67
+
68
+
69
+ def output(path: PathLike, extra: PathProvider | None = None, **kwargs) -> Path:
70
+ field_info: pydantic.fields.FieldInfo = pydantic.Field(_path.data(path), **kwargs) # pyright: ignore[reportAssignmentType]
71
+ field_info.metadata.append(MetaAsset(kind=AssetKind.OUTPUT, extra=extra))
72
+ return field_info # pyright: ignore[reportReturnType]
@@ -0,0 +1,6 @@
1
+ import pydantic_settings as ps
2
+
3
+
4
+ class BaseConfig(ps.BaseSettings):
5
+ model_config = ps.SettingsConfigDict(cli_parse_args=True)
6
+ # TODO: add support for config files
@@ -0,0 +1,66 @@
1
+ from . import comet, dvc, git, logging
2
+ from ._abc import (
3
+ AddTag,
4
+ AddTags,
5
+ End,
6
+ LogAsset,
7
+ LogCode,
8
+ LogMetric,
9
+ LogMetrics,
10
+ LogOther,
11
+ LogOthers,
12
+ LogParam,
13
+ LogParams,
14
+ Plugin,
15
+ Start,
16
+ )
17
+ from ._exp import (
18
+ Experiment,
19
+ add_tag,
20
+ add_tags,
21
+ end,
22
+ exp,
23
+ log_asset,
24
+ log_code,
25
+ log_metric,
26
+ log_metrics,
27
+ log_other,
28
+ log_others,
29
+ log_param,
30
+ log_params,
31
+ start,
32
+ )
33
+
34
+ __all__ = [
35
+ "AddTag",
36
+ "AddTags",
37
+ "End",
38
+ "Experiment",
39
+ "LogAsset",
40
+ "LogCode",
41
+ "LogMetric",
42
+ "LogMetrics",
43
+ "LogOther",
44
+ "LogOthers",
45
+ "LogParam",
46
+ "LogParams",
47
+ "Plugin",
48
+ "Start",
49
+ "add_tag",
50
+ "add_tags",
51
+ "comet",
52
+ "dvc",
53
+ "end",
54
+ "exp",
55
+ "git",
56
+ "log_asset",
57
+ "log_code",
58
+ "log_metric",
59
+ "log_metrics",
60
+ "log_other",
61
+ "log_others",
62
+ "log_param",
63
+ "log_params",
64
+ "logging",
65
+ "start",
66
+ ]
@@ -0,0 +1,144 @@
1
+ import bisect
2
+ import functools
3
+ import operator
4
+ from collections.abc import Iterable, Mapping
5
+ from typing import Any
6
+
7
+ import attrs
8
+ from loguru import logger
9
+
10
+ from liblaf.cherries.typed import PathLike
11
+
12
+
13
+ @attrs.define
14
+ @functools.total_ordering
15
+ class Plugin[**P, T]:
16
+ priority: int = attrs.field(default=0, kw_only=True)
17
+ _children: list["Plugin"] = attrs.field(
18
+ factory=list, eq=False, order=False, alias="children"
19
+ )
20
+
21
+ def __attrs_post_init__(self) -> None:
22
+ self._children.sort(key=operator.attrgetter("priority"))
23
+
24
+ def __call__(self, *args: P.args, **kwargs: P.kwargs) -> T:
25
+ ret: T | None = None
26
+ for child in self._children:
27
+ try:
28
+ ret = child(*args, **kwargs)
29
+ except BaseException as err:
30
+ if isinstance(err, (KeyboardInterrupt, SystemExit)):
31
+ raise
32
+ logger.exception(child)
33
+ return ret # pyright: ignore[reportReturnType]
34
+
35
+ def __lt__(self, other: "Plugin") -> bool:
36
+ if not isinstance(other, Plugin):
37
+ return NotImplemented
38
+ return self.priority < other.priority
39
+
40
+ def __eq__(self, other: "Plugin") -> bool: # pyright: ignore[reportIncompatibleMethodOverride]
41
+ if not isinstance(other, Plugin):
42
+ return NotImplemented
43
+ return self.priority == other.priority
44
+
45
+ @property
46
+ def children(self) -> list["Plugin"]:
47
+ return self._children
48
+
49
+ def add(self, *children: "Plugin") -> None:
50
+ for c in children:
51
+ bisect.insort(self._children, c, key=operator.attrgetter("priority"))
52
+
53
+ def extend(self, children: Iterable["Plugin"]) -> None:
54
+ self.add(*children)
55
+
56
+ def remove(self, child: "Plugin") -> None:
57
+ self._children.remove(child)
58
+
59
+
60
+ @attrs.define
61
+ class AddTag(Plugin):
62
+ def __call__(self, tag: str, /, **kwargs) -> None:
63
+ super().__call__(tag, **kwargs)
64
+
65
+
66
+ @attrs.define
67
+ class AddTags(Plugin):
68
+ def __call__(self, tags: Iterable[str], /, **kwargs) -> None:
69
+ super().__call__(tags, **kwargs)
70
+
71
+
72
+ @attrs.define
73
+ class End(Plugin):
74
+ def __call__(self, **kwargs) -> None:
75
+ super().__call__(**kwargs)
76
+
77
+
78
+ @attrs.define
79
+ class LogAsset(Plugin):
80
+ def __call__(self, path: PathLike, /, prefix: str | None = None, **kwargs) -> None:
81
+ super().__call__(path, prefix=prefix, **kwargs)
82
+
83
+
84
+ @attrs.define
85
+ class LogCode(Plugin):
86
+ def __call__(self, path: PathLike, /, **kwargs) -> None:
87
+ super().__call__(path, **kwargs)
88
+
89
+
90
+ @attrs.define
91
+ class LogMetric(Plugin):
92
+ def __call__(
93
+ self,
94
+ key: str,
95
+ value: float,
96
+ /,
97
+ step: int | None = None,
98
+ epoch: int | None = None,
99
+ **kwargs,
100
+ ) -> None:
101
+ super().__call__(key, value, step=step, epoch=epoch, **kwargs)
102
+
103
+
104
+ @attrs.define
105
+ class LogMetrics(Plugin):
106
+ def __call__(
107
+ self,
108
+ metrics: Mapping[str, Any],
109
+ /,
110
+ step: int | None = None,
111
+ epoch: int | None = None,
112
+ **kwargs,
113
+ ) -> None:
114
+ super().__call__(metrics, step=step, epoch=epoch, **kwargs)
115
+
116
+
117
+ @attrs.define
118
+ class LogOther(Plugin):
119
+ def __call__(self, key: str, value: Any, /, **kwargs) -> None:
120
+ super().__call__(key, value, **kwargs)
121
+
122
+
123
+ @attrs.define
124
+ class LogOthers(Plugin):
125
+ def __call__(self, others: Mapping[str, Any], /, **kwargs) -> None:
126
+ super().__call__(others, **kwargs)
127
+
128
+
129
+ @attrs.define
130
+ class LogParam(Plugin):
131
+ def __call__(self, name: str, value: Any, /, **kwargs) -> None:
132
+ super().__call__(name, value, **kwargs)
133
+
134
+
135
+ @attrs.define
136
+ class LogParams(Plugin):
137
+ def __call__(self, params: Mapping[str, Any], /, **kwargs) -> None:
138
+ super().__call__(params, **kwargs)
139
+
140
+
141
+ @attrs.define
142
+ class Start(Plugin):
143
+ def __call__(self, **kwargs) -> None:
144
+ super().__call__(**kwargs)
@@ -0,0 +1,97 @@
1
+ import datetime
2
+ import functools
3
+ from os import PathLike
4
+ from pathlib import Path
5
+
6
+ import attrs
7
+ import comet_ml as comet
8
+
9
+ from liblaf.cherries import pathutils as _path
10
+
11
+ from ._abc import (
12
+ AddTag,
13
+ AddTags,
14
+ End,
15
+ LogAsset,
16
+ LogCode,
17
+ LogMetric,
18
+ LogMetrics,
19
+ LogOther,
20
+ LogOthers,
21
+ LogParam,
22
+ LogParams,
23
+ Start,
24
+ )
25
+
26
+
27
+ @attrs.define
28
+ class Experiment:
29
+ add_tag: AddTag = attrs.field(factory=AddTag, init=False)
30
+ add_tags: AddTags = attrs.field(factory=AddTags, init=False)
31
+ end: End = attrs.field(factory=End, init=False)
32
+ log_asset: LogAsset = attrs.field(factory=LogAsset, init=False)
33
+ log_code: LogCode = attrs.field(factory=LogCode, init=False)
34
+ log_metric: LogMetric = attrs.field(factory=LogMetric, init=False)
35
+ log_metrics: LogMetrics = attrs.field(factory=LogMetrics, init=False)
36
+ log_other: LogOther = attrs.field(factory=LogOther, init=False)
37
+ log_others: LogOthers = attrs.field(factory=LogOthers, init=False)
38
+ log_param: LogParam = attrs.field(factory=LogParam, init=False)
39
+ log_params: LogParams = attrs.field(factory=LogParams, init=False)
40
+ start: Start = attrs.field(factory=Start, init=False)
41
+
42
+ @property
43
+ def comet(self) -> comet.CometExperiment:
44
+ return comet.get_running_experiment() # pyright: ignore[reportReturnType]
45
+
46
+ @functools.cached_property
47
+ def exp_dir(self) -> Path:
48
+ return _path.exp_dir(absolute=True)
49
+
50
+ @property
51
+ def exp_key(self) -> str:
52
+ return self.comet.get_key()
53
+
54
+ @property
55
+ def exp_name(self) -> str:
56
+ return self.comet.get_name()
57
+
58
+ @exp_name.setter
59
+ def exp_name(self, value: str) -> None:
60
+ self.comet.set_name(value)
61
+
62
+ @property
63
+ def exp_url(self) -> str:
64
+ return self.comet.url # pyright: ignore[reportReturnType]
65
+
66
+ @property
67
+ def project_id(self) -> str:
68
+ return self.comet.project_id # pyright: ignore[reportReturnType]
69
+
70
+ @property
71
+ def project_name(self) -> str:
72
+ return self.comet.project_name
73
+
74
+ @functools.cached_property
75
+ def start_time(self) -> datetime.datetime:
76
+ return datetime.datetime.now().astimezone()
77
+
78
+ def log_input(self, path: PathLike, /, **kwargs) -> None:
79
+ self.log_asset(path, prefix="inputs/", **kwargs)
80
+
81
+ def log_output(self, path: PathLike, /, **kwargs) -> None:
82
+ self.log_asset(path, prefix="outputs/", **kwargs)
83
+
84
+
85
+ exp = Experiment()
86
+ add_tag: AddTag = exp.add_tag
87
+ add_tags: AddTags = exp.add_tags
88
+ end: End = exp.end
89
+ log_asset: LogAsset = exp.log_asset
90
+ log_code: LogCode = exp.log_code
91
+ log_metric: LogMetric = exp.log_metric
92
+ log_metrics: LogMetrics = exp.log_metrics
93
+ log_other: LogOther = exp.log_other
94
+ log_others: LogOthers = exp.log_others
95
+ log_param: LogParam = exp.log_param
96
+ log_params: LogParams = exp.log_params
97
+ start: Start = exp.start