liblaf-cherries 0.0.9__py3-none-any.whl → 0.0.11__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.
Files changed (47) hide show
  1. liblaf/cherries/__init__.pyi +75 -24
  2. liblaf/cherries/_run.py +31 -26
  3. liblaf/cherries/_version.py +2 -2
  4. liblaf/cherries/info/__init__.pyi +11 -0
  5. liblaf/cherries/info/_exp_name.py +19 -0
  6. liblaf/cherries/info/_git.py +49 -0
  7. liblaf/cherries/pathutils/__init__.pyi +20 -0
  8. liblaf/cherries/pathutils/_convert.py +40 -0
  9. liblaf/cherries/pathutils/_path.py +68 -0
  10. liblaf/cherries/pathutils/_special.py +50 -0
  11. liblaf/cherries/plugin/__init__.pyi +74 -6
  12. liblaf/cherries/plugin/_abc.py +112 -21
  13. liblaf/cherries/plugin/_dvc.py +47 -0
  14. liblaf/cherries/plugin/_git.py +41 -18
  15. liblaf/cherries/plugin/_logging.py +22 -30
  16. liblaf/cherries/plugin/_mlflow.py +73 -0
  17. liblaf/cherries/plugin/_run.py +105 -0
  18. liblaf/cherries/presets/__init__.pyi +3 -0
  19. liblaf/cherries/presets/_default.py +32 -0
  20. liblaf/cherries/typed.py +3 -0
  21. liblaf/cherries/utils/__init__.pyi +3 -0
  22. liblaf/cherries/utils/_functools.py +6 -0
  23. {liblaf_cherries-0.0.9.dist-info → liblaf_cherries-0.0.11.dist-info}/METADATA +7 -5
  24. liblaf_cherries-0.0.11.dist-info/RECORD +35 -0
  25. liblaf/cherries/_env.py +0 -4
  26. liblaf/cherries/_experiment.py +0 -111
  27. liblaf/cherries/_main.py +0 -38
  28. liblaf/cherries/_start.py +0 -26
  29. liblaf/cherries/git/__init__.pyi +0 -18
  30. liblaf/cherries/git/_commit.py +0 -17
  31. liblaf/cherries/git/_entrypoint.py +0 -12
  32. liblaf/cherries/git/_grapes.py +0 -3
  33. liblaf/cherries/git/github/__init__.pyi +0 -4
  34. liblaf/cherries/git/github/_link.py +0 -25
  35. liblaf/cherries/git/github/_repo.py +0 -23
  36. liblaf/cherries/integration/__init__.pyi +0 -5
  37. liblaf/cherries/integration/_backend.py +0 -53
  38. liblaf/cherries/integration/_factory.py +0 -13
  39. liblaf/cherries/integration/_neptune.py +0 -66
  40. liblaf/cherries/plugin/_default.py +0 -9
  41. liblaf/cherries/plugin/_restic.py +0 -60
  42. liblaf_cherries-0.0.9.dist-info/RECORD +0 -38
  43. /liblaf/cherries/{git → info}/__init__.py +0 -0
  44. /liblaf/cherries/{git/github → pathutils}/__init__.py +0 -0
  45. /liblaf/cherries/{integration → presets}/__init__.py +0 -0
  46. {liblaf_cherries-0.0.9.dist-info → liblaf_cherries-0.0.11.dist-info}/WHEEL +0 -0
  47. {liblaf_cherries-0.0.9.dist-info → liblaf_cherries-0.0.11.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,47 @@
1
+ import subprocess as sp
2
+ from pathlib import Path
3
+ from typing import override
4
+
5
+ import attrs
6
+
7
+ from liblaf.cherries import pathutils as _path
8
+ from liblaf.cherries.typed import PathLike
9
+
10
+ from ._abc import End, LogArtifact, LogArtifacts
11
+
12
+
13
+ @attrs.define
14
+ class DvcEnd(End):
15
+ @override
16
+ def __call__(self) -> None:
17
+ sp.run(["dvc", "status"], check=True)
18
+ sp.run(["dvc", "push"], check=True)
19
+
20
+
21
+ @attrs.define
22
+ class DvcLogArtifact(LogArtifact):
23
+ @override
24
+ def __call__(
25
+ self, local_path: PathLike, artifact_path: PathLike | None = None, **kwargs
26
+ ) -> Path:
27
+ local_path: Path = _path.as_path(local_path)
28
+ sp.run(["dvc", "add", local_path], check=False)
29
+ return local_path
30
+
31
+
32
+ @attrs.define
33
+ class DvcLogArtifacts(LogArtifacts):
34
+ @override
35
+ def __call__(
36
+ self, local_dir: PathLike, artifact_path: PathLike | None = None, **kwargs
37
+ ) -> Path:
38
+ local_dir: Path = _path.as_path(local_dir)
39
+ sp.run(["dvc", "add", local_dir], check=False)
40
+ return local_dir
41
+
42
+
43
+ def check_ignore(local_path: PathLike) -> bool:
44
+ proc: sp.CompletedProcess[bytes] = sp.run(
45
+ ["dvc", "check-ignore", local_path], check=False
46
+ )
47
+ return proc.returncode == 0
@@ -1,23 +1,46 @@
1
- import git
2
- import pydantic_settings as ps
1
+ from typing import override
3
2
 
4
- from liblaf import cherries
3
+ import attrs
4
+ from environs import env
5
5
 
6
+ from liblaf.cherries import info as _info
6
7
 
7
- class PluginGit(cherries.Plugin):
8
- model_config = ps.SettingsConfigDict(env_prefix=cherries.ENV_PREFIX + "GIT_")
9
- auto_commit: bool = False
10
- auto_commit_message: str = cherries.git.DEFAULT_COMMIT_MESSAGE
8
+ from ._abc import End, Start
9
+ from ._run import run
11
10
 
12
- def _pre_start(self) -> None:
13
- if self.auto_commit:
14
- cherries.git.commit(self.auto_commit_message)
15
11
 
16
- def _post_start(self, run: cherries.Experiment) -> None:
17
- r = git.Repo(search_parent_directories=True)
18
- sha: str = r.head.commit.hexsha
19
- run.log_other("cherries/git/sha", sha)
20
- if browse := cherries.git.permalink(repo=r):
21
- run.log_other("cherries/git/browse", browse)
22
- if entrypoint := cherries.git.permalink(repo=r, filepath=run.entrypoint):
23
- run.log_other("cherries/git/entrypoint", entrypoint)
12
+ @attrs.define
13
+ class GitEnd(End):
14
+ dry_run: bool = env.bool("LIBLAF_CHERRIES_GIT_DRY_RUN", default=False)
15
+
16
+ @override
17
+ def __call__(self) -> None:
18
+ git_auto_commit(
19
+ "chore(cherries): auto commit (on run end)", dry_run=self.dry_run
20
+ )
21
+
22
+
23
+ @attrs.define
24
+ class GitStart(Start):
25
+ dry_run: bool = env.bool("LIBLAF_CHERRIES_GIT_DRY_RUN", default=False)
26
+
27
+ @override
28
+ def __call__(self) -> None:
29
+ git_auto_commit(
30
+ "chore(cherries): auto commit (on run start)", dry_run=self.dry_run
31
+ )
32
+
33
+
34
+ def git_auto_commit(
35
+ header: str = "chore(cherries): auto commit", *, dry_run: bool = False
36
+ ) -> None:
37
+ body: str = ""
38
+ if run.run_name and run.run_url:
39
+ body += f"🏃 View run {run.run_name} at: {run.run_url}\n"
40
+ if run.exp_name and run.exp_url:
41
+ body += f"🧪 View experiment {run.exp_name} at: {run.exp_url}\n"
42
+ message: str = f"{header}\n\n{body}" if body else header
43
+ _info.git_auto_commit(message, dry_run=dry_run)
44
+ run.set_tag("cherries.git.branch", _info.git_branch())
45
+ run.set_tag("cherries.git.sha", _info.git_commit_sha())
46
+ run.set_tag("cherries.git.url", _info.git_commit_url())
@@ -1,39 +1,31 @@
1
- from pathlib import Path
1
+ from typing import override
2
2
 
3
- import loguru
4
- import pydantic_settings as ps
3
+ import attrs
5
4
  from loguru import logger
6
5
 
7
- import liblaf.cherries as cherries # noqa: PLR0402
8
6
  from liblaf import grapes
9
7
 
10
- DEFAULT_FILTER: "loguru.FilterDict" = {
11
- "": "INFO",
12
- "__main__": "TRACE",
13
- "liblaf": "DEBUG",
14
- }
15
- DEFAULT_FILE_FILTER: "loguru.FilterDict" = {
16
- **DEFAULT_FILTER,
17
- "liblaf.cherries": "SUCCESS",
18
- }
8
+ from ._abc import End, Start
9
+ from ._run import run
19
10
 
20
11
 
21
- class PluginLogging(cherries.Plugin):
22
- model_config = ps.SettingsConfigDict(env_prefix=cherries.ENV_PREFIX + "LOGGING_")
23
- file: Path | None = Path("run.log")
24
- jsonl: Path | None = Path("run.log.jsonl")
12
+ @attrs.define
13
+ class LoggingEnd(End):
14
+ @override
15
+ def __call__(self) -> None:
16
+ logger.complete()
17
+ run.log_artifact(run.exp_dir / "run.log")
18
+ run.log_artifact(run.exp_dir / "run.log.jsonl")
25
19
 
26
- def _pre_start(self) -> None:
27
- handlers: list[loguru.HandlerConfig] = [grapes.logging.rich_handler()]
28
- if self.file is not None:
29
- handlers.append(grapes.logging.file_handler(self.file))
30
- if self.jsonl is not None:
31
- handlers.append(grapes.logging.jsonl_handler(self.jsonl))
32
- grapes.init_logging(handlers=handlers)
33
20
 
34
- def _pre_end(self, run: cherries.Experiment) -> None:
35
- logger.complete()
36
- if self.file is not None:
37
- run.upload_file("cherries/logging/run.log", self.file)
38
- if self.jsonl is not None:
39
- run.upload_file("cherries/logging/run.log.jsonl", self.jsonl)
21
+ @attrs.define
22
+ class LoggingStart(Start):
23
+ @override
24
+ def __call__(self) -> None:
25
+ grapes.init_logging(
26
+ handlers=[
27
+ grapes.logging.rich_handler(),
28
+ grapes.logging.file_handler(file=run.exp_dir / "run.log"),
29
+ grapes.logging.jsonl_handler(run.exp_dir / "run.log.jsonl"),
30
+ ]
31
+ )
@@ -0,0 +1,73 @@
1
+ from pathlib import Path
2
+ from typing import Any, override
3
+
4
+ import attrs
5
+ import mlflow
6
+
7
+ from liblaf.cherries import info as _info
8
+ from liblaf.cherries import pathutils as _path
9
+ from liblaf.cherries.typed import PathLike
10
+
11
+ from ._abc import End, LogArtifact, LogArtifacts, LogMetric, LogParam, SetTag, Start
12
+
13
+
14
+ @attrs.define
15
+ class MlflowEnd(End):
16
+ @override
17
+ def __call__(self) -> None:
18
+ mlflow.end_run()
19
+
20
+
21
+ @attrs.define
22
+ class MlflowLogArtifact(LogArtifact):
23
+ @override
24
+ def __call__(
25
+ self, local_path: PathLike, artifact_path: PathLike | None = None, **kwargs
26
+ ) -> Path:
27
+ mlflow.log_artifact(
28
+ _path.as_os_path(local_path), _path.as_posix(artifact_path), **kwargs
29
+ )
30
+ return _path.as_path(local_path)
31
+
32
+
33
+ @attrs.define
34
+ class MlflowLogArtifacts(LogArtifacts):
35
+ @override
36
+ def __call__(
37
+ self, local_dir: PathLike, artifact_path: PathLike | None = None, **kwargs
38
+ ) -> Path:
39
+ mlflow.log_artifact(
40
+ _path.as_os_path(local_dir), _path.as_posix(artifact_path), **kwargs
41
+ )
42
+ return _path.as_path(local_dir)
43
+
44
+
45
+ @attrs.define
46
+ class MlflowLogParam(LogParam):
47
+ @override
48
+ def __call__(self, key: str, value: Any, **kwargs) -> None:
49
+ mlflow.log_param(key, value, **kwargs)
50
+
51
+
52
+ @attrs.define
53
+ class MlflowLogMetric(LogMetric):
54
+ @override
55
+ def __call__(
56
+ self, key: str, value: float, step: int | None = None, **kwargs
57
+ ) -> None:
58
+ mlflow.log_metric(key, value, step, **kwargs)
59
+
60
+
61
+ @attrs.define
62
+ class MlflowSetTag(SetTag):
63
+ @override
64
+ def __call__(self, key: str, value: Any, **kwargs) -> None:
65
+ mlflow.set_tag(key, value, **kwargs)
66
+
67
+
68
+ @attrs.define
69
+ class MlflowStart(Start):
70
+ @override
71
+ def __call__(self) -> None:
72
+ mlflow.set_experiment(_info.exp_name())
73
+ mlflow.start_run()
@@ -0,0 +1,105 @@
1
+ import datetime
2
+ import functools
3
+ from pathlib import Path
4
+
5
+ import attrs
6
+ import mlflow
7
+
8
+ from liblaf.cherries import pathutils as _path
9
+ from liblaf.cherries.typed import PathLike
10
+
11
+ from ._abc import End, LogArtifact, LogArtifacts, LogMetric, LogParam, SetTag, Start
12
+
13
+
14
+ @attrs.define
15
+ class Run:
16
+ end: End = attrs.field(factory=End, init=False)
17
+ log_artifact: LogArtifact = attrs.field(factory=LogArtifact, init=False)
18
+ log_artifacts: LogArtifacts = attrs.field(factory=LogArtifacts, init=False)
19
+ log_metric: LogMetric = attrs.field(factory=LogMetric, init=False)
20
+ log_param: LogParam = attrs.field(factory=LogParam, init=False)
21
+ set_tag: SetTag = attrs.field(factory=SetTag, init=False)
22
+ start: Start = attrs.field(factory=Start, init=False)
23
+
24
+ @property
25
+ def active_run(self) -> mlflow.ActiveRun:
26
+ return mlflow.active_run() # pyright: ignore[reportReturnType]
27
+
28
+ @functools.cached_property
29
+ def exp_dir(self) -> Path:
30
+ return _path.exp_dir(absolute=True)
31
+
32
+ @property
33
+ def exp_id(self) -> str:
34
+ return self.active_run.info.experiment_id
35
+
36
+ @property
37
+ def exp_name(self) -> str:
38
+ return self.exp_id
39
+
40
+ @property
41
+ def exp_url(self) -> str:
42
+ tracking_uri: str = self.tracking_uri.rstrip("/")
43
+ return f"{tracking_uri}/#/experiments/{self.exp_id}"
44
+
45
+ @property
46
+ def tracking_uri(self) -> str:
47
+ return mlflow.get_tracking_uri()
48
+
49
+ @property
50
+ def run_id(self) -> str:
51
+ return self.active_run.info.run_id
52
+
53
+ @property
54
+ def run_name(self) -> str:
55
+ return self.active_run.info.run_name # pyright: ignore[reportReturnType]
56
+
57
+ @property
58
+ def run_url(self) -> str:
59
+ return f"{self.exp_url}/runs/{self.run_id}"
60
+
61
+ @property
62
+ def start_time(self) -> datetime.datetime:
63
+ return datetime.datetime.fromtimestamp(
64
+ self.active_run.info.start_time / 1000, tz=datetime.UTC
65
+ ).astimezone()
66
+
67
+ def log_input(
68
+ self, local_path: PathLike, artifact_path: PathLike | None = "inputs", **kwargs
69
+ ) -> Path:
70
+ return self.log_artifact(local_path, artifact_path, **kwargs)
71
+
72
+ def log_inputs(
73
+ self, local_dir: PathLike, artifact_path: PathLike | None = "inputs", **kwargs
74
+ ) -> Path:
75
+ return self.log_artifacts(local_dir, artifact_path, **kwargs)
76
+
77
+ def log_output(
78
+ self, local_path: PathLike, artifact_path: PathLike | None = "outputs", **kwargs
79
+ ) -> Path:
80
+ return self.log_artifact(local_path, artifact_path, **kwargs)
81
+
82
+ def log_outputs(
83
+ self, local_dir: PathLike, artifact_path: PathLike | None = "outputs", **kwargs
84
+ ) -> Path:
85
+ return self.log_artifacts(local_dir, artifact_path, **kwargs)
86
+
87
+ def log_src(
88
+ self, local_path: PathLike, artifact_path: PathLike | None = "src", **kwargs
89
+ ) -> Path:
90
+ return self.log_artifact(local_path, artifact_path, **kwargs)
91
+
92
+
93
+ run = Run()
94
+ end: End = run.end
95
+ log_artifact: LogArtifact = run.log_artifact
96
+ log_artifacts: LogArtifacts = run.log_artifacts
97
+ log_metric: LogMetric = run.log_metric
98
+ log_param: LogParam = run.log_param
99
+ set_tag: SetTag = run.set_tag
100
+ start: Start = run.start
101
+ log_input = run.log_input
102
+ log_inputs = run.log_inputs
103
+ log_output = run.log_output
104
+ log_outputs = run.log_outputs
105
+ log_src = run.log_src
@@ -0,0 +1,3 @@
1
+ from ._default import default
2
+
3
+ __all__ = ["default"]
@@ -0,0 +1,32 @@
1
+ from liblaf.cherries import plugin
2
+
3
+
4
+ def default() -> plugin.Run:
5
+ plugin.run.end.add(
6
+ plugin.LoggingEnd(),
7
+ plugin.GitEnd(),
8
+ plugin.DvcEnd(),
9
+ plugin.MlflowEnd(),
10
+ )
11
+ plugin.run.log_artifact.add(
12
+ plugin.DvcLogArtifact(),
13
+ plugin.MlflowLogArtifact(),
14
+ )
15
+ plugin.run.log_artifacts.add(
16
+ plugin.DvcLogArtifacts(),
17
+ plugin.MlflowLogArtifacts(),
18
+ )
19
+ plugin.run.log_metric.add(
20
+ plugin.MlflowLogMetric(),
21
+ )
22
+ plugin.run.log_param.add(
23
+ plugin.MlflowLogParam(),
24
+ )
25
+ plugin.run.set_tag.add(
26
+ plugin.MlflowSetTag(),
27
+ )
28
+ plugin.run.start.add(
29
+ plugin.LoggingStart(),
30
+ plugin.MlflowStart(),
31
+ )
32
+ return plugin.run
@@ -0,0 +1,3 @@
1
+ import os
2
+
3
+ type PathLike = str | os.PathLike[str]
@@ -0,0 +1,3 @@
1
+ from ._functools import cache
2
+
3
+ __all__ = ["cache"]
@@ -0,0 +1,6 @@
1
+ import functools
2
+ from collections.abc import Callable
3
+
4
+
5
+ def cache[**P, T](fn: Callable[P, T]) -> Callable[P, T]:
6
+ return functools.cache(fn) # pyright: ignore[reportReturnType]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: liblaf-cherries
3
- Version: 0.0.9
3
+ Version: 0.0.11
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,13 +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: dvc[webdav]<4,>=3.59.2
34
35
  Requires-Dist: environs<15,>=14.1.1
36
+ Requires-Dist: gitpython<4,>=3.1.44
35
37
  Requires-Dist: lazy-loader<0.5,>=0.4
36
- Requires-Dist: liblaf-grapes<0.2,>=0.1.14
38
+ Requires-Dist: liblaf-grapes<0.2,>=0.1.21
37
39
  Requires-Dist: loguru<0.8,>=0.7.3
38
- Requires-Dist: neptune<2,>=1.13.0
39
- Requires-Dist: pydantic-settings<3,>=2.8.1
40
- Requires-Dist: pydantic<3,>=2.11.1
40
+ Requires-Dist: mlflow<3,>=2.22.0
41
+ Requires-Dist: pydantic-settings<3,>=2.9.1
42
+ Requires-Dist: pydantic<3,>=2.11.4
41
43
  Requires-Dist: rich<15,>=14.0.0
42
44
  Description-Content-Type: text/markdown
43
45
 
@@ -0,0 +1,35 @@
1
+ liblaf/cherries/__init__.py,sha256=OHb6Xou2v6u42swTgjRfzej4CIlRg4OmgOIQXUiRjKA,97
2
+ liblaf/cherries/__init__.pyi,sha256=F3-WoAFWZ5Kl4DnUcVIuSRFraHF_EWEhcaVBWnuBiXo,1338
3
+ liblaf/cherries/_config.py,sha256=rm-Y6roi8hBvQYdH-VQh_ovWCyVsX_7R0x1jokBPCDM,741
4
+ liblaf/cherries/_run.py,sha256=NyrhAbOAEu01Xo5Rxyo19PMcwxrCQGj0hbxt1eSnjPE,860
5
+ liblaf/cherries/_version.py,sha256=rk0lhpp6Em5toAI4J7GwApfOdY7w_QTcFpJpUR4GdVY,513
6
+ liblaf/cherries/_version.pyi,sha256=Pnv4Bxw13LHeuVkPLPsTtnp4N4jOGcAfFJw05uMMgBY,108
7
+ liblaf/cherries/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ liblaf/cherries/typed.py,sha256=mim8QVtwczTSHyw5mhEdfFcXis9o32n0CZyu8BrEorE,50
9
+ liblaf/cherries/info/__init__.py,sha256=OHb6Xou2v6u42swTgjRfzej4CIlRg4OmgOIQXUiRjKA,97
10
+ liblaf/cherries/info/__init__.pyi,sha256=UsHH6XpE_RRre9sJdwgzOnV9HElnE00ltRKnfXOQso4,252
11
+ liblaf/cherries/info/_exp_name.py,sha256=dlC1OIg9RM2oYW7qsWCF6AGV0z6bbQxpJOsdft-KUyk,454
12
+ liblaf/cherries/info/_git.py,sha256=oy1xx23sXWA7RqQuLGc3HxcLCF7zHTSpZvZ9pukJGdI,1232
13
+ liblaf/cherries/pathutils/__init__.py,sha256=OHb6Xou2v6u42swTgjRfzej4CIlRg4OmgOIQXUiRjKA,97
14
+ liblaf/cherries/pathutils/__init__.pyi,sha256=mr3Dk8ragpNGXmXJriMt21vc7YmqUD6X587exFe9tCI,413
15
+ liblaf/cherries/pathutils/_convert.py,sha256=JTO9vETXvj7f4GTtIbOmAoDNEDc_4hoEHZ7ujbyTgS8,859
16
+ liblaf/cherries/pathutils/_path.py,sha256=igdLrCqk6aIX36V8c1LClhrFViDFWx4WbiR67BcbVIY,1548
17
+ liblaf/cherries/pathutils/_special.py,sha256=b5D3PtW__u0Zpiv69OYoV7TTYc6Dgs4Bu-P8jwrROV4,1415
18
+ liblaf/cherries/plugin/__init__.py,sha256=OHb6Xou2v6u42swTgjRfzej4CIlRg4OmgOIQXUiRjKA,97
19
+ liblaf/cherries/plugin/__init__.pyi,sha256=GH1Hh8uvHdQmne0mJ4OJIM1RhxZAxmlaJ9PVyYO7lw0,1267
20
+ liblaf/cherries/plugin/_abc.py,sha256=Y2IZTd7NPOFl5dv-DeX7H3LE7u4R0Utv9S8ho3pCJGA,3367
21
+ liblaf/cherries/plugin/_dvc.py,sha256=Bl6SoZHDcOu86ybu9uPl-Zk3ezBcaAiW0tTPVuj2-5c,1226
22
+ liblaf/cherries/plugin/_git.py,sha256=87gLkIsOc6_fO7RcNdwsXVzl1FCMzhDSkxeaMZwwXXM,1345
23
+ liblaf/cherries/plugin/_logging.py,sha256=snCmJa1qTtNsJ5GzTkejgcO4ySWA0m0sMH8qY4w08ww,728
24
+ liblaf/cherries/plugin/_mlflow.py,sha256=HlTat251ogGJBvxvw_-ZjDtCEsFtcDhofrjRfJIV2qY,1833
25
+ liblaf/cherries/plugin/_run.py,sha256=XqMOEFDqba8xOaUIynJwS1Dg6JGVpqtWsHG_XJenypk,3287
26
+ liblaf/cherries/presets/__init__.py,sha256=OHb6Xou2v6u42swTgjRfzej4CIlRg4OmgOIQXUiRjKA,97
27
+ liblaf/cherries/presets/__init__.pyi,sha256=ka0zjVDb1kWn1VuDGjAzMX5S9sVKKYcxKelp4EwUBf8,53
28
+ liblaf/cherries/presets/_default.py,sha256=hhe10JzMui8WJhvoLlOfDCHMRlPmdnzRSduFH7rCtP0,742
29
+ liblaf/cherries/utils/__init__.py,sha256=OHb6Xou2v6u42swTgjRfzej4CIlRg4OmgOIQXUiRjKA,97
30
+ liblaf/cherries/utils/__init__.pyi,sha256=F5aTcXpWVmUoctPbLfmQXKyuXYRspAIjaIzfL1_3Lrw,51
31
+ liblaf/cherries/utils/_functools.py,sha256=0Puwvj1Wq4kp3S--hI-CXwUBZ56AtfkqIzFHllQtuug,181
32
+ liblaf_cherries-0.0.11.dist-info/METADATA,sha256=Fno2MjAvm3-xeK5BLh8S5hicRp3mtX_YTDG337ykay4,1944
33
+ liblaf_cherries-0.0.11.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
34
+ liblaf_cherries-0.0.11.dist-info/licenses/LICENSE,sha256=Ph4NzyU3lGVDeYv-mf8aRmImH8v9rVL9F362FV4G6Ow,1063
35
+ liblaf_cherries-0.0.11.dist-info/RECORD,,
liblaf/cherries/_env.py DELETED
@@ -1,4 +0,0 @@
1
- from environs import Env
2
-
3
- ENV_PREFIX: str = "LIBLAF_CHERRIES_"
4
- env = Env(prefix=ENV_PREFIX)
@@ -1,111 +0,0 @@
1
- import atexit
2
- import dataclasses
3
- import datetime
4
- import os
5
- from collections.abc import Sequence
6
- from pathlib import Path
7
- from typing import Any
8
-
9
- from environs import env
10
- from loguru import logger
11
-
12
- from liblaf import cherries
13
-
14
-
15
- @dataclasses.dataclass(kw_only=True)
16
- class Experiment:
17
- backend: cherries.Backend = dataclasses.field(
18
- default_factory=cherries.backend_factory
19
- )
20
- enabled: bool = dataclasses.field(
21
- default_factory=lambda: env.bool("LIBLAF_CHERRIES_ENABLED", True)
22
- )
23
- plugins: Sequence[cherries.Plugin] = dataclasses.field(
24
- default_factory=cherries.default_plugins
25
- )
26
-
27
- @property
28
- def entrypoint(self) -> Path:
29
- return self.backend.entrypoint
30
-
31
- @property
32
- def id(self) -> str:
33
- return self.backend.id
34
-
35
- @property
36
- def name(self) -> str:
37
- return self.backend.name
38
-
39
- @property
40
- def start_time(self) -> datetime.datetime:
41
- return self.backend.start_time
42
-
43
- @property
44
- def url(self) -> str:
45
- return self.backend.url
46
-
47
- def start(self) -> None:
48
- if not self.enabled:
49
- return
50
- self.plugins = sorted(self.plugins, key=lambda plugin: plugin.priority)
51
- for plugin in self.plugins:
52
- plugin.pre_start()
53
- self.backend.start()
54
- cherries.set_current_experiment(self)
55
- for plugin in self.plugins:
56
- plugin.post_start(self)
57
- self.log_other("cherries/entrypoint", self.entrypoint)
58
- self.log_other("cherries/start_time", self.start_time)
59
- atexit.register(self.end)
60
-
61
- def end(self) -> None:
62
- if not self.enabled:
63
- return
64
- for plugin in reversed(self.plugins):
65
- plugin.pre_end(self)
66
- self.backend.end()
67
- for plugin in reversed(self.plugins):
68
- plugin.post_end(self)
69
- self.enabled = False # prevent `end()` from being called multiple times
70
-
71
- def log_metric(
72
- self,
73
- key: str,
74
- value: float,
75
- *,
76
- step: float | None = None,
77
- timestamp: float | None = None,
78
- **kwargs,
79
- ) -> None:
80
- if not self.enabled:
81
- return
82
- logger.opt(depth=1).debug("{}: {}", key, value)
83
- self.backend.log_metric(key, value, step=step, timestamp=timestamp, **kwargs)
84
-
85
- def log_other(self, key: str, value: Any, **kwargs) -> None:
86
- if not self.enabled:
87
- return
88
- logger.opt(depth=1).info("{}: {}", key, value)
89
- self.backend.log_other(key, value, **kwargs)
90
-
91
- def upload_file(self, key: str, path: str | os.PathLike[str], **kwargs) -> None:
92
- if not self.enabled:
93
- return
94
- path = Path(path)
95
- logger.opt(depth=1).info("Uploading file: {}", path)
96
- self.backend.upload_file(key, path, **kwargs)
97
-
98
-
99
- _current_experiment: Experiment | None = None
100
-
101
-
102
- def current_experiment() -> Experiment:
103
- global _current_experiment # noqa: PLW0603
104
- if _current_experiment is None:
105
- _current_experiment = Experiment()
106
- return _current_experiment
107
-
108
-
109
- def set_current_experiment(experiment: Experiment) -> None:
110
- global _current_experiment # noqa: PLW0603
111
- _current_experiment = experiment
liblaf/cherries/_main.py DELETED
@@ -1,38 +0,0 @@
1
- import functools
2
- import inspect
3
- from collections.abc import Callable, Sequence
4
- from typing import ParamSpec, TypeVar
5
-
6
- from liblaf import cherries
7
-
8
- _P = ParamSpec("_P")
9
- _T = TypeVar("_T")
10
-
11
-
12
- def main(
13
- *,
14
- backend: cherries.Backend | None = None,
15
- enabled: bool | None = None,
16
- plugins: Sequence[cherries.Plugin] | None = None,
17
- ) -> Callable[[Callable[_P, _T]], Callable[_P, _T]]:
18
- def wrapper(fn: Callable[_P, _T]) -> Callable[_P, _T]:
19
- @functools.wraps(fn)
20
- def wrapped(*args: _P.args, **kwargs: _P.kwargs) -> _T:
21
- exp: cherries.Experiment = cherries.start(
22
- backend=backend, enabled=enabled, plugins=plugins
23
- )
24
- sig: inspect.Signature = inspect.signature(fn)
25
- bound_args: inspect.BoundArguments = sig.bind(*args, **kwargs)
26
- if len(bound_args.arguments) == 1:
27
- exp.log_other(
28
- "cherries/config", next(iter(bound_args.arguments.values()))
29
- )
30
- elif len(bound_args.arguments) > 1:
31
- exp.log_other("cherries/args", bound_args.arguments)
32
- ret: _T = fn(*args, **kwargs)
33
- exp.end()
34
- return ret
35
-
36
- return wrapped
37
-
38
- return wrapper
liblaf/cherries/_start.py DELETED
@@ -1,26 +0,0 @@
1
- from collections.abc import Sequence
2
-
3
- from environs import Env
4
-
5
- from liblaf import cherries
6
-
7
-
8
- def start(
9
- *,
10
- backend: cherries.Backend | None = None,
11
- enabled: bool | None = None,
12
- plugins: Sequence[cherries.Plugin] | None = None,
13
- ) -> cherries.Experiment:
14
- backend = backend or cherries.backend_factory()
15
- if enabled is None:
16
- enabled = Env().bool("LIBLAF_CHERRIES_ENABLED", True)
17
- if plugins is None:
18
- plugins = cherries.default_plugins()
19
- exp = cherries.Experiment(backend=backend, enabled=enabled, plugins=plugins)
20
- exp.start()
21
- return exp
22
-
23
-
24
- def end() -> None:
25
- exp: cherries.Experiment = cherries.current_experiment()
26
- exp.end()
@@ -1,18 +0,0 @@
1
- from . import github
2
- from ._commit import DEFAULT_COMMIT_MESSAGE, commit
3
- from ._entrypoint import entrypoint
4
- from ._grapes import GitInfo, info, root, root_safe
5
- from .github import permalink, user_repo
6
-
7
- __all__ = [
8
- "DEFAULT_COMMIT_MESSAGE",
9
- "GitInfo",
10
- "commit",
11
- "entrypoint",
12
- "github",
13
- "info",
14
- "permalink",
15
- "root",
16
- "root_safe",
17
- "user_repo",
18
- ]