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,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.