liblaf-cherries 0.0.9__py3-none-any.whl → 0.0.10__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 +74 -23
  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 +72 -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 +104 -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.10.dist-info}/METADATA +7 -5
  24. liblaf_cherries-0.0.10.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.10.dist-info}/WHEEL +0 -0
  47. {liblaf_cherries-0.0.9.dist-info → liblaf_cherries-0.0.10.dist-info}/licenses/LICENSE +0 -0
@@ -1,36 +1,87 @@
1
- from . import git, integration, plugin, utils
1
+ from . import info, pathutils, plugin, presets, utils
2
2
  from ._config import BaseConfig
3
- from ._env import ENV_PREFIX, env
4
- from ._experiment import Experiment, current_experiment, set_current_experiment
5
- from ._main import main
6
3
  from ._run import run
7
- from ._start import end, start
8
- from .git import entrypoint
9
- from .integration import Backend, BackendNeptune, backend_factory
10
- from .plugin import Plugin, PluginGit, PluginLogging, PluginRestic, default_plugins
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
+ )
11
43
 
12
44
  __all__ = [
13
- "ENV_PREFIX",
14
- "Backend",
15
- "BackendNeptune",
16
45
  "BaseConfig",
17
- "Experiment",
46
+ "End",
47
+ "LogArtifact",
48
+ "LogArtifacts",
49
+ "LogMetric",
50
+ "LogParam",
51
+ "LoggingEnd",
52
+ "LoggingStart",
18
53
  "Plugin",
19
- "PluginGit",
20
- "PluginLogging",
21
- "PluginRestic",
22
- "backend_factory",
23
- "current_experiment",
24
- "default_plugins",
54
+ "Run",
55
+ "Start",
56
+ "as_os_path",
57
+ "as_path",
58
+ "as_posix",
59
+ "config",
60
+ "data",
25
61
  "end",
26
62
  "entrypoint",
27
- "env",
28
- "git",
29
- "integration",
30
- "main",
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",
31
80
  "plugin",
81
+ "presets",
32
82
  "run",
33
- "set_current_experiment",
83
+ "set_tag",
84
+ "src",
34
85
  "start",
35
86
  "utils",
36
87
  ]
liblaf/cherries/_run.py CHANGED
@@ -1,26 +1,31 @@
1
- from collections.abc import Sequence
2
- from typing import Protocol, get_type_hints
3
-
4
- from liblaf import cherries
5
-
6
-
7
- class MainFunction[T: cherries.BaseConfig](Protocol):
8
- def __call__(self, cfg: T) -> None: ...
9
-
10
-
11
- def run[T: cherries.BaseConfig](
12
- main: MainFunction[T],
13
- *,
14
- backend: cherries.Backend | None = None,
15
- enabled: bool | None = None,
16
- plugins: Sequence[cherries.Plugin] | None = None,
17
- ) -> None:
18
- exp: cherries.Experiment = cherries.start(
19
- backend=backend, enabled=enabled, plugins=plugins
20
- )
21
- type_hints: dict[str, type[T]] = get_type_hints(main)
22
- cls: type[T] = next(iter(type_hints.values()))
23
- cfg: T = cls()
24
- exp.log_other("cherries/config", cfg)
25
- main(cfg)
26
- exp.end()
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()
@@ -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.9'
21
- __version_tuple__ = version_tuple = (0, 0, 9)
20
+ __version__ = version = '0.0.10'
21
+ __version_tuple__ = version_tuple = (0, 0, 10)
@@ -0,0 +1,11 @@
1
+ from ._exp_name import exp_name
2
+ from ._git import git_auto_commit, git_branch, git_commit_sha, git_commit_url, git_info
3
+
4
+ __all__ = [
5
+ "exp_name",
6
+ "git_auto_commit",
7
+ "git_branch",
8
+ "git_commit_sha",
9
+ "git_commit_url",
10
+ "git_info",
11
+ ]
@@ -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
@@ -1,7 +1,73 @@
1
- from ._abc import Plugin
2
- from ._default import default_plugins
3
- from ._git import PluginGit
4
- from ._logging import PluginLogging
5
- from ._restic import PluginRestic
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
+ )
6
37
 
7
- __all__ = ["Plugin", "PluginGit", "PluginLogging", "PluginRestic", "default_plugins"]
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
+ ]
@@ -1,31 +1,122 @@
1
1
  from __future__ import annotations
2
2
 
3
- import pydantic_settings as ps
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
4
9
 
5
- from liblaf import cherries
10
+ import attrs
11
+ from loguru import logger
6
12
 
13
+ from liblaf.cherries import pathutils as _path
14
+ from liblaf.cherries.typed import PathLike
7
15
 
8
- class Plugin(ps.BaseSettings):
9
- enabled: bool = True
10
- priority: float = 0.0
11
16
 
12
- def pre_start(self) -> None:
13
- if self.enabled:
14
- self._pre_start()
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
+ )
15
24
 
16
- def post_start(self, run: cherries.Experiment) -> None:
17
- if self.enabled:
18
- self._post_start(run)
25
+ def __attrs_post_init__(self) -> None:
26
+ self._children.sort(key=operator.attrgetter("priority"))
19
27
 
20
- def pre_end(self, run: cherries.Experiment) -> None:
21
- if self.enabled:
22
- self._pre_end(run)
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]
23
38
 
24
- def post_end(self, run: cherries.Experiment) -> None:
25
- if self.enabled:
26
- self._post_end(run)
39
+ def __lt__(self, other: Plugin) -> bool:
40
+ if not isinstance(other, Plugin):
41
+ return NotImplemented
42
+ return self.priority < other.priority
27
43
 
28
- def _pre_start(self) -> None: ...
29
- def _post_start(self, run: cherries.Experiment) -> None: ...
30
- def _pre_end(self, run: cherries.Experiment) -> None: ...
31
- def _post_end(self, run: cherries.Experiment) -> None: ...
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__()