nnlogging 0.1.2__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.
- nnlogging/__init__.py +16 -0
- nnlogging/shell.py +450 -0
- nnlogging/shell_protocol.py +86 -0
- nnlogging/typings/__init__.py +41 -0
- nnlogging/typings/aliases.py +66 -0
- nnlogging/typings/exceptions.py +10 -0
- nnlogging/typings/generics.py +23 -0
- nnlogging/typings/protocols.py +14 -0
- nnlogging/utils/__init__.py +83 -0
- nnlogging/utils/exception/__init__.py +11 -0
- nnlogging/utils/exception/branch_exists.py +49 -0
- nnlogging/utils/exception/branch_not_found.py +49 -0
- nnlogging/utils/exception/task_exists.py +54 -0
- nnlogging/utils/exception/task_not_found.py +49 -0
- nnlogging/utils/factory_funcs/rich_.py +154 -0
- nnlogging/utils/factory_funcs/shell_.py +192 -0
- nnlogging/utils/helpers.py +39 -0
- nnlogging/utils/shell_funcs/branch_.py +164 -0
- nnlogging/utils/shell_funcs/logger_.py +199 -0
- nnlogging/utils/shell_funcs/run_.py +97 -0
- nnlogging/utils/shell_funcs/task_.py +111 -0
- nnlogging-0.1.2.dist-info/METADATA +146 -0
- nnlogging-0.1.2.dist-info/RECORD +24 -0
- nnlogging-0.1.2.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
from collections.abc import Collection
|
|
2
|
+
from logging import Formatter as LoggingFormatter
|
|
3
|
+
from typing import Literal
|
|
4
|
+
|
|
5
|
+
from rich.highlighter import Highlighter as RichHighlighter
|
|
6
|
+
from rich.progress import ProgressColumn as RichProgressColumn
|
|
7
|
+
from rich.theme import Theme as RichTheme
|
|
8
|
+
|
|
9
|
+
from nnlogging.shell_protocol import ShellProtocol
|
|
10
|
+
from nnlogging.typings import FormatTimeCallable, Omitable, Sink
|
|
11
|
+
from nnlogging.utils.exception import (
|
|
12
|
+
raise_branch_exists_error,
|
|
13
|
+
raise_branch_not_found_error,
|
|
14
|
+
)
|
|
15
|
+
from nnlogging.utils.factory_funcs.shell_ import BranchConfig, get_branch
|
|
16
|
+
from nnlogging.utils.helpers import evolve_
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def branch_configure(
|
|
20
|
+
inst: ShellProtocol,
|
|
21
|
+
/,
|
|
22
|
+
config: BranchConfig | None = None,
|
|
23
|
+
*,
|
|
24
|
+
markup: Omitable[bool] = ...,
|
|
25
|
+
highlighter: Omitable[RichHighlighter] = ...,
|
|
26
|
+
width: Omitable[int | None] = ...,
|
|
27
|
+
height: Omitable[int | None] = ...,
|
|
28
|
+
emoji: Omitable[bool] = ...,
|
|
29
|
+
color_system: Omitable[Literal["auto", "standard", "truecolor"] | None] = ...,
|
|
30
|
+
theme: Omitable[RichTheme] = ...,
|
|
31
|
+
soft_wrap: Omitable[bool] = ...,
|
|
32
|
+
force_terminal: Omitable[bool | None] = ...,
|
|
33
|
+
force_jupyter: Omitable[bool | None] = ...,
|
|
34
|
+
force_interactive: Omitable[bool | None] = ...,
|
|
35
|
+
level: Omitable[str | int] = ...,
|
|
36
|
+
show_level: Omitable[bool] = ...,
|
|
37
|
+
show_time: Omitable[bool] = ...,
|
|
38
|
+
show_path: Omitable[bool] = ...,
|
|
39
|
+
log_time_format: Omitable[str | FormatTimeCallable] = ...,
|
|
40
|
+
omit_repeated_times: Omitable[bool] = ...,
|
|
41
|
+
rich_tracebacks: Omitable[bool] = ...,
|
|
42
|
+
tracebacks_show_locals: Omitable[bool] = ...,
|
|
43
|
+
log_message_format: Omitable[str | LoggingFormatter] = ...,
|
|
44
|
+
columns: Omitable[Collection[str | RichProgressColumn]] = ...,
|
|
45
|
+
transient: Omitable[bool] = ...,
|
|
46
|
+
refresh_per_second: Omitable[float] = ...,
|
|
47
|
+
speed_estimate_period: Omitable[float] = ...,
|
|
48
|
+
default_column_markup: Omitable[bool] = ...,
|
|
49
|
+
):
|
|
50
|
+
inst.branch_config = evolve_(
|
|
51
|
+
config or inst.branch_config,
|
|
52
|
+
markup=markup,
|
|
53
|
+
highlighter=highlighter,
|
|
54
|
+
width=width,
|
|
55
|
+
height=height,
|
|
56
|
+
emoji=emoji,
|
|
57
|
+
color_system=color_system,
|
|
58
|
+
theme=theme,
|
|
59
|
+
soft_wrap=soft_wrap,
|
|
60
|
+
force_terminal=force_terminal,
|
|
61
|
+
force_jupyter=force_jupyter,
|
|
62
|
+
force_interactive=force_interactive,
|
|
63
|
+
level=level,
|
|
64
|
+
show_level=show_level,
|
|
65
|
+
show_time=show_time,
|
|
66
|
+
show_path=show_path,
|
|
67
|
+
log_time_format=log_time_format,
|
|
68
|
+
omit_repeated_times=omit_repeated_times,
|
|
69
|
+
rich_tracebacks=rich_tracebacks,
|
|
70
|
+
tracebacks_show_locals=tracebacks_show_locals,
|
|
71
|
+
log_message_format=log_message_format,
|
|
72
|
+
columns=columns,
|
|
73
|
+
transient=transient,
|
|
74
|
+
refresh_per_second=refresh_per_second,
|
|
75
|
+
speed_estimate_period=speed_estimate_period,
|
|
76
|
+
default_column_markup=default_column_markup,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def branch_add(
|
|
81
|
+
inst: ShellProtocol,
|
|
82
|
+
/,
|
|
83
|
+
name: str,
|
|
84
|
+
sink: Sink | None = None,
|
|
85
|
+
*,
|
|
86
|
+
markup: Omitable[bool] = ...,
|
|
87
|
+
highlighter: Omitable[RichHighlighter] = ...,
|
|
88
|
+
width: Omitable[int | None] = ...,
|
|
89
|
+
height: Omitable[int | None] = ...,
|
|
90
|
+
emoji: Omitable[bool] = ...,
|
|
91
|
+
color_system: Omitable[Literal["auto", "standard", "truecolor"] | None] = ...,
|
|
92
|
+
theme: Omitable[RichTheme] = ...,
|
|
93
|
+
soft_wrap: Omitable[bool] = ...,
|
|
94
|
+
force_terminal: Omitable[bool | None] = ...,
|
|
95
|
+
force_jupyter: Omitable[bool | None] = ...,
|
|
96
|
+
force_interactive: Omitable[bool | None] = ...,
|
|
97
|
+
level: Omitable[str | int] = ...,
|
|
98
|
+
show_level: Omitable[bool] = ...,
|
|
99
|
+
show_time: Omitable[bool] = ...,
|
|
100
|
+
show_path: Omitable[bool] = ...,
|
|
101
|
+
log_time_format: Omitable[str | FormatTimeCallable] = ...,
|
|
102
|
+
omit_repeated_times: Omitable[bool] = ...,
|
|
103
|
+
rich_tracebacks: Omitable[bool] = ...,
|
|
104
|
+
tracebacks_show_locals: Omitable[bool] = ...,
|
|
105
|
+
log_message_format: Omitable[str | LoggingFormatter] = ...,
|
|
106
|
+
):
|
|
107
|
+
if name in inst.branches:
|
|
108
|
+
raise_branch_exists_error(
|
|
109
|
+
inst,
|
|
110
|
+
name,
|
|
111
|
+
stacklevel=6,
|
|
112
|
+
)
|
|
113
|
+
else:
|
|
114
|
+
branch = get_branch(
|
|
115
|
+
inst.branch_config,
|
|
116
|
+
sink=sink,
|
|
117
|
+
markup=markup,
|
|
118
|
+
highlighter=highlighter,
|
|
119
|
+
width=width,
|
|
120
|
+
height=height,
|
|
121
|
+
emoji=emoji,
|
|
122
|
+
color_system=color_system,
|
|
123
|
+
theme=theme,
|
|
124
|
+
soft_wrap=soft_wrap,
|
|
125
|
+
force_terminal=force_terminal,
|
|
126
|
+
force_jupyter=force_jupyter,
|
|
127
|
+
force_interactive=force_interactive,
|
|
128
|
+
level=level,
|
|
129
|
+
show_level=show_level,
|
|
130
|
+
show_time=show_time,
|
|
131
|
+
show_path=show_path,
|
|
132
|
+
log_time_format=log_time_format,
|
|
133
|
+
omit_repeated_times=omit_repeated_times,
|
|
134
|
+
rich_tracebacks=rich_tracebacks,
|
|
135
|
+
tracebacks_show_locals=tracebacks_show_locals,
|
|
136
|
+
log_message_format=log_message_format,
|
|
137
|
+
)
|
|
138
|
+
inst.branches[name] = branch
|
|
139
|
+
if inst.logger is not None:
|
|
140
|
+
# TODO: add branch-add-hint
|
|
141
|
+
inst.logger.addHandler(branch["handler"])
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def branch_remove(
|
|
145
|
+
inst: ShellProtocol,
|
|
146
|
+
/,
|
|
147
|
+
name: str,
|
|
148
|
+
):
|
|
149
|
+
if name not in inst.branches:
|
|
150
|
+
raise_branch_not_found_error(
|
|
151
|
+
inst,
|
|
152
|
+
name,
|
|
153
|
+
stacklevel=6,
|
|
154
|
+
)
|
|
155
|
+
else:
|
|
156
|
+
branch = inst.branches[name]
|
|
157
|
+
branch["handler"].close()
|
|
158
|
+
branch["tasks"].clear()
|
|
159
|
+
if branch["progress"] is not None:
|
|
160
|
+
branch["progress"].stop()
|
|
161
|
+
if inst.logger is not None:
|
|
162
|
+
# TODO: add branch-remove-hint
|
|
163
|
+
inst.logger.removeHandler(branch["handler"])
|
|
164
|
+
del inst.branches[name]
|
|
@@ -0,0 +1,199 @@
|
|
|
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.factory_funcs.shell_ import LoggerConfig, get_logging_logger
|
|
8
|
+
from nnlogging.utils.helpers import evolve_
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _logger_open(
|
|
12
|
+
inst: ShellProtocol,
|
|
13
|
+
/,
|
|
14
|
+
):
|
|
15
|
+
if inst.logger is None:
|
|
16
|
+
logging.captureWarnings(True)
|
|
17
|
+
inst.logger = get_logging_logger(inst.logger_config)
|
|
18
|
+
for name in inst.branches:
|
|
19
|
+
branch = inst.branches[name]
|
|
20
|
+
inst.logger.addHandler(branch["handler"])
|
|
21
|
+
# TODO: add `filter` support in v0.2.0
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _logger_close(
|
|
25
|
+
inst: ShellProtocol,
|
|
26
|
+
/,
|
|
27
|
+
):
|
|
28
|
+
if inst.logger is not None:
|
|
29
|
+
logging.captureWarnings(False)
|
|
30
|
+
for handler in inst.logger.handlers[:]:
|
|
31
|
+
inst.logger.removeHandler(handler)
|
|
32
|
+
handler.close()
|
|
33
|
+
# TODO: add `filter` support in v0.2.0
|
|
34
|
+
# inst.logger.filters.clear()
|
|
35
|
+
inst.logger.setLevel(logging.NOTSET)
|
|
36
|
+
inst.logger.propagate = False
|
|
37
|
+
inst.logger = None
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def logger_configure(
|
|
41
|
+
inst: ShellProtocol,
|
|
42
|
+
/,
|
|
43
|
+
config: LoggerConfig | None = None,
|
|
44
|
+
*,
|
|
45
|
+
name: Omitable[str] = ...,
|
|
46
|
+
level: Omitable[int | str] = ...,
|
|
47
|
+
propagate: Omitable[bool] = ...,
|
|
48
|
+
):
|
|
49
|
+
_logger_close(inst)
|
|
50
|
+
inst.logger_config = evolve_(
|
|
51
|
+
config or inst.logger_config,
|
|
52
|
+
name=name,
|
|
53
|
+
level=level,
|
|
54
|
+
propagate=propagate,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def log(
|
|
59
|
+
inst: ShellProtocol,
|
|
60
|
+
/,
|
|
61
|
+
level: int,
|
|
62
|
+
msg: object,
|
|
63
|
+
*args: object,
|
|
64
|
+
log_options: LogOptions | None = None,
|
|
65
|
+
console_options: ConsolePrintOptions | None = None,
|
|
66
|
+
):
|
|
67
|
+
_logger_open(inst)
|
|
68
|
+
assert inst.logger is not None
|
|
69
|
+
match msg:
|
|
70
|
+
case RichConsoleRenderable():
|
|
71
|
+
debug(
|
|
72
|
+
inst,
|
|
73
|
+
f"Sending {repr(msg)} to branches ...",
|
|
74
|
+
log_options=log_options or LogOptions(),
|
|
75
|
+
)
|
|
76
|
+
for name in inst.branches:
|
|
77
|
+
branch = inst.branches[name]
|
|
78
|
+
if level >= inst.logger.level and level >= branch["handler"].level:
|
|
79
|
+
branch["console"].print(
|
|
80
|
+
msg,
|
|
81
|
+
*args,
|
|
82
|
+
**(console_options or ConsolePrintOptions()),
|
|
83
|
+
)
|
|
84
|
+
case _:
|
|
85
|
+
inst.logger.log(
|
|
86
|
+
level,
|
|
87
|
+
msg,
|
|
88
|
+
*args,
|
|
89
|
+
**(log_options or LogOptions()),
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def debug(
|
|
94
|
+
inst: ShellProtocol,
|
|
95
|
+
/,
|
|
96
|
+
msg: object,
|
|
97
|
+
*args: object,
|
|
98
|
+
log_options: LogOptions | None = None,
|
|
99
|
+
console_options: ConsolePrintOptions | None = None,
|
|
100
|
+
):
|
|
101
|
+
log(
|
|
102
|
+
inst,
|
|
103
|
+
logging.DEBUG,
|
|
104
|
+
msg,
|
|
105
|
+
*args,
|
|
106
|
+
log_options=log_options,
|
|
107
|
+
console_options=console_options,
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def info(
|
|
112
|
+
inst: ShellProtocol,
|
|
113
|
+
/,
|
|
114
|
+
msg: object,
|
|
115
|
+
*args: object,
|
|
116
|
+
log_options: LogOptions | None = None,
|
|
117
|
+
console_options: ConsolePrintOptions | None = None,
|
|
118
|
+
):
|
|
119
|
+
log(
|
|
120
|
+
inst,
|
|
121
|
+
logging.INFO,
|
|
122
|
+
msg,
|
|
123
|
+
*args,
|
|
124
|
+
log_options=log_options,
|
|
125
|
+
console_options=console_options,
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def warn(
|
|
130
|
+
inst: ShellProtocol,
|
|
131
|
+
/,
|
|
132
|
+
msg: object,
|
|
133
|
+
*args: object,
|
|
134
|
+
log_options: LogOptions | None = None,
|
|
135
|
+
console_options: ConsolePrintOptions | None = None,
|
|
136
|
+
):
|
|
137
|
+
log(
|
|
138
|
+
inst,
|
|
139
|
+
logging.WARNING,
|
|
140
|
+
msg,
|
|
141
|
+
*args,
|
|
142
|
+
log_options=log_options,
|
|
143
|
+
console_options=console_options,
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def error(
|
|
148
|
+
inst: ShellProtocol,
|
|
149
|
+
/,
|
|
150
|
+
msg: object,
|
|
151
|
+
*args: object,
|
|
152
|
+
log_options: LogOptions | None = None,
|
|
153
|
+
console_options: ConsolePrintOptions | None = None,
|
|
154
|
+
):
|
|
155
|
+
log(
|
|
156
|
+
inst,
|
|
157
|
+
logging.ERROR,
|
|
158
|
+
msg,
|
|
159
|
+
*args,
|
|
160
|
+
log_options=log_options,
|
|
161
|
+
console_options=console_options,
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def critical(
|
|
166
|
+
inst: ShellProtocol,
|
|
167
|
+
/,
|
|
168
|
+
msg: object,
|
|
169
|
+
*args: object,
|
|
170
|
+
log_options: LogOptions | None = None,
|
|
171
|
+
console_options: ConsolePrintOptions | None = None,
|
|
172
|
+
):
|
|
173
|
+
log(
|
|
174
|
+
inst,
|
|
175
|
+
logging.CRITICAL,
|
|
176
|
+
msg,
|
|
177
|
+
*args,
|
|
178
|
+
log_options=log_options,
|
|
179
|
+
console_options=console_options,
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def exception(
|
|
184
|
+
inst: ShellProtocol,
|
|
185
|
+
/,
|
|
186
|
+
msg: object,
|
|
187
|
+
*args: object,
|
|
188
|
+
log_options: LogOptions | None = None,
|
|
189
|
+
console_options: ConsolePrintOptions | None = None,
|
|
190
|
+
):
|
|
191
|
+
default_log_options = LogOptions(exc_info=True)
|
|
192
|
+
log(
|
|
193
|
+
inst,
|
|
194
|
+
logging.CRITICAL,
|
|
195
|
+
msg,
|
|
196
|
+
*args,
|
|
197
|
+
log_options=default_log_options | (log_options or LogOptions()),
|
|
198
|
+
console_options=console_options,
|
|
199
|
+
)
|
|
@@ -0,0 +1,97 @@
|
|
|
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.factory_funcs.shell_ import RunConfig, get_aim_run
|
|
7
|
+
from nnlogging.utils.helpers import evolve_
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _run_open(
|
|
11
|
+
inst: ShellProtocol,
|
|
12
|
+
/,
|
|
13
|
+
):
|
|
14
|
+
if inst.run is None:
|
|
15
|
+
inst.run = get_aim_run(inst.run_config)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _run_close(
|
|
19
|
+
inst: ShellProtocol,
|
|
20
|
+
/,
|
|
21
|
+
):
|
|
22
|
+
if inst.run is not None:
|
|
23
|
+
inst.run.close()
|
|
24
|
+
del inst.run
|
|
25
|
+
inst.run = None
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def run_configure(
|
|
29
|
+
inst: ShellProtocol,
|
|
30
|
+
/,
|
|
31
|
+
config: RunConfig | None = None,
|
|
32
|
+
*,
|
|
33
|
+
experiment: Omitable[str | None] = ...,
|
|
34
|
+
repo: Omitable[str | AimRepo | None] = ...,
|
|
35
|
+
system_tracking_interval: Omitable[float | None] = ...,
|
|
36
|
+
capture_terminal_logs: Omitable[bool] = ...,
|
|
37
|
+
log_system_params: Omitable[bool] = ...,
|
|
38
|
+
run_hash: Omitable[str | None] = ...,
|
|
39
|
+
read_only: Omitable[bool] = ...,
|
|
40
|
+
force_resume: Omitable[bool] = ...,
|
|
41
|
+
):
|
|
42
|
+
_run_close(inst)
|
|
43
|
+
inst.run_config = evolve_(
|
|
44
|
+
config or inst.run_config,
|
|
45
|
+
experiment=experiment,
|
|
46
|
+
repo=repo,
|
|
47
|
+
system_tracking_interval=system_tracking_interval,
|
|
48
|
+
capture_terminal_logs=capture_terminal_logs,
|
|
49
|
+
log_system_params=log_system_params,
|
|
50
|
+
run_hash=run_hash,
|
|
51
|
+
read_only=read_only,
|
|
52
|
+
force_resume=force_resume,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def update_metadata(
|
|
57
|
+
inst: ShellProtocol,
|
|
58
|
+
/,
|
|
59
|
+
label: str,
|
|
60
|
+
metadata: AimObject,
|
|
61
|
+
):
|
|
62
|
+
_run_open(inst)
|
|
63
|
+
assert inst.run is not None
|
|
64
|
+
inst.run[label] = metadata
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def add_tag(inst: ShellProtocol, /, tag: str):
|
|
68
|
+
_run_open(inst)
|
|
69
|
+
assert inst.run is not None
|
|
70
|
+
inst.run.add_tag(tag)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def remove_tag(inst: ShellProtocol, /, tag: str):
|
|
74
|
+
_run_open(inst)
|
|
75
|
+
assert inst.run is not None
|
|
76
|
+
inst.run.remove_tag(tag)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def track(
|
|
80
|
+
inst: ShellProtocol,
|
|
81
|
+
/,
|
|
82
|
+
value: object,
|
|
83
|
+
*,
|
|
84
|
+
name: str | None = None,
|
|
85
|
+
step: int | None = None,
|
|
86
|
+
epoch: int | None = None,
|
|
87
|
+
context: AimObject = None,
|
|
88
|
+
):
|
|
89
|
+
_run_open(inst)
|
|
90
|
+
assert inst.run is not None
|
|
91
|
+
inst.run.track(
|
|
92
|
+
value,
|
|
93
|
+
name=name, # pyright: ignore[reportArgumentType]
|
|
94
|
+
step=step, # pyright: ignore[reportArgumentType]
|
|
95
|
+
epoch=epoch, # pyright: ignore[reportArgumentType]
|
|
96
|
+
context=context,
|
|
97
|
+
)
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
from nnlogging.shell_protocol import ShellProtocol
|
|
2
|
+
from nnlogging.utils.exception import (
|
|
3
|
+
raise_task_exists_error,
|
|
4
|
+
raise_task_not_found_error,
|
|
5
|
+
)
|
|
6
|
+
from nnlogging.utils.factory_funcs.rich_ import get_rich_progress
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _task_open(
|
|
10
|
+
inst: ShellProtocol,
|
|
11
|
+
/,
|
|
12
|
+
name: str,
|
|
13
|
+
):
|
|
14
|
+
for branch_name in inst.branches:
|
|
15
|
+
branch = inst.branches[branch_name]
|
|
16
|
+
if name in branch["tasks"]:
|
|
17
|
+
raise_task_exists_error(
|
|
18
|
+
inst,
|
|
19
|
+
branch_name,
|
|
20
|
+
name,
|
|
21
|
+
stacklevel=2,
|
|
22
|
+
)
|
|
23
|
+
if branch["progress"] is None:
|
|
24
|
+
progress = get_rich_progress(
|
|
25
|
+
branch["console"],
|
|
26
|
+
columns=inst.branch_config.columns,
|
|
27
|
+
transient=inst.branch_config.transient,
|
|
28
|
+
refresh_per_second=inst.branch_config.refresh_per_second,
|
|
29
|
+
speed_estimate_period=inst.branch_config.speed_estimate_period,
|
|
30
|
+
default_column_markup=inst.branch_config.default_column_markup,
|
|
31
|
+
)
|
|
32
|
+
progress.start()
|
|
33
|
+
branch["progress"] = progress
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _task_close(
|
|
37
|
+
inst: ShellProtocol,
|
|
38
|
+
/,
|
|
39
|
+
name: str,
|
|
40
|
+
):
|
|
41
|
+
task_found = False
|
|
42
|
+
for branch_name in inst.branches:
|
|
43
|
+
branch = inst.branches[branch_name]
|
|
44
|
+
if name in branch["tasks"]:
|
|
45
|
+
task_found = True
|
|
46
|
+
assert branch["progress"] is not None # definitely
|
|
47
|
+
task_id = branch["tasks"][name]
|
|
48
|
+
if branch["progress"]._tasks[task_id].finished: # pyright: ignore[reportPrivateUsage]
|
|
49
|
+
del branch["tasks"][name]
|
|
50
|
+
if branch["progress"].finished:
|
|
51
|
+
branch["progress"].stop()
|
|
52
|
+
branch["progress"] = None
|
|
53
|
+
if not task_found:
|
|
54
|
+
raise_task_not_found_error(
|
|
55
|
+
inst,
|
|
56
|
+
name,
|
|
57
|
+
stacklevel=2,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def task_add(
|
|
62
|
+
inst: ShellProtocol,
|
|
63
|
+
/,
|
|
64
|
+
name: str,
|
|
65
|
+
*,
|
|
66
|
+
desc: str,
|
|
67
|
+
total: float | None,
|
|
68
|
+
done: float = 0,
|
|
69
|
+
):
|
|
70
|
+
_task_open(inst, name)
|
|
71
|
+
for branch_name in inst.branches:
|
|
72
|
+
branch = inst.branches[branch_name]
|
|
73
|
+
assert branch["progress"] is not None # definitely
|
|
74
|
+
task_id = branch["progress"].add_task(
|
|
75
|
+
description=desc,
|
|
76
|
+
total=total,
|
|
77
|
+
completed=done, # pyright: ignore[reportArgumentType]
|
|
78
|
+
)
|
|
79
|
+
branch["tasks"][name] = task_id
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def task_remove(
|
|
83
|
+
inst: ShellProtocol,
|
|
84
|
+
/,
|
|
85
|
+
name: str,
|
|
86
|
+
):
|
|
87
|
+
for branch_name in inst.branches:
|
|
88
|
+
branch = inst.branches[branch_name]
|
|
89
|
+
if name in branch["tasks"]:
|
|
90
|
+
assert branch["progress"] is not None # definitely
|
|
91
|
+
task_id = branch["tasks"][name]
|
|
92
|
+
branch["progress"]._tasks[task_id].finished_time = 0 # pyright: ignore[reportPrivateUsage]
|
|
93
|
+
_task_close(inst, name)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def advance(
|
|
97
|
+
inst: ShellProtocol,
|
|
98
|
+
/,
|
|
99
|
+
name: str,
|
|
100
|
+
*,
|
|
101
|
+
advance: float,
|
|
102
|
+
):
|
|
103
|
+
for branch_name in inst.branches:
|
|
104
|
+
branch = inst.branches[branch_name]
|
|
105
|
+
assert branch["progress"] is not None # definitely
|
|
106
|
+
task_id = branch["tasks"][name]
|
|
107
|
+
branch["progress"].advance(
|
|
108
|
+
task_id=task_id,
|
|
109
|
+
advance=advance,
|
|
110
|
+
)
|
|
111
|
+
_task_close(inst, name)
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nnlogging
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: A powerful and elegant logging library designed specifically for neural network and machine learning experiments. nnlogging seamlessly integrates [Rich](https://github.com/Textualize/rich) for beautiful terminal output and [Aim](https://github.com/aimhubio/aim) for comprehensive experiment tracking.
|
|
5
|
+
Requires-Python: <3.13,>=3.10
|
|
6
|
+
Requires-Dist: aim>=3.29.0
|
|
7
|
+
Requires-Dist: rich>=14.0.0
|
|
8
|
+
Provides-Extra: dev
|
|
9
|
+
Requires-Dist: faker>=37.11.0; extra == 'dev'
|
|
10
|
+
Requires-Dist: pytest-cov>=7.0.0; extra == 'dev'
|
|
11
|
+
Requires-Dist: pytest-html>=4.1.1; extra == 'dev'
|
|
12
|
+
Requires-Dist: pytest-repeat>=0.9.4; extra == 'dev'
|
|
13
|
+
Requires-Dist: pytest-sugar>=1.1.1; extra == 'dev'
|
|
14
|
+
Requires-Dist: pytest-xdist>=3.8.0; extra == 'dev'
|
|
15
|
+
Requires-Dist: pytest>=8.4.2; extra == 'dev'
|
|
16
|
+
Requires-Dist: twine>=6.2.0; extra == 'dev'
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
|
|
19
|
+
# nnlogging
|
|
20
|
+
|
|
21
|
+
[nnlogging](https://codeberg.org/nnaf/nnlogging) is a powerful, modern logging
|
|
22
|
+
library for neural network and machine learning experiments, combining
|
|
23
|
+
[Rich](https://github.com/Textualize/rich) for beautiful terminal output with
|
|
24
|
+
[Aim](https://github.com/aimhubio/aim) for comprehensive experiment tracking.
|
|
25
|
+
|
|
26
|
+
## ✨ Features
|
|
27
|
+
|
|
28
|
+
- 🎨 **Beautiful Console Output** - Rich-powered colorful logging with
|
|
29
|
+
integrated progress bars.
|
|
30
|
+
- 📊 **Effortless Experiment Tracking** - Seamless Aim integration for metrics,
|
|
31
|
+
parameters, and system monitoring.
|
|
32
|
+
- 🔧 **Simplified API** - Get started in two lines of code with the global
|
|
33
|
+
shell.
|
|
34
|
+
- 📈 **Advanced Progress Tracking** - Manage multiple progress bars for
|
|
35
|
+
training, validation, and other tasks.
|
|
36
|
+
- 🎯 **ML-Focused Design** - Purpose-built for the machine learning workflow.
|
|
37
|
+
- 🐍 **Modern Python** - Fully typed, Python 3.10+ codebase.
|
|
38
|
+
|
|
39
|
+
## 🚀 Installation
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pip install nnlogging
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**Requirements:** Python 3.10-3.12
|
|
46
|
+
|
|
47
|
+
## ⚡ Quick Start
|
|
48
|
+
|
|
49
|
+
The new `nnlogging` API is designed for simplicity. You can get a
|
|
50
|
+
pre-configured, global logger instance and start logging right away.
|
|
51
|
+
|
|
52
|
+
### Basic Logging
|
|
53
|
+
|
|
54
|
+
Get the global shell or customize your shell from scratch to use logging methods
|
|
55
|
+
and trackings.
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
from nnlogging import get_global_shell
|
|
59
|
+
|
|
60
|
+
# 1. Get the global shell logger
|
|
61
|
+
shell = get_global_shell()
|
|
62
|
+
|
|
63
|
+
# 2. Log messages!
|
|
64
|
+
shell.info("Starting training...")
|
|
65
|
+
shell.warn("Learning rate seems high.")
|
|
66
|
+
shell.debug("This is a detailed debug message.")
|
|
67
|
+
shell.error("CUDA out of memory.", extra={"show_locals": True})
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Experiment Tracking with Aim
|
|
71
|
+
|
|
72
|
+
Configure the Aim run to track metrics, hyperparameters, and more.
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
import random
|
|
76
|
+
from nnlogging import get_global_shell
|
|
77
|
+
|
|
78
|
+
shell = get_global_shell()
|
|
79
|
+
|
|
80
|
+
# 1. Configure the experiment tracker
|
|
81
|
+
shell.run_configure(
|
|
82
|
+
experiment="resnet_training",
|
|
83
|
+
log_system_params=True, # Track CPU/GPU usage
|
|
84
|
+
capture_terminal_logs=True # Save console output in Aim
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
# 2. Log hyperparameters
|
|
88
|
+
config = {"lr": 0.001, "batch_size": 32, "model": "ResNet50"}
|
|
89
|
+
shell.update_metadata("hparams", config)
|
|
90
|
+
shell.info(f"Using config: {config}")
|
|
91
|
+
|
|
92
|
+
# 3. Track metrics in your training loop
|
|
93
|
+
for epoch in range(10):
|
|
94
|
+
train_loss = 1.0 / (epoch + 1) + random.random() * 0.1
|
|
95
|
+
shell.track(train_loss, name="train_loss", epoch=epoch)
|
|
96
|
+
shell.info(f"Epoch {epoch}: Loss={train_loss:.4f}")
|
|
97
|
+
|
|
98
|
+
shell.info("Experiment finished!")
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
To view the results, run `aim up` in your terminal.
|
|
102
|
+
|
|
103
|
+
### Progress Tracking
|
|
104
|
+
|
|
105
|
+
Add tasks to the logger to display and update rich progress bars.
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
import time
|
|
109
|
+
from nnlogging import get_global_shell
|
|
110
|
+
|
|
111
|
+
shell = get_global_shell()
|
|
112
|
+
|
|
113
|
+
# 1. Add a task to the progress display
|
|
114
|
+
shell.task_add("training", desc="Training", total=500)
|
|
115
|
+
|
|
116
|
+
# 2. Update the task during your training loop
|
|
117
|
+
for step in range(500):
|
|
118
|
+
# training_step()
|
|
119
|
+
time.sleep(0.01)
|
|
120
|
+
shell.advance("training", 1)
|
|
121
|
+
|
|
122
|
+
if step % 100 == 0:
|
|
123
|
+
shell.info(f"Completed step {step}")
|
|
124
|
+
|
|
125
|
+
# 3. Remove the task when finished
|
|
126
|
+
shell.task_remove("training")
|
|
127
|
+
shell.info("Training complete!")
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## 🔄 Workflow
|
|
131
|
+
|
|
132
|
+
1. **`get_global_shell()`** - Get the logger instance at the start of your
|
|
133
|
+
script.
|
|
134
|
+
2. **`run_configure()`** - (Optional) Configure Aim for experiment tracking.
|
|
135
|
+
3. **Log & Track** - Use `shell.info()`, `shell.track()`, and `shell.advance()`
|
|
136
|
+
throughout your code.
|
|
137
|
+
4. **Visualize** - Run `aim up` to launch the Aim UI and analyze your results.
|
|
138
|
+
|
|
139
|
+
## 🤝 Contributing
|
|
140
|
+
|
|
141
|
+
Contributions are welcome! Please feel free to open an issue or submit a pull
|
|
142
|
+
request.
|
|
143
|
+
|
|
144
|
+
## 📄 License
|
|
145
|
+
|
|
146
|
+
This project is licensed under the MIT License.
|