liblaf-cherries 0.0.9__tar.gz → 0.0.10__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.
- {liblaf_cherries-0.0.9 → liblaf_cherries-0.0.10}/.gitignore +6 -1
- {liblaf_cherries-0.0.9 → liblaf_cherries-0.0.10}/PKG-INFO +7 -5
- {liblaf_cherries-0.0.9 → liblaf_cherries-0.0.10}/pyproject.toml +6 -4
- liblaf_cherries-0.0.10/src/liblaf/cherries/__init__.pyi +87 -0
- liblaf_cherries-0.0.10/src/liblaf/cherries/_run.py +31 -0
- {liblaf_cherries-0.0.9 → liblaf_cherries-0.0.10}/src/liblaf/cherries/_version.py +2 -2
- liblaf_cherries-0.0.10/src/liblaf/cherries/info/__init__.pyi +11 -0
- liblaf_cherries-0.0.10/src/liblaf/cherries/info/_exp_name.py +19 -0
- liblaf_cherries-0.0.10/src/liblaf/cherries/info/_git.py +49 -0
- liblaf_cherries-0.0.10/src/liblaf/cherries/pathutils/__init__.pyi +20 -0
- liblaf_cherries-0.0.10/src/liblaf/cherries/pathutils/_convert.py +40 -0
- liblaf_cherries-0.0.10/src/liblaf/cherries/pathutils/_path.py +68 -0
- liblaf_cherries-0.0.10/src/liblaf/cherries/pathutils/_special.py +50 -0
- liblaf_cherries-0.0.10/src/liblaf/cherries/plugin/__init__.pyi +73 -0
- liblaf_cherries-0.0.10/src/liblaf/cherries/plugin/_abc.py +122 -0
- liblaf_cherries-0.0.10/src/liblaf/cherries/plugin/_dvc.py +47 -0
- liblaf_cherries-0.0.10/src/liblaf/cherries/plugin/_git.py +46 -0
- liblaf_cherries-0.0.10/src/liblaf/cherries/plugin/_logging.py +31 -0
- liblaf_cherries-0.0.10/src/liblaf/cherries/plugin/_mlflow.py +73 -0
- liblaf_cherries-0.0.10/src/liblaf/cherries/plugin/_run.py +104 -0
- liblaf_cherries-0.0.10/src/liblaf/cherries/presets/__init__.pyi +3 -0
- liblaf_cherries-0.0.10/src/liblaf/cherries/presets/_default.py +32 -0
- liblaf_cherries-0.0.10/src/liblaf/cherries/typed.py +3 -0
- liblaf_cherries-0.0.10/src/liblaf/cherries/utils/__init__.pyi +3 -0
- liblaf_cherries-0.0.10/src/liblaf/cherries/utils/_functools.py +6 -0
- liblaf_cherries-0.0.9/src/liblaf/cherries/__init__.pyi +0 -36
- liblaf_cherries-0.0.9/src/liblaf/cherries/_env.py +0 -4
- liblaf_cherries-0.0.9/src/liblaf/cherries/_experiment.py +0 -111
- liblaf_cherries-0.0.9/src/liblaf/cherries/_main.py +0 -38
- liblaf_cherries-0.0.9/src/liblaf/cherries/_run.py +0 -26
- liblaf_cherries-0.0.9/src/liblaf/cherries/_start.py +0 -26
- liblaf_cherries-0.0.9/src/liblaf/cherries/git/__init__.pyi +0 -18
- liblaf_cherries-0.0.9/src/liblaf/cherries/git/_commit.py +0 -17
- liblaf_cherries-0.0.9/src/liblaf/cherries/git/_entrypoint.py +0 -12
- liblaf_cherries-0.0.9/src/liblaf/cherries/git/_grapes.py +0 -3
- liblaf_cherries-0.0.9/src/liblaf/cherries/git/github/__init__.pyi +0 -4
- liblaf_cherries-0.0.9/src/liblaf/cherries/git/github/_link.py +0 -25
- liblaf_cherries-0.0.9/src/liblaf/cherries/git/github/_repo.py +0 -23
- liblaf_cherries-0.0.9/src/liblaf/cherries/integration/__init__.pyi +0 -5
- liblaf_cherries-0.0.9/src/liblaf/cherries/integration/_backend.py +0 -53
- liblaf_cherries-0.0.9/src/liblaf/cherries/integration/_factory.py +0 -13
- liblaf_cherries-0.0.9/src/liblaf/cherries/integration/_neptune.py +0 -66
- liblaf_cherries-0.0.9/src/liblaf/cherries/plugin/__init__.pyi +0 -7
- liblaf_cherries-0.0.9/src/liblaf/cherries/plugin/_abc.py +0 -31
- liblaf_cherries-0.0.9/src/liblaf/cherries/plugin/_default.py +0 -9
- liblaf_cherries-0.0.9/src/liblaf/cherries/plugin/_git.py +0 -23
- liblaf_cherries-0.0.9/src/liblaf/cherries/plugin/_logging.py +0 -39
- liblaf_cherries-0.0.9/src/liblaf/cherries/plugin/_restic.py +0 -60
- liblaf_cherries-0.0.9/src/liblaf/cherries/utils/__init__.pyi +0 -0
- {liblaf_cherries-0.0.9 → liblaf_cherries-0.0.10}/LICENSE +0 -0
- {liblaf_cherries-0.0.9 → liblaf_cherries-0.0.10}/docs/README.md +0 -0
- {liblaf_cherries-0.0.9 → liblaf_cherries-0.0.10}/src/liblaf/cherries/__init__.py +0 -0
- {liblaf_cherries-0.0.9 → liblaf_cherries-0.0.10}/src/liblaf/cherries/_config.py +0 -0
- {liblaf_cherries-0.0.9 → liblaf_cherries-0.0.10}/src/liblaf/cherries/_version.pyi +0 -0
- {liblaf_cherries-0.0.9/src/liblaf/cherries/git → liblaf_cherries-0.0.10/src/liblaf/cherries/info}/__init__.py +0 -0
- {liblaf_cherries-0.0.9/src/liblaf/cherries/git/github → liblaf_cherries-0.0.10/src/liblaf/cherries/pathutils}/__init__.py +0 -0
- {liblaf_cherries-0.0.9/src/liblaf/cherries/integration → liblaf_cherries-0.0.10/src/liblaf/cherries/plugin}/__init__.py +0 -0
- {liblaf_cherries-0.0.9/src/liblaf/cherries/plugin → liblaf_cherries-0.0.10/src/liblaf/cherries/presets}/__init__.py +0 -0
- {liblaf_cherries-0.0.9 → liblaf_cherries-0.0.10}/src/liblaf/cherries/py.typed +0 -0
- {liblaf_cherries-0.0.9 → liblaf_cherries-0.0.10}/src/liblaf/cherries/utils/__init__.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: liblaf-cherries
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.10
|
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.
|
38
|
+
Requires-Dist: liblaf-grapes<0.2,>=0.1.21
|
37
39
|
Requires-Dist: loguru<0.8,>=0.7.3
|
38
|
-
Requires-Dist:
|
39
|
-
Requires-Dist: pydantic-settings<3,>=2.
|
40
|
-
Requires-Dist: pydantic<3,>=2.11.
|
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
|
|
@@ -60,13 +60,15 @@ classifiers = [
|
|
60
60
|
"Typing :: Typed",
|
61
61
|
]
|
62
62
|
dependencies = [
|
63
|
+
"dvc[webdav]>=3.59.2,<4",
|
63
64
|
"environs>=14.1.1,<15",
|
65
|
+
"gitpython>=3.1.44,<4",
|
64
66
|
"lazy-loader>=0.4,<0.5",
|
65
|
-
"liblaf-grapes>=0.1.
|
67
|
+
"liblaf-grapes>=0.1.21,<0.2",
|
66
68
|
"loguru>=0.7.3,<0.8",
|
67
|
-
"
|
68
|
-
"pydantic-settings>=2.
|
69
|
-
"pydantic>=2.11.
|
69
|
+
"mlflow>=2.22.0,<3",
|
70
|
+
"pydantic-settings>=2.9.1,<3",
|
71
|
+
"pydantic>=2.11.4,<3",
|
70
72
|
"rich>=14.0.0,<15",
|
71
73
|
]
|
72
74
|
description = "Add your description here"
|
@@ -0,0 +1,87 @@
|
|
1
|
+
from . import info, pathutils, plugin, presets, utils
|
2
|
+
from ._config import BaseConfig
|
3
|
+
from ._run import run
|
4
|
+
from .pathutils import (
|
5
|
+
as_os_path,
|
6
|
+
as_path,
|
7
|
+
as_posix,
|
8
|
+
config,
|
9
|
+
data,
|
10
|
+
entrypoint,
|
11
|
+
exp_dir,
|
12
|
+
git_root,
|
13
|
+
git_root_safe,
|
14
|
+
inputs,
|
15
|
+
outputs,
|
16
|
+
params,
|
17
|
+
path,
|
18
|
+
src,
|
19
|
+
)
|
20
|
+
from .plugin import (
|
21
|
+
End,
|
22
|
+
LogArtifact,
|
23
|
+
LogArtifacts,
|
24
|
+
LoggingEnd,
|
25
|
+
LoggingStart,
|
26
|
+
LogMetric,
|
27
|
+
LogParam,
|
28
|
+
Plugin,
|
29
|
+
Run,
|
30
|
+
Start,
|
31
|
+
end,
|
32
|
+
log_artifact,
|
33
|
+
log_artifacts,
|
34
|
+
log_input,
|
35
|
+
log_inputs,
|
36
|
+
log_metric,
|
37
|
+
log_output,
|
38
|
+
log_outputs,
|
39
|
+
log_param,
|
40
|
+
set_tag,
|
41
|
+
start,
|
42
|
+
)
|
43
|
+
|
44
|
+
__all__ = [
|
45
|
+
"BaseConfig",
|
46
|
+
"End",
|
47
|
+
"LogArtifact",
|
48
|
+
"LogArtifacts",
|
49
|
+
"LogMetric",
|
50
|
+
"LogParam",
|
51
|
+
"LoggingEnd",
|
52
|
+
"LoggingStart",
|
53
|
+
"Plugin",
|
54
|
+
"Run",
|
55
|
+
"Start",
|
56
|
+
"as_os_path",
|
57
|
+
"as_path",
|
58
|
+
"as_posix",
|
59
|
+
"config",
|
60
|
+
"data",
|
61
|
+
"end",
|
62
|
+
"entrypoint",
|
63
|
+
"exp_dir",
|
64
|
+
"git_root",
|
65
|
+
"git_root_safe",
|
66
|
+
"info",
|
67
|
+
"inputs",
|
68
|
+
"log_artifact",
|
69
|
+
"log_artifacts",
|
70
|
+
"log_input",
|
71
|
+
"log_inputs",
|
72
|
+
"log_metric",
|
73
|
+
"log_output",
|
74
|
+
"log_outputs",
|
75
|
+
"log_param",
|
76
|
+
"outputs",
|
77
|
+
"params",
|
78
|
+
"path",
|
79
|
+
"pathutils",
|
80
|
+
"plugin",
|
81
|
+
"presets",
|
82
|
+
"run",
|
83
|
+
"set_tag",
|
84
|
+
"src",
|
85
|
+
"start",
|
86
|
+
"utils",
|
87
|
+
]
|
@@ -0,0 +1,31 @@
|
|
1
|
+
from collections.abc import Callable
|
2
|
+
from typing import get_type_hints
|
3
|
+
|
4
|
+
import pydantic
|
5
|
+
|
6
|
+
from liblaf.cherries import pathutils as _path
|
7
|
+
from liblaf.cherries import plugin, presets
|
8
|
+
|
9
|
+
|
10
|
+
def run[C: pydantic.BaseModel, T](main: Callable[[C], T]) -> T:
|
11
|
+
run: plugin.Run = start()
|
12
|
+
type_hints: dict[str, type[C]] = get_type_hints(main)
|
13
|
+
cls: type[C] = next(iter(type_hints.values()))
|
14
|
+
cfg: C = cls()
|
15
|
+
run.log_param("cherries.config", cfg.model_dump(mode="json"))
|
16
|
+
ret: T = main(cfg)
|
17
|
+
run.end()
|
18
|
+
return ret
|
19
|
+
|
20
|
+
|
21
|
+
def start() -> plugin.Run:
|
22
|
+
run: plugin.Run = presets.default()
|
23
|
+
run.start()
|
24
|
+
run.log_src(_path.entrypoint(absolute=True))
|
25
|
+
run.set_tag("cherries.entrypoint", _path.entrypoint(absolute=False))
|
26
|
+
run.set_tag("cherries.exp-dir", _path.exp_dir(absolute=False))
|
27
|
+
return plugin.run
|
28
|
+
|
29
|
+
|
30
|
+
def end() -> None:
|
31
|
+
plugin.run.end()
|
@@ -0,0 +1,19 @@
|
|
1
|
+
import git.exc
|
2
|
+
from environs import env
|
3
|
+
|
4
|
+
from liblaf import grapes
|
5
|
+
|
6
|
+
from ._git import git_info
|
7
|
+
|
8
|
+
|
9
|
+
def exp_name() -> str:
|
10
|
+
if name := env.str("LIBLAF_CHERRIES_EXPERIMENT_NAME", "").strip():
|
11
|
+
return name
|
12
|
+
if name := env.str("MLFLOW_EXPERIMENT_NAME", "").strip():
|
13
|
+
return name
|
14
|
+
try:
|
15
|
+
info: grapes.git.GitInfo = git_info()
|
16
|
+
except git.exc.InvalidGitRepositoryError:
|
17
|
+
return "Default"
|
18
|
+
else:
|
19
|
+
return info.repo
|
@@ -0,0 +1,49 @@
|
|
1
|
+
import subprocess as sp
|
2
|
+
|
3
|
+
import git
|
4
|
+
|
5
|
+
from liblaf import grapes
|
6
|
+
from liblaf.cherries import pathutils as _path
|
7
|
+
|
8
|
+
|
9
|
+
def git_auto_commit(
|
10
|
+
message: str = "chore(cherries): auto commit", *, dry_run: bool = False
|
11
|
+
) -> None:
|
12
|
+
repo: git.Repo = _repo()
|
13
|
+
if not repo.is_dirty(untracked_files=True):
|
14
|
+
return
|
15
|
+
repo.git.add(all=True, dry_run=dry_run)
|
16
|
+
sp.run(["git", "status"], check=False)
|
17
|
+
if dry_run:
|
18
|
+
return
|
19
|
+
repo.git.commit(message=message)
|
20
|
+
|
21
|
+
|
22
|
+
def git_branch() -> str:
|
23
|
+
repo: git.Repo = _repo()
|
24
|
+
return repo.active_branch.name
|
25
|
+
|
26
|
+
|
27
|
+
def git_commit_sha() -> str:
|
28
|
+
repo: git.Repo = _repo()
|
29
|
+
return repo.head.commit.hexsha
|
30
|
+
|
31
|
+
|
32
|
+
def git_commit_url(sha: str | None = None) -> str:
|
33
|
+
if sha is None:
|
34
|
+
sha = git_commit_sha()
|
35
|
+
info: grapes.git.GitInfo = git_info()
|
36
|
+
if info.platform == "github":
|
37
|
+
return f"https://github.com/{info.owner}/{info.repo}/commit/{sha}"
|
38
|
+
raise NotImplementedError
|
39
|
+
|
40
|
+
|
41
|
+
def git_info() -> grapes.git.GitInfo:
|
42
|
+
info: grapes.git.GitInfo = grapes.git.info(
|
43
|
+
_path.entrypoint(absolute=True), search_parent_directories=True
|
44
|
+
)
|
45
|
+
return info
|
46
|
+
|
47
|
+
|
48
|
+
def _repo() -> git.Repo:
|
49
|
+
return git.Repo(_path.entrypoint(absolute=True), search_parent_directories=True)
|
@@ -0,0 +1,20 @@
|
|
1
|
+
from ._convert import as_os_path, as_path, as_posix
|
2
|
+
from ._path import entrypoint, exp_dir, git_root, git_root_safe
|
3
|
+
from ._special import config, data, inputs, outputs, params, path, src
|
4
|
+
|
5
|
+
__all__ = [
|
6
|
+
"as_os_path",
|
7
|
+
"as_path",
|
8
|
+
"as_posix",
|
9
|
+
"config",
|
10
|
+
"data",
|
11
|
+
"entrypoint",
|
12
|
+
"exp_dir",
|
13
|
+
"git_root",
|
14
|
+
"git_root_safe",
|
15
|
+
"inputs",
|
16
|
+
"outputs",
|
17
|
+
"params",
|
18
|
+
"path",
|
19
|
+
"src",
|
20
|
+
]
|
@@ -0,0 +1,40 @@
|
|
1
|
+
from pathlib import Path
|
2
|
+
from typing import overload
|
3
|
+
|
4
|
+
from liblaf.cherries.typed import PathLike
|
5
|
+
|
6
|
+
|
7
|
+
@overload
|
8
|
+
def as_os_path(path: PathLike) -> str: ...
|
9
|
+
@overload
|
10
|
+
def as_os_path(path: None) -> None: ...
|
11
|
+
def as_os_path(path: PathLike | None) -> str | None:
|
12
|
+
if path is None:
|
13
|
+
return None
|
14
|
+
if isinstance(path, str):
|
15
|
+
return path
|
16
|
+
return str(path)
|
17
|
+
|
18
|
+
|
19
|
+
@overload
|
20
|
+
def as_path(path: PathLike) -> Path: ...
|
21
|
+
@overload
|
22
|
+
def as_path(path: None) -> None: ...
|
23
|
+
|
24
|
+
|
25
|
+
def as_path(path: PathLike | None) -> Path | None:
|
26
|
+
if path is None:
|
27
|
+
return None
|
28
|
+
return Path(path)
|
29
|
+
|
30
|
+
|
31
|
+
@overload
|
32
|
+
def as_posix(path: PathLike) -> str: ...
|
33
|
+
@overload
|
34
|
+
def as_posix(path: None) -> None: ...
|
35
|
+
def as_posix(path: PathLike | None) -> str | None:
|
36
|
+
if path is None:
|
37
|
+
return None
|
38
|
+
if isinstance(path, str):
|
39
|
+
return path
|
40
|
+
return Path(path).as_posix()
|
@@ -0,0 +1,68 @@
|
|
1
|
+
import sys
|
2
|
+
from pathlib import Path
|
3
|
+
|
4
|
+
import git
|
5
|
+
import git.exc
|
6
|
+
from loguru import logger
|
7
|
+
|
8
|
+
from liblaf.cherries import utils
|
9
|
+
|
10
|
+
|
11
|
+
@utils.cache
|
12
|
+
def entrypoint(*, absolute: bool = False) -> Path:
|
13
|
+
if absolute:
|
14
|
+
return _entrypoint_absolute()
|
15
|
+
return _entrypoint_relative()
|
16
|
+
|
17
|
+
|
18
|
+
@utils.cache
|
19
|
+
def exp_dir(*, absolute: bool = False) -> Path:
|
20
|
+
if absolute:
|
21
|
+
return _exp_dir_absolute()
|
22
|
+
return _exp_dir_relative()
|
23
|
+
|
24
|
+
|
25
|
+
@utils.cache
|
26
|
+
def git_root() -> Path:
|
27
|
+
entrypoint: Path = _entrypoint_absolute()
|
28
|
+
repo = git.Repo(entrypoint, search_parent_directories=True)
|
29
|
+
return Path(repo.working_dir)
|
30
|
+
|
31
|
+
|
32
|
+
@utils.cache
|
33
|
+
def git_root_safe() -> Path:
|
34
|
+
try:
|
35
|
+
return git_root()
|
36
|
+
except git.exc.InvalidGitRepositoryError:
|
37
|
+
logger.warning("Not in a git repository, using current directory")
|
38
|
+
return _entrypoint_absolute().parent
|
39
|
+
|
40
|
+
|
41
|
+
@utils.cache
|
42
|
+
def _entrypoint_absolute() -> Path:
|
43
|
+
path = Path(sys.argv[0])
|
44
|
+
return path.absolute()
|
45
|
+
|
46
|
+
|
47
|
+
@utils.cache
|
48
|
+
def _entrypoint_relative() -> Path:
|
49
|
+
path: Path = _entrypoint_absolute()
|
50
|
+
return path.relative_to(git_root_safe())
|
51
|
+
|
52
|
+
|
53
|
+
@utils.cache
|
54
|
+
def _exp_dir_absolute() -> Path:
|
55
|
+
entrypoint: Path = _entrypoint_absolute()
|
56
|
+
for path in entrypoint.parents:
|
57
|
+
if (path / "exp.cherries.toml").is_file():
|
58
|
+
return path
|
59
|
+
if (path / "src").is_dir():
|
60
|
+
return path
|
61
|
+
return git_root_safe()
|
62
|
+
|
63
|
+
|
64
|
+
@utils.cache
|
65
|
+
def _exp_dir_relative() -> Path:
|
66
|
+
absolute: Path = _exp_dir_absolute()
|
67
|
+
root: Path = git_root_safe()
|
68
|
+
return absolute.relative_to(root)
|
@@ -0,0 +1,50 @@
|
|
1
|
+
from pathlib import Path
|
2
|
+
|
3
|
+
from liblaf.grapes.typed import PathLike
|
4
|
+
|
5
|
+
from ._path import exp_dir
|
6
|
+
|
7
|
+
|
8
|
+
def config(
|
9
|
+
path: PathLike = "", *, mkdir: bool = True, prefix: PathLike = "config"
|
10
|
+
) -> Path:
|
11
|
+
return _path(path, mkdir=mkdir, prefix=prefix)
|
12
|
+
|
13
|
+
|
14
|
+
def data(path: PathLike = "", *, mkdir: bool = True, prefix: PathLike = "data") -> Path:
|
15
|
+
return _path(path, mkdir=mkdir, prefix=prefix)
|
16
|
+
|
17
|
+
|
18
|
+
def inputs(
|
19
|
+
path: PathLike = "", *, mkdir: bool = True, prefix: PathLike = "data/inputs"
|
20
|
+
) -> Path:
|
21
|
+
return _path(path, mkdir=mkdir, prefix=prefix)
|
22
|
+
|
23
|
+
|
24
|
+
def outputs(
|
25
|
+
path: PathLike = "", *, mkdir: bool = True, prefix: PathLike = "data/outputs"
|
26
|
+
) -> Path:
|
27
|
+
return _path(path, mkdir=mkdir, prefix=prefix)
|
28
|
+
|
29
|
+
|
30
|
+
def params(
|
31
|
+
path: PathLike = "", *, mkdir: bool = True, prefix: PathLike = "params"
|
32
|
+
) -> Path:
|
33
|
+
return _path(path, mkdir=mkdir, prefix=prefix)
|
34
|
+
|
35
|
+
|
36
|
+
def path(path: PathLike = "", *, mkdir: bool = True, prefix: PathLike = "") -> Path:
|
37
|
+
return _path(path, mkdir=mkdir, prefix=prefix)
|
38
|
+
|
39
|
+
|
40
|
+
def src(path: PathLike = "", *, mkdir: bool = True, prefix: PathLike = "src") -> Path:
|
41
|
+
return _path(path, mkdir=mkdir, prefix=prefix)
|
42
|
+
|
43
|
+
|
44
|
+
def _path(path: PathLike = "", *, mkdir: bool = True, prefix: PathLike = "") -> Path:
|
45
|
+
path = Path(path)
|
46
|
+
if not path.is_absolute():
|
47
|
+
path = exp_dir(absolute=True) / prefix / path
|
48
|
+
if mkdir:
|
49
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
50
|
+
return path
|
@@ -0,0 +1,73 @@
|
|
1
|
+
from ._abc import (
|
2
|
+
End,
|
3
|
+
LogArtifact,
|
4
|
+
LogArtifacts,
|
5
|
+
LogMetric,
|
6
|
+
LogParam,
|
7
|
+
Plugin,
|
8
|
+
Start,
|
9
|
+
)
|
10
|
+
from ._dvc import DvcEnd, DvcLogArtifact, DvcLogArtifacts
|
11
|
+
from ._git import GitEnd, GitStart
|
12
|
+
from ._logging import LoggingEnd, LoggingStart
|
13
|
+
from ._mlflow import (
|
14
|
+
MlflowEnd,
|
15
|
+
MlflowLogArtifact,
|
16
|
+
MlflowLogArtifacts,
|
17
|
+
MlflowLogMetric,
|
18
|
+
MlflowLogParam,
|
19
|
+
MlflowSetTag,
|
20
|
+
MlflowStart,
|
21
|
+
)
|
22
|
+
from ._run import (
|
23
|
+
Run,
|
24
|
+
end,
|
25
|
+
log_artifact,
|
26
|
+
log_artifacts,
|
27
|
+
log_input,
|
28
|
+
log_inputs,
|
29
|
+
log_metric,
|
30
|
+
log_output,
|
31
|
+
log_outputs,
|
32
|
+
log_param,
|
33
|
+
run,
|
34
|
+
set_tag,
|
35
|
+
start,
|
36
|
+
)
|
37
|
+
|
38
|
+
__all__ = [
|
39
|
+
"DvcEnd",
|
40
|
+
"DvcLogArtifact",
|
41
|
+
"DvcLogArtifacts",
|
42
|
+
"End",
|
43
|
+
"GitEnd",
|
44
|
+
"GitStart",
|
45
|
+
"LogArtifact",
|
46
|
+
"LogArtifacts",
|
47
|
+
"LogMetric",
|
48
|
+
"LogParam",
|
49
|
+
"LoggingEnd",
|
50
|
+
"LoggingStart",
|
51
|
+
"MlflowEnd",
|
52
|
+
"MlflowLogArtifact",
|
53
|
+
"MlflowLogArtifacts",
|
54
|
+
"MlflowLogMetric",
|
55
|
+
"MlflowLogParam",
|
56
|
+
"MlflowSetTag",
|
57
|
+
"MlflowStart",
|
58
|
+
"Plugin",
|
59
|
+
"Run",
|
60
|
+
"Start",
|
61
|
+
"end",
|
62
|
+
"log_artifact",
|
63
|
+
"log_artifacts",
|
64
|
+
"log_input",
|
65
|
+
"log_inputs",
|
66
|
+
"log_metric",
|
67
|
+
"log_output",
|
68
|
+
"log_outputs",
|
69
|
+
"log_param",
|
70
|
+
"run",
|
71
|
+
"set_tag",
|
72
|
+
"start",
|
73
|
+
]
|
@@ -0,0 +1,122 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import bisect
|
4
|
+
import functools
|
5
|
+
import operator
|
6
|
+
from collections.abc import Iterable
|
7
|
+
from pathlib import Path
|
8
|
+
from typing import Any, override
|
9
|
+
|
10
|
+
import attrs
|
11
|
+
from loguru import logger
|
12
|
+
|
13
|
+
from liblaf.cherries import pathutils as _path
|
14
|
+
from liblaf.cherries.typed import PathLike
|
15
|
+
|
16
|
+
|
17
|
+
@functools.total_ordering
|
18
|
+
@attrs.define
|
19
|
+
class Plugin[**P, T]:
|
20
|
+
priority: int = attrs.field(default=0, kw_only=True, eq=True, order=True)
|
21
|
+
_children: list[Plugin] = attrs.field(
|
22
|
+
factory=list, eq=False, order=False, alias="children"
|
23
|
+
)
|
24
|
+
|
25
|
+
def __attrs_post_init__(self) -> None:
|
26
|
+
self._children.sort(key=operator.attrgetter("priority"))
|
27
|
+
|
28
|
+
def __call__(self, *args: P.args, **kwargs: P.kwargs) -> T:
|
29
|
+
ret: T | None = None
|
30
|
+
for child in self._children:
|
31
|
+
try:
|
32
|
+
ret = child(*args, **kwargs)
|
33
|
+
except BaseException as err:
|
34
|
+
if isinstance(err, (KeyboardInterrupt, SystemExit)):
|
35
|
+
raise
|
36
|
+
logger.exception(child)
|
37
|
+
return ret # pyright: ignore[reportReturnType]
|
38
|
+
|
39
|
+
def __lt__(self, other: Plugin) -> bool:
|
40
|
+
if not isinstance(other, Plugin):
|
41
|
+
return NotImplemented
|
42
|
+
return self.priority < other.priority
|
43
|
+
|
44
|
+
def __eq__(self, other: Plugin) -> bool: # pyright: ignore[reportIncompatibleMethodOverride]
|
45
|
+
if not isinstance(other, Plugin):
|
46
|
+
return NotImplemented
|
47
|
+
return self.priority == other.priority
|
48
|
+
|
49
|
+
@property
|
50
|
+
def children(self) -> list[Plugin]:
|
51
|
+
return self._children
|
52
|
+
|
53
|
+
def add(self, *child: Plugin) -> None:
|
54
|
+
for c in child:
|
55
|
+
bisect.insort(self._children, c, key=operator.attrgetter("priority"))
|
56
|
+
|
57
|
+
def extend(self, children: Iterable[Plugin]) -> None:
|
58
|
+
self.add(*children)
|
59
|
+
|
60
|
+
def remove(self, child: Plugin) -> None:
|
61
|
+
self._children.remove(child)
|
62
|
+
|
63
|
+
|
64
|
+
@attrs.define
|
65
|
+
class End(Plugin):
|
66
|
+
@override
|
67
|
+
def __call__(self) -> None:
|
68
|
+
return super().__call__()
|
69
|
+
|
70
|
+
|
71
|
+
@attrs.define
|
72
|
+
class LogArtifact(Plugin):
|
73
|
+
@override
|
74
|
+
def __call__(
|
75
|
+
self, local_path: PathLike, artifact_path: PathLike | None = None, **kwargs
|
76
|
+
) -> Path:
|
77
|
+
ret: Path | None = super().__call__(local_path, artifact_path, **kwargs)
|
78
|
+
if ret is None:
|
79
|
+
ret = _path.as_path(local_path)
|
80
|
+
return ret
|
81
|
+
|
82
|
+
|
83
|
+
@attrs.define
|
84
|
+
class LogArtifacts(Plugin):
|
85
|
+
@override
|
86
|
+
def __call__(
|
87
|
+
self, local_dir: PathLike, artifact_path: PathLike | None = None, **kwargs
|
88
|
+
) -> Path:
|
89
|
+
ret: Path | None = super().__call__(local_dir, artifact_path, **kwargs)
|
90
|
+
if ret is None:
|
91
|
+
ret = _path.as_path(local_dir)
|
92
|
+
return ret
|
93
|
+
|
94
|
+
|
95
|
+
@attrs.define
|
96
|
+
class LogMetric(Plugin):
|
97
|
+
@override
|
98
|
+
def __call__(
|
99
|
+
self, key: str, value: float, step: int | None = None, **kwargs
|
100
|
+
) -> None:
|
101
|
+
return super().__call__(key, value, step, **kwargs)
|
102
|
+
|
103
|
+
|
104
|
+
@attrs.define
|
105
|
+
class LogParam(Plugin):
|
106
|
+
@override
|
107
|
+
def __call__(self, key: str, value: Any, **kwargs) -> None:
|
108
|
+
return super().__call__(key, value, **kwargs)
|
109
|
+
|
110
|
+
|
111
|
+
@attrs.define
|
112
|
+
class SetTag(Plugin):
|
113
|
+
@override
|
114
|
+
def __call__(self, key: str, value: Any, **kwargs) -> None:
|
115
|
+
return super().__call__(key, value, **kwargs)
|
116
|
+
|
117
|
+
|
118
|
+
@attrs.define
|
119
|
+
class Start(Plugin):
|
120
|
+
@override
|
121
|
+
def __call__(self) -> None:
|
122
|
+
return super().__call__()
|
@@ -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
|