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.
@@ -0,0 +1,83 @@
1
+ """
2
+ ``nnlogging.utils``
3
+ """
4
+
5
+ from .exception import (
6
+ raise_branch_exists_error,
7
+ raise_branch_not_found_error,
8
+ raise_task_exists_error,
9
+ raise_task_not_found_error,
10
+ )
11
+ from .factory_funcs.rich_ import (
12
+ get_rich_console,
13
+ get_rich_handler,
14
+ get_rich_progress,
15
+ get_rich_progress_default_columns,
16
+ )
17
+ from .factory_funcs.shell_ import (
18
+ BranchConfig,
19
+ LoggerConfig,
20
+ RunConfig,
21
+ get_aim_run,
22
+ get_branch,
23
+ get_logging_logger,
24
+ )
25
+ from .helpers import evolve_, get__debugging, get_name, or_
26
+ from .shell_funcs.branch_ import branch_add, branch_configure, branch_remove
27
+ from .shell_funcs.logger_ import (
28
+ critical,
29
+ debug,
30
+ error,
31
+ exception,
32
+ info,
33
+ log,
34
+ logger_configure,
35
+ warn,
36
+ )
37
+ from .shell_funcs.run_ import add_tag, remove_tag, run_configure, track, update_metadata
38
+ from .shell_funcs.task_ import advance, task_add, task_remove
39
+
40
+ __all__ = [
41
+ # helper
42
+ "evolve_",
43
+ "or_",
44
+ "get__debugging",
45
+ "get_name",
46
+ # rich factory
47
+ "get_rich_console",
48
+ "get_rich_handler",
49
+ "get_rich_progress",
50
+ "get_rich_progress_default_columns",
51
+ # shell factory
52
+ "LoggerConfig",
53
+ "get_logging_logger",
54
+ "RunConfig",
55
+ "get_aim_run",
56
+ "BranchConfig",
57
+ "get_branch",
58
+ # shell func
59
+ "branch_add",
60
+ "branch_configure",
61
+ "branch_remove",
62
+ "critical",
63
+ "debug",
64
+ "error",
65
+ "exception",
66
+ "info",
67
+ "log",
68
+ "logger_configure",
69
+ "warn",
70
+ "add_tag",
71
+ "remove_tag",
72
+ "run_configure",
73
+ "track",
74
+ "update_metadata",
75
+ "advance",
76
+ "task_add",
77
+ "task_remove",
78
+ # exception
79
+ "raise_branch_exists_error",
80
+ "raise_branch_not_found_error",
81
+ "raise_task_exists_error",
82
+ "raise_task_not_found_error",
83
+ ]
@@ -0,0 +1,11 @@
1
+ from .branch_exists import raise_branch_exists_error
2
+ from .branch_not_found import raise_branch_not_found_error
3
+ from .task_exists import raise_task_exists_error
4
+ from .task_not_found import raise_task_not_found_error
5
+
6
+ __all__ = [
7
+ "raise_branch_exists_error",
8
+ "raise_branch_not_found_error",
9
+ "raise_task_exists_error",
10
+ "raise_task_not_found_error",
11
+ ]
@@ -0,0 +1,49 @@
1
+ import sys
2
+
3
+ from nnlogging.shell_protocol import ShellProtocol
4
+ from nnlogging.typings import BranchExistsError, LogOptions
5
+ from nnlogging.utils.helpers import get__debugging, get_name
6
+
7
+ if sys.version_info >= (3, 11):
8
+ from typing import Never
9
+ else:
10
+ from typing_extensions import Never
11
+
12
+
13
+ def _branch_exists_msg(
14
+ inst: ShellProtocol,
15
+ name: str,
16
+ /,
17
+ ):
18
+ return f'branch "{name}" already exists in {get_name(inst)}.branches'
19
+
20
+
21
+ def _branch_exists_error(
22
+ inst: ShellProtocol,
23
+ name: str,
24
+ /,
25
+ ):
26
+ return BranchExistsError(
27
+ f'branch "{name}" already exists in {get_name(inst)}.branches: {inst.branches}'
28
+ )
29
+
30
+
31
+ def raise_branch_exists_error(
32
+ inst: ShellProtocol,
33
+ name: str,
34
+ /,
35
+ *,
36
+ stacklevel: int = 1,
37
+ ) -> Never:
38
+ try:
39
+ raise _branch_exists_error(inst, name)
40
+ except BranchExistsError as e:
41
+ inst.exception(
42
+ _branch_exists_msg(inst, name),
43
+ log_options=LogOptions(
44
+ exc_info=e,
45
+ stack_info=get__debugging(inst),
46
+ stacklevel=stacklevel + 1,
47
+ ),
48
+ )
49
+ raise
@@ -0,0 +1,49 @@
1
+ import sys
2
+
3
+ from nnlogging.shell_protocol import ShellProtocol
4
+ from nnlogging.typings import BranchNotFoundError, LogOptions
5
+ from nnlogging.utils.helpers import get__debugging, get_name
6
+
7
+ if sys.version_info >= (3, 11):
8
+ from typing import Never
9
+ else:
10
+ from typing_extensions import Never
11
+
12
+
13
+ def _branch_not_found_msg(
14
+ inst: ShellProtocol,
15
+ name: str,
16
+ /,
17
+ ):
18
+ return f'branch "{name}" not found in {get_name(inst)}.branches'
19
+
20
+
21
+ def _branch_not_found_error(
22
+ inst: ShellProtocol,
23
+ name: str,
24
+ /,
25
+ ):
26
+ return BranchNotFoundError(
27
+ f'branch "{name}" not found in {get_name(inst)}.branches: {inst.branches}'
28
+ )
29
+
30
+
31
+ def raise_branch_not_found_error(
32
+ inst: ShellProtocol,
33
+ name: str,
34
+ /,
35
+ *,
36
+ stacklevel: int = 1,
37
+ ) -> Never:
38
+ try:
39
+ raise _branch_not_found_error(inst, name)
40
+ except BranchNotFoundError as e:
41
+ inst.exception(
42
+ _branch_not_found_msg(inst, name),
43
+ log_options=LogOptions(
44
+ exc_info=e,
45
+ stack_info=get__debugging(inst),
46
+ stacklevel=stacklevel + 1,
47
+ ),
48
+ )
49
+ raise
@@ -0,0 +1,54 @@
1
+ import sys
2
+
3
+ from nnlogging.shell_protocol import ShellProtocol
4
+ from nnlogging.typings import LogOptions, TaskExistsError
5
+ from nnlogging.utils.helpers import get__debugging, get_name
6
+
7
+ if sys.version_info >= (3, 11):
8
+ from typing import Never
9
+ else:
10
+ from typing_extensions import Never
11
+
12
+
13
+ def _task_exists_msg(
14
+ inst: ShellProtocol,
15
+ branch: str,
16
+ name: str,
17
+ /,
18
+ ):
19
+ return (
20
+ f'task "{name}" already exists in {get_name(inst)}.branches."{branch}"."tasks"'
21
+ )
22
+
23
+
24
+ def _task_exists_error(
25
+ inst: ShellProtocol,
26
+ branch: str,
27
+ name: str,
28
+ /,
29
+ ):
30
+ return TaskExistsError(
31
+ f'task "{name}" already exists in {get_name(inst)}.branches."{branch}"."tasks": {inst.branches[branch]["tasks"]}'
32
+ )
33
+
34
+
35
+ def raise_task_exists_error(
36
+ inst: ShellProtocol,
37
+ branch: str,
38
+ name: str,
39
+ /,
40
+ *,
41
+ stacklevel: int = 1,
42
+ ) -> Never:
43
+ try:
44
+ raise _task_exists_error(inst, branch, name)
45
+ except TaskExistsError as e:
46
+ inst.exception(
47
+ _task_exists_msg(inst, branch, name),
48
+ log_options=LogOptions(
49
+ exc_info=e,
50
+ stack_info=get__debugging(inst),
51
+ stacklevel=stacklevel + 1,
52
+ ),
53
+ )
54
+ raise
@@ -0,0 +1,49 @@
1
+ import sys
2
+
3
+ from nnlogging.shell_protocol import ShellProtocol
4
+ from nnlogging.typings import LogOptions, TaskNotFoundError
5
+ from nnlogging.utils.helpers import get__debugging, get_name
6
+
7
+ if sys.version_info >= (3, 11):
8
+ from typing import Never
9
+ else:
10
+ from typing_extensions import Never
11
+
12
+
13
+ def _task_not_found_msg(
14
+ inst: ShellProtocol,
15
+ name: str,
16
+ /,
17
+ ):
18
+ return f'task "{name}" not found in {get_name(inst)}.branches'
19
+
20
+
21
+ def _task_not_found_error(
22
+ inst: ShellProtocol,
23
+ name: str,
24
+ /,
25
+ ):
26
+ return TaskNotFoundError(
27
+ f'task "{name}" not found in {get_name(inst)}.branches: {inst.branches}'
28
+ )
29
+
30
+
31
+ def raise_task_not_found_error(
32
+ inst: ShellProtocol,
33
+ name: str,
34
+ /,
35
+ *,
36
+ stacklevel: int = 1,
37
+ ) -> Never:
38
+ try:
39
+ raise _task_not_found_error(inst, name)
40
+ except TaskNotFoundError as e:
41
+ inst.exception(
42
+ _task_not_found_msg(inst, name),
43
+ log_options=LogOptions(
44
+ exc_info=e,
45
+ stack_info=get__debugging(inst),
46
+ stacklevel=stacklevel + 1,
47
+ ),
48
+ )
49
+ raise
@@ -0,0 +1,154 @@
1
+ import logging
2
+ from collections.abc import Collection
3
+ from logging import Formatter as LoggingFormatter
4
+ from sys import stderr
5
+ from typing import Literal
6
+
7
+ from rich.console import Console as RichConsole
8
+ from rich.highlighter import (
9
+ Highlighter as RichHighlighter,
10
+ NullHighlighter as RichNullHighlighter,
11
+ )
12
+ from rich.logging import RichHandler
13
+ from rich.progress import Progress as RichProgress, ProgressColumn as RichProgressColumn
14
+ from rich.theme import Theme as RichTheme
15
+
16
+ from nnlogging.typings import FormatTimeCallable, Sink
17
+
18
+
19
+ def get_rich_console(
20
+ sink: Sink | None = None,
21
+ /,
22
+ *,
23
+ width: int | None = None,
24
+ height: int | None = None,
25
+ markup: bool = True,
26
+ emoji: bool = True,
27
+ color_system: Literal["auto", "standard", "truecolor"] | None = "auto",
28
+ theme: RichTheme | None = None,
29
+ highlighter: RichHighlighter | None = None,
30
+ soft_wrap: bool = True,
31
+ force_terminal: bool | None = None,
32
+ force_jupyter: bool | None = None,
33
+ force_interactive: bool | None = None,
34
+ ):
35
+ console = RichConsole(
36
+ file=sink or stderr, # pyright: ignore[reportArgumentType]
37
+ width=width,
38
+ height=height,
39
+ markup=markup,
40
+ emoji=emoji,
41
+ color_system=color_system,
42
+ theme=theme,
43
+ highlight=highlighter is not None
44
+ and not isinstance(highlighter, RichNullHighlighter),
45
+ highlighter=highlighter or RichNullHighlighter(),
46
+ soft_wrap=soft_wrap,
47
+ force_terminal=force_terminal,
48
+ force_jupyter=force_jupyter,
49
+ force_interactive=force_interactive,
50
+ )
51
+ return console
52
+
53
+
54
+ def get_rich_handler(
55
+ console: RichConsole | None = None,
56
+ /,
57
+ *,
58
+ level: str | int = logging.NOTSET,
59
+ show_level: bool = True,
60
+ show_time: bool = True,
61
+ show_path: bool = True,
62
+ log_time_format: str | FormatTimeCallable = "[%x %X]",
63
+ omit_repeated_times: bool = True,
64
+ markup: bool = True,
65
+ highlighter: RichHighlighter | None = None,
66
+ rich_tracebacks: bool = True,
67
+ tracebacks_show_locals: bool = False,
68
+ log_message_format: str | LoggingFormatter = "%(message)s",
69
+ ):
70
+ handler = RichHandler(
71
+ console=console or get_rich_console(),
72
+ level=level,
73
+ show_level=show_level,
74
+ show_time=show_time,
75
+ show_path=show_path,
76
+ log_time_format=log_time_format,
77
+ omit_repeated_times=omit_repeated_times,
78
+ markup=markup,
79
+ highlighter=highlighter or RichNullHighlighter(),
80
+ rich_tracebacks=rich_tracebacks,
81
+ tracebacks_show_locals=tracebacks_show_locals,
82
+ )
83
+ match log_message_format:
84
+ case str():
85
+ handler.setFormatter(LoggingFormatter(log_message_format))
86
+ case LoggingFormatter():
87
+ handler.setFormatter(log_message_format)
88
+ return handler
89
+
90
+
91
+ def get_rich_progress_default_columns(
92
+ *,
93
+ markup: bool = True,
94
+ ):
95
+ from rich.progress import (
96
+ BarColumn,
97
+ SpinnerColumn,
98
+ TaskProgressColumn,
99
+ TextColumn,
100
+ TimeRemainingColumn,
101
+ )
102
+
103
+ spinner_column = SpinnerColumn(
104
+ spinner_name="dots",
105
+ style="progress.spinner",
106
+ finished_text=" ",
107
+ )
108
+ text_column = TextColumn(
109
+ text_format="{task.description}",
110
+ style="progress.description",
111
+ justify="left",
112
+ markup=markup,
113
+ )
114
+ bar_column = BarColumn(bar_width=40)
115
+ task_progress_column = TaskProgressColumn(
116
+ text_format="{task.percentage:>3.0f}%",
117
+ text_format_no_percentage="",
118
+ style="progress.percentage",
119
+ justify="right",
120
+ markup=markup,
121
+ show_speed=True,
122
+ )
123
+ time_remaining_column = TimeRemainingColumn(
124
+ compact=False,
125
+ elapsed_when_finished=True,
126
+ )
127
+
128
+ return (
129
+ spinner_column,
130
+ text_column,
131
+ bar_column,
132
+ task_progress_column,
133
+ time_remaining_column,
134
+ )
135
+
136
+
137
+ def get_rich_progress(
138
+ console: RichConsole | None = None,
139
+ /,
140
+ *,
141
+ columns: Collection[str | RichProgressColumn] | None = None,
142
+ transient: bool = False,
143
+ refresh_per_second: float = 10,
144
+ speed_estimate_period: float = 3600,
145
+ default_column_markup: bool = True,
146
+ ):
147
+ progress = RichProgress(
148
+ *(columns or get_rich_progress_default_columns(markup=default_column_markup)),
149
+ console=console,
150
+ transient=transient,
151
+ refresh_per_second=refresh_per_second,
152
+ speed_estimate_period=speed_estimate_period,
153
+ )
154
+ return progress
@@ -0,0 +1,192 @@
1
+ import logging
2
+ from collections.abc import Collection
3
+ from dataclasses import asdict, dataclass, field
4
+ from logging import Formatter as LoggingFormatter
5
+ from typing import Literal
6
+
7
+ from aim import Repo as AimRepo, Run as AimRun
8
+ from rich.highlighter import Highlighter as RichHighlighter
9
+ from rich.progress import ProgressColumn as RichProgressColumn
10
+ from rich.theme import Theme as RichTheme
11
+
12
+ from nnlogging.typings import Branch, FormatTimeCallable, Omitable, Sink
13
+ from nnlogging.utils.factory_funcs.rich_ import (
14
+ get_rich_console,
15
+ get_rich_handler,
16
+ get_rich_progress_default_columns,
17
+ )
18
+ from nnlogging.utils.helpers import evolve_, or_
19
+
20
+
21
+ @dataclass(slots=True, kw_only=True)
22
+ class LoggerConfig:
23
+ name: str = "nnlogging"
24
+ level: int | str = logging.DEBUG
25
+ propagate: bool = False
26
+
27
+
28
+ def get_logging_logger(
29
+ config: LoggerConfig | None = None,
30
+ /,
31
+ *,
32
+ name: Omitable[str] = ...,
33
+ level: Omitable[int | str] = ...,
34
+ propagate: Omitable[bool] = ...,
35
+ ):
36
+ config_base = config or LoggerConfig()
37
+ config_evolved = evolve_(
38
+ config_base,
39
+ name=name,
40
+ level=level,
41
+ propagate=propagate,
42
+ )
43
+ logger = logging.getLogger(config_evolved.name)
44
+ logger.setLevel(config_evolved.level)
45
+ logger.propagate = config_evolved.propagate
46
+ return logger
47
+
48
+
49
+ @dataclass(slots=True, kw_only=True)
50
+ class RunConfig:
51
+ experiment: str | None = None
52
+ repo: str | AimRepo | None = None
53
+ system_tracking_interval: float | None = 10
54
+ capture_terminal_logs: bool = False
55
+ log_system_params: bool = False
56
+ run_hash: str | None = None
57
+ read_only: bool = False
58
+ force_resume: bool = False
59
+
60
+
61
+ def get_aim_run(
62
+ config: RunConfig | None = None,
63
+ /,
64
+ *,
65
+ experiment: Omitable[str | None] = ...,
66
+ repo: Omitable[str | AimRepo | None] = ...,
67
+ system_tracking_interval: Omitable[float | None] = ...,
68
+ capture_terminal_logs: Omitable[bool] = ...,
69
+ log_system_params: Omitable[bool] = ...,
70
+ run_hash: Omitable[str | None] = ...,
71
+ read_only: Omitable[bool] = ...,
72
+ force_resume: Omitable[bool] = ...,
73
+ ):
74
+ config_base = config or RunConfig()
75
+ config_evolved = evolve_(
76
+ config_base,
77
+ experiment=experiment,
78
+ repo=repo,
79
+ system_tracking_interval=system_tracking_interval,
80
+ capture_terminal_logs=capture_terminal_logs,
81
+ log_system_params=log_system_params,
82
+ run_hash=run_hash,
83
+ read_only=read_only,
84
+ force_resume=force_resume,
85
+ )
86
+ run = AimRun(**asdict(config_evolved)) # pyright: ignore[reportAny]
87
+ return run
88
+
89
+
90
+ @dataclass(slots=True, kw_only=True)
91
+ class BranchConfig:
92
+ # shared
93
+ markup: bool = True
94
+ highlighter: RichHighlighter | None = None
95
+
96
+ # console
97
+ width: int | None = None
98
+ height: int | None = None
99
+ emoji: bool = True
100
+ color_system: Literal["auto", "standard", "truecolor"] | None = "auto"
101
+ theme: RichTheme | None = None
102
+ soft_wrap: bool = True
103
+ force_terminal: bool | None = None
104
+ force_jupyter: bool | None = None
105
+ force_interactive: bool | None = None
106
+
107
+ # handler
108
+ level: str | int = logging.NOTSET
109
+ show_level: bool = True
110
+ show_time: bool = True
111
+ show_path: bool = True
112
+ log_time_format: str | FormatTimeCallable = "[%x %X]"
113
+ omit_repeated_times: bool = True
114
+ rich_tracebacks: bool = True
115
+ tracebacks_show_locals: bool = False
116
+ log_message_format: str | LoggingFormatter = "%(message)s"
117
+
118
+ # progress
119
+ columns: Collection[str | RichProgressColumn] = field(
120
+ default_factory=get_rich_progress_default_columns,
121
+ compare=False,
122
+ )
123
+ transient: bool = False
124
+ refresh_per_second: float = 10
125
+ speed_estimate_period: float = 3600
126
+ default_column_markup: bool = True
127
+
128
+
129
+ def get_branch(
130
+ config: BranchConfig | None = None,
131
+ /,
132
+ sink: Sink | None = None,
133
+ *,
134
+ markup: Omitable[bool] = ...,
135
+ highlighter: Omitable[RichHighlighter | None] = ...,
136
+ width: Omitable[int | None] = ...,
137
+ height: Omitable[int | None] = ...,
138
+ emoji: Omitable[bool] = ...,
139
+ color_system: Omitable[Literal["auto", "standard", "truecolor"] | None] = ...,
140
+ theme: Omitable[RichTheme | None] = ...,
141
+ soft_wrap: Omitable[bool] = ...,
142
+ force_terminal: Omitable[bool | None] = ...,
143
+ force_jupyter: Omitable[bool | None] = ...,
144
+ force_interactive: Omitable[bool | None] = ...,
145
+ level: Omitable[str | int] = ...,
146
+ show_level: Omitable[bool] = ...,
147
+ show_time: Omitable[bool] = ...,
148
+ show_path: Omitable[bool] = ...,
149
+ log_time_format: Omitable[str | FormatTimeCallable] = ...,
150
+ omit_repeated_times: Omitable[bool] = ...,
151
+ rich_tracebacks: Omitable[bool] = ...,
152
+ tracebacks_show_locals: Omitable[bool] = ...,
153
+ log_message_format: Omitable[str | LoggingFormatter] = ...,
154
+ ):
155
+ if config is None:
156
+ config = BranchConfig()
157
+ console = get_rich_console(
158
+ sink,
159
+ width=or_(width, config.width),
160
+ height=or_(height, config.height),
161
+ markup=or_(markup, config.markup),
162
+ emoji=or_(emoji, config.emoji),
163
+ color_system=or_(color_system, config.color_system),
164
+ theme=or_(theme, config.theme),
165
+ highlighter=or_(highlighter, config.highlighter),
166
+ soft_wrap=or_(soft_wrap, config.soft_wrap),
167
+ force_terminal=or_(force_terminal, config.force_terminal),
168
+ force_jupyter=or_(force_jupyter, config.force_jupyter),
169
+ force_interactive=or_(force_interactive, config.force_interactive),
170
+ )
171
+ handler = get_rich_handler(
172
+ console,
173
+ level=or_(level, config.level),
174
+ show_level=or_(show_level, config.show_level),
175
+ show_time=or_(show_time, config.show_time),
176
+ show_path=or_(show_path, config.show_path),
177
+ log_time_format=or_(log_time_format, config.log_time_format),
178
+ omit_repeated_times=or_(omit_repeated_times, config.omit_repeated_times),
179
+ markup=or_(markup, config.markup),
180
+ highlighter=or_(highlighter, config.highlighter),
181
+ rich_tracebacks=or_(rich_tracebacks, config.rich_tracebacks),
182
+ tracebacks_show_locals=or_(
183
+ tracebacks_show_locals, config.tracebacks_show_locals
184
+ ),
185
+ log_message_format=or_(log_message_format, config.log_message_format),
186
+ )
187
+ return Branch(
188
+ console=console,
189
+ handler=handler,
190
+ tasks=dict(),
191
+ progress=None,
192
+ )
@@ -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