nnlogging 0.1.0__py3-none-any.whl → 0.1.1__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.

Potentially problematic release.


This version of nnlogging might be problematic. Click here for more details.

@@ -0,0 +1,198 @@
1
+ import logging
2
+
3
+ from rich.console import ConsoleRenderable as RichConsoleRenderable
4
+
5
+ from nnlogging.shell.protocol import ShellProtocol
6
+ from nnlogging.typings import ConsolePrintOptions, LogOptions, Omitable
7
+ from nnlogging.utils import LoggerConfig, evolve_, get_logging_logger
8
+
9
+
10
+ def _logger_open(
11
+ inst: ShellProtocol,
12
+ /,
13
+ ):
14
+ if inst.logger is None:
15
+ logging.captureWarnings(True)
16
+ inst.logger = get_logging_logger(inst.logger_config)
17
+ for name in inst.branches:
18
+ branch = inst.branches[name]
19
+ inst.logger.addHandler(branch["handler"])
20
+ # TODO: add `filter` support in v0.2.0
21
+
22
+
23
+ def _logger_close(
24
+ inst: ShellProtocol,
25
+ /,
26
+ ):
27
+ if inst.logger is not None:
28
+ logging.captureWarnings(False)
29
+ for handler in inst.logger.handlers[:]:
30
+ inst.logger.removeHandler(handler)
31
+ handler.close()
32
+ # TODO: add `filter` support in v0.2.0
33
+ # inst.logger.filters.clear()
34
+ inst.logger.setLevel(logging.NOTSET)
35
+ inst.logger.propagate = False
36
+ inst.logger = None
37
+
38
+
39
+ def logger_configure(
40
+ inst: ShellProtocol,
41
+ /,
42
+ config: LoggerConfig | None = None,
43
+ *,
44
+ name: Omitable[str] = ...,
45
+ level: Omitable[int | str] = ...,
46
+ propagate: Omitable[bool] = ...,
47
+ ):
48
+ _logger_close(inst)
49
+ inst.logger_config = evolve_(
50
+ config or inst.logger_config,
51
+ name=name,
52
+ level=level,
53
+ propagate=propagate,
54
+ )
55
+
56
+
57
+ def log(
58
+ inst: ShellProtocol,
59
+ /,
60
+ level: int,
61
+ msg: object,
62
+ *args: object,
63
+ log_options: LogOptions | None = None,
64
+ console_options: ConsolePrintOptions | None = None,
65
+ ):
66
+ _logger_open(inst)
67
+ assert inst.logger is not None
68
+ match msg:
69
+ case RichConsoleRenderable():
70
+ debug(
71
+ inst,
72
+ f"Sending {repr(msg)} to branches ...",
73
+ log_options=log_options or LogOptions(),
74
+ )
75
+ for name in inst.branches:
76
+ branch = inst.branches[name]
77
+ if level >= inst.logger.level and level >= branch["handler"].level:
78
+ branch["console"].print(
79
+ msg,
80
+ *args,
81
+ **(console_options or ConsolePrintOptions()),
82
+ )
83
+ case _:
84
+ inst.logger.log(
85
+ level,
86
+ msg,
87
+ *args,
88
+ **(log_options or LogOptions()),
89
+ )
90
+
91
+
92
+ def debug(
93
+ inst: ShellProtocol,
94
+ /,
95
+ msg: object,
96
+ *args: object,
97
+ log_options: LogOptions | None = None,
98
+ console_options: ConsolePrintOptions | None = None,
99
+ ):
100
+ log(
101
+ inst,
102
+ logging.DEBUG,
103
+ msg,
104
+ *args,
105
+ log_options=log_options,
106
+ console_options=console_options,
107
+ )
108
+
109
+
110
+ def info(
111
+ inst: ShellProtocol,
112
+ /,
113
+ msg: object,
114
+ *args: object,
115
+ log_options: LogOptions | None = None,
116
+ console_options: ConsolePrintOptions | None = None,
117
+ ):
118
+ log(
119
+ inst,
120
+ logging.INFO,
121
+ msg,
122
+ *args,
123
+ log_options=log_options,
124
+ console_options=console_options,
125
+ )
126
+
127
+
128
+ def warn(
129
+ inst: ShellProtocol,
130
+ /,
131
+ msg: object,
132
+ *args: object,
133
+ log_options: LogOptions | None = None,
134
+ console_options: ConsolePrintOptions | None = None,
135
+ ):
136
+ log(
137
+ inst,
138
+ logging.WARNING,
139
+ msg,
140
+ *args,
141
+ log_options=log_options,
142
+ console_options=console_options,
143
+ )
144
+
145
+
146
+ def error(
147
+ inst: ShellProtocol,
148
+ /,
149
+ msg: object,
150
+ *args: object,
151
+ log_options: LogOptions | None = None,
152
+ console_options: ConsolePrintOptions | None = None,
153
+ ):
154
+ log(
155
+ inst,
156
+ logging.ERROR,
157
+ msg,
158
+ *args,
159
+ log_options=log_options,
160
+ console_options=console_options,
161
+ )
162
+
163
+
164
+ def critical(
165
+ inst: ShellProtocol,
166
+ /,
167
+ msg: object,
168
+ *args: object,
169
+ log_options: LogOptions | None = None,
170
+ console_options: ConsolePrintOptions | None = None,
171
+ ):
172
+ log(
173
+ inst,
174
+ logging.CRITICAL,
175
+ msg,
176
+ *args,
177
+ log_options=log_options,
178
+ console_options=console_options,
179
+ )
180
+
181
+
182
+ def exception(
183
+ inst: ShellProtocol,
184
+ /,
185
+ msg: object,
186
+ *args: object,
187
+ log_options: LogOptions | None = None,
188
+ console_options: ConsolePrintOptions | None = None,
189
+ ):
190
+ default_log_options = LogOptions(exc_info=True)
191
+ log(
192
+ inst,
193
+ logging.CRITICAL,
194
+ msg,
195
+ *args,
196
+ log_options=default_log_options | (log_options or LogOptions()),
197
+ console_options=console_options,
198
+ )
@@ -0,0 +1,96 @@
1
+ from aim import Repo as AimRepo
2
+ from aim.sdk.types import AimObject
3
+
4
+ from nnlogging.shell.protocol import ShellProtocol
5
+ from nnlogging.typings import Omitable
6
+ from nnlogging.utils import RunConfig, evolve_, get_aim_run
7
+
8
+
9
+ def _run_open(
10
+ inst: ShellProtocol,
11
+ /,
12
+ ):
13
+ if inst.run is None:
14
+ inst.run = get_aim_run(inst.run_config)
15
+
16
+
17
+ def _run_close(
18
+ inst: ShellProtocol,
19
+ /,
20
+ ):
21
+ if inst.run is not None:
22
+ inst.run.close()
23
+ del inst.run
24
+ inst.run = None
25
+
26
+
27
+ def run_configure(
28
+ inst: ShellProtocol,
29
+ /,
30
+ config: RunConfig | None = None,
31
+ *,
32
+ experiment: Omitable[str | None] = ...,
33
+ repo: Omitable[str | AimRepo | None] = ...,
34
+ system_tracking_interval: Omitable[float | None] = ...,
35
+ capture_terminal_logs: Omitable[bool] = ...,
36
+ log_system_params: Omitable[bool] = ...,
37
+ run_hash: Omitable[str | None] = ...,
38
+ read_only: Omitable[bool] = ...,
39
+ force_resume: Omitable[bool] = ...,
40
+ ):
41
+ _run_close(inst)
42
+ inst.run_config = evolve_(
43
+ config or inst.run_config,
44
+ experiment=experiment,
45
+ repo=repo,
46
+ system_tracking_interval=system_tracking_interval,
47
+ capture_terminal_logs=capture_terminal_logs,
48
+ log_system_params=log_system_params,
49
+ run_hash=run_hash,
50
+ read_only=read_only,
51
+ force_resume=force_resume,
52
+ )
53
+
54
+
55
+ def update_metadata(
56
+ inst: ShellProtocol,
57
+ /,
58
+ label: str,
59
+ metadata: AimObject,
60
+ ):
61
+ _run_open(inst)
62
+ assert inst.run is not None
63
+ inst.run[label] = metadata
64
+
65
+
66
+ def add_tag(inst: ShellProtocol, /, tag: str):
67
+ _run_open(inst)
68
+ assert inst.run is not None
69
+ inst.run.add_tag(tag)
70
+
71
+
72
+ def remove_tag(inst: ShellProtocol, /, tag: str):
73
+ _run_open(inst)
74
+ assert inst.run is not None
75
+ inst.run.remove_tag(tag)
76
+
77
+
78
+ def track(
79
+ inst: ShellProtocol,
80
+ /,
81
+ value: object,
82
+ *,
83
+ name: str | None = None,
84
+ step: int | None = None,
85
+ epoch: int | None = None,
86
+ context: AimObject = None,
87
+ ):
88
+ _run_open(inst)
89
+ assert inst.run is not None
90
+ inst.run.track(
91
+ value,
92
+ name=name, # pyright: ignore[reportArgumentType]
93
+ step=step, # pyright: ignore[reportArgumentType]
94
+ epoch=epoch, # pyright: ignore[reportArgumentType]
95
+ context=context,
96
+ )
@@ -0,0 +1,115 @@
1
+ import sys
2
+
3
+ from nnlogging.shell.exception import (
4
+ raise_task_exists_error,
5
+ raise_task_not_found_error,
6
+ )
7
+ from nnlogging.shell.protocol import ShellProtocol
8
+ from nnlogging.utils import get_rich_progress
9
+
10
+ sys.version_info.major
11
+
12
+
13
+ def _task_open(
14
+ inst: ShellProtocol,
15
+ /,
16
+ name: str,
17
+ ):
18
+ for branch_name in inst.branches:
19
+ branch = inst.branches[branch_name]
20
+ if name in branch["tasks"]:
21
+ raise_task_exists_error(
22
+ inst,
23
+ branch_name,
24
+ name,
25
+ stacklevel=2,
26
+ )
27
+ if branch["progress"] is None:
28
+ progress = get_rich_progress(
29
+ branch["console"],
30
+ columns=inst.branch_config.columns,
31
+ transient=inst.branch_config.transient,
32
+ refresh_per_second=inst.branch_config.refresh_per_second,
33
+ speed_estimate_period=inst.branch_config.speed_estimate_period,
34
+ default_column_markup=inst.branch_config.default_column_markup,
35
+ )
36
+ progress.start()
37
+ branch["progress"] = progress
38
+
39
+
40
+ def _task_close(
41
+ inst: ShellProtocol,
42
+ /,
43
+ name: str,
44
+ ):
45
+ task_found = False
46
+ for branch_name in inst.branches:
47
+ branch = inst.branches[branch_name]
48
+ if name in branch["tasks"]:
49
+ task_found = True
50
+ assert branch["progress"] is not None # definitely
51
+ task_id = branch["tasks"][name]
52
+ if branch["progress"]._tasks[task_id].finished: # pyright: ignore[reportPrivateUsage]
53
+ del branch["tasks"][name]
54
+ if branch["progress"].finished:
55
+ branch["progress"].stop()
56
+ branch["progress"] = None
57
+ if not task_found:
58
+ raise_task_not_found_error(
59
+ inst,
60
+ name,
61
+ stacklevel=2,
62
+ )
63
+
64
+
65
+ def task_add(
66
+ inst: ShellProtocol,
67
+ /,
68
+ name: str,
69
+ *,
70
+ desc: str,
71
+ total: float | None,
72
+ done: float = 0,
73
+ ):
74
+ _task_open(inst, name)
75
+ for branch_name in inst.branches:
76
+ branch = inst.branches[branch_name]
77
+ assert branch["progress"] is not None # definitely
78
+ task_id = branch["progress"].add_task(
79
+ description=desc,
80
+ total=total,
81
+ completed=done, # pyright: ignore[reportArgumentType]
82
+ )
83
+ branch["tasks"][name] = task_id
84
+
85
+
86
+ def task_remove(
87
+ inst: ShellProtocol,
88
+ /,
89
+ name: str,
90
+ ):
91
+ for branch_name in inst.branches:
92
+ branch = inst.branches[branch_name]
93
+ if name in branch["tasks"]:
94
+ assert branch["progress"] is not None # definitely
95
+ task_id = branch["tasks"][name]
96
+ branch["progress"]._tasks[task_id].finished_time = 0 # pyright: ignore[reportPrivateUsage]
97
+ _task_close(inst, name)
98
+
99
+
100
+ def advance(
101
+ inst: ShellProtocol,
102
+ /,
103
+ name: str,
104
+ *,
105
+ advance: float,
106
+ ):
107
+ for branch_name in inst.branches:
108
+ branch = inst.branches[branch_name]
109
+ assert branch["progress"] is not None # definitely
110
+ task_id = branch["tasks"][name]
111
+ branch["progress"].advance(
112
+ task_id=task_id,
113
+ advance=advance,
114
+ )
115
+ _task_close(inst, name)
@@ -0,0 +1,41 @@
1
+ from .aliases import (
2
+ Branch,
3
+ ConsolePrintOptions,
4
+ ExcInfo,
5
+ FormatTimeCallable,
6
+ GenericPath,
7
+ LogOptions,
8
+ Sink,
9
+ )
10
+ from .exceptions import (
11
+ BranchExistsError,
12
+ BranchNotFoundError,
13
+ TaskExistsError,
14
+ TaskNotFoundError,
15
+ )
16
+ from .generics import DataclassT, Omitable, Pathlike, T
17
+ from .protocols import TerminalWritable, Writable
18
+
19
+ __all__ = [
20
+ # aliases
21
+ "Branch",
22
+ "Sink",
23
+ "GenericPath",
24
+ "FormatTimeCallable",
25
+ "ExcInfo",
26
+ "ConsolePrintOptions",
27
+ "LogOptions",
28
+ # generics
29
+ "T",
30
+ "DataclassT",
31
+ "Omitable",
32
+ "Pathlike",
33
+ # protocols
34
+ "Writable",
35
+ "TerminalWritable",
36
+ # exception
37
+ "BranchNotFoundError",
38
+ "BranchExistsError",
39
+ "TaskExistsError",
40
+ "TaskNotFoundError",
41
+ ]
@@ -0,0 +1,66 @@
1
+ import sys
2
+ from collections.abc import Mapping
3
+ from datetime import datetime
4
+ from types import TracebackType
5
+ from typing import Callable, Literal, TypeAlias, TypedDict
6
+
7
+ from rich.console import Console as RichConsole
8
+ from rich.logging import RichHandler
9
+ from rich.progress import Progress as RichProgress, TaskID as RichTaskID
10
+ from rich.style import Style as RichStyle
11
+ from rich.text import Text as RichText
12
+
13
+ from nnlogging.typings.generics import Pathlike
14
+ from nnlogging.typings.protocols import TerminalWritable, Writable
15
+
16
+ if sys.version_info >= (3, 11):
17
+ from typing import NotRequired, Required
18
+ else:
19
+ from typing_extensions import NotRequired, Required
20
+
21
+
22
+ Sink: TypeAlias = Writable | TerminalWritable
23
+ GenericPath = Pathlike[str] | Pathlike[bytes]
24
+ FormatTimeCallable: TypeAlias = Callable[[datetime], RichText]
25
+
26
+
27
+ class Branch(TypedDict):
28
+ console: Required[RichConsole]
29
+ handler: Required[RichHandler]
30
+ tasks: Required[dict[str, RichTaskID]]
31
+ progress: Required[RichProgress | None]
32
+
33
+
34
+ JustifyMethod = Literal["default", "left", "center", "right", "full"]
35
+ OverflowMethod = Literal["fold", "crop", "ellipsis", "ignore"]
36
+
37
+
38
+ class ConsolePrintOptions(TypedDict):
39
+ sep: NotRequired[str]
40
+ end: NotRequired[str]
41
+ style: NotRequired[str | RichStyle | None]
42
+ justify: NotRequired[JustifyMethod | None]
43
+ overflow: NotRequired[OverflowMethod | None]
44
+ no_wrap: NotRequired[bool | None]
45
+ emoji: NotRequired[bool | None]
46
+ markup: NotRequired[bool | None]
47
+ highlight: NotRequired[bool | None]
48
+ width: NotRequired[int | None]
49
+ height: NotRequired[int | None]
50
+ crop: NotRequired[bool]
51
+ soft_wrap: NotRequired[bool | None]
52
+ new_line_start: NotRequired[bool]
53
+
54
+
55
+ _SysExcInfo: TypeAlias = (
56
+ tuple[type[BaseException], BaseException, TracebackType | None]
57
+ | tuple[None, None, None]
58
+ )
59
+ ExcInfo: TypeAlias = None | bool | _SysExcInfo | BaseException
60
+
61
+
62
+ class LogOptions(TypedDict):
63
+ exc_info: NotRequired[ExcInfo]
64
+ stack_info: NotRequired[bool]
65
+ stacklevel: NotRequired[int]
66
+ extra: NotRequired[Mapping[str, object] | None]
@@ -0,0 +1,10 @@
1
+ class BranchExistsError(LookupError): ...
2
+
3
+
4
+ class BranchNotFoundError(LookupError): ...
5
+
6
+
7
+ class TaskExistsError(LookupError): ...
8
+
9
+
10
+ class TaskNotFoundError(LookupError): ...
@@ -0,0 +1,23 @@
1
+ import sys
2
+ from dataclasses import Field
3
+ from os import PathLike
4
+ from types import EllipsisType
5
+ from typing import Any, ClassVar, Protocol, TypeVar
6
+
7
+ if sys.version_info >= (3, 12):
8
+ from typing import TypeAliasType
9
+ else:
10
+ from typing_extensions import TypeAliasType
11
+
12
+
13
+ class DataclassInstance(Protocol):
14
+ __dataclass_fields__: ClassVar[dict[str, Field[Any]]] # pyright: ignore[reportExplicitAny]
15
+
16
+
17
+ T = TypeVar("T")
18
+ PathT = TypeVar("PathT", str, bytes)
19
+
20
+
21
+ Omitable = TypeAliasType("Omitable", T | EllipsisType, type_params=(T,))
22
+ DataclassT = TypeVar("DataclassT", bound=DataclassInstance)
23
+ Pathlike = TypeAliasType("Pathlike", PathT | PathLike[PathT], type_params=(PathT,))
@@ -0,0 +1,14 @@
1
+ from typing import Protocol, runtime_checkable
2
+
3
+
4
+ @runtime_checkable
5
+ class Writable(Protocol):
6
+ def write(self, text: str, /) -> int: ...
7
+ def flush(self) -> None: ...
8
+
9
+
10
+ @runtime_checkable
11
+ class TerminalWritable(Protocol):
12
+ def write(self, text: str, /) -> int: ...
13
+ def flush(self) -> None: ...
14
+ def isatty(self) -> bool: ...
@@ -0,0 +1,39 @@
1
+ """
2
+ ``nnlogging.utils``
3
+ """
4
+
5
+ from .helpers import evolve_, get__debugging, get_name, or_
6
+ from .rich_factories import (
7
+ get_rich_console,
8
+ get_rich_handler,
9
+ get_rich_progress,
10
+ get_rich_progress_default_columns,
11
+ )
12
+ from .shell_factories import (
13
+ BranchConfig,
14
+ LoggerConfig,
15
+ RunConfig,
16
+ get_aim_run,
17
+ get_branch,
18
+ get_logging_logger,
19
+ )
20
+
21
+ __all__ = [
22
+ # helper
23
+ "evolve_",
24
+ "or_",
25
+ "get__debugging",
26
+ "get_name",
27
+ # rich factory
28
+ "get_rich_console",
29
+ "get_rich_handler",
30
+ "get_rich_progress",
31
+ "get_rich_progress_default_columns",
32
+ # shell factory
33
+ "LoggerConfig",
34
+ "get_logging_logger",
35
+ "RunConfig",
36
+ "get_aim_run",
37
+ "BranchConfig",
38
+ "get_branch",
39
+ ]
@@ -0,0 +1,39 @@
1
+ from dataclasses import replace
2
+ from types import EllipsisType
3
+
4
+ from nnlogging.typings import DataclassT, Omitable, T
5
+
6
+
7
+ def or_(
8
+ value: Omitable[T],
9
+ default: T,
10
+ /,
11
+ ) -> T:
12
+ return default if isinstance(value, EllipsisType) else value
13
+
14
+
15
+ def evolve_(
16
+ inst: DataclassT,
17
+ /,
18
+ **kwargs: object,
19
+ ) -> DataclassT:
20
+ evolutions = {k: v for k, v in kwargs.items() if not isinstance(v, EllipsisType)}
21
+ return replace(inst, **evolutions)
22
+
23
+
24
+ def get_name(
25
+ inst: object,
26
+ /,
27
+ ):
28
+ inst_name = getattr(inst, "name", str(inst))
29
+ assert isinstance(inst_name, str)
30
+ return inst_name
31
+
32
+
33
+ def get__debugging(
34
+ inst: object,
35
+ /,
36
+ ):
37
+ inst_debugging = getattr(inst, "_debugging", False)
38
+ assert isinstance(inst_debugging, bool)
39
+ return inst_debugging