logbar 0.2.4__tar.gz → 0.3.0__tar.gz
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.
- {logbar-0.2.4 → logbar-0.3.0}/LogBar.egg-info/PKG-INFO +139 -2
- {logbar-0.2.4 → logbar-0.3.0}/LogBar.egg-info/SOURCES.txt +4 -1
- {logbar-0.2.4 → logbar-0.3.0}/PKG-INFO +139 -2
- {logbar-0.2.4 → logbar-0.3.0}/README.md +138 -1
- {logbar-0.2.4 → logbar-0.3.0}/logbar/columns.py +2 -16
- logbar-0.3.0/logbar/drawing.py +353 -0
- {logbar-0.2.4 → logbar-0.3.0}/logbar/logbar.py +93 -7
- {logbar-0.2.4 → logbar-0.3.0}/logbar/progress.py +95 -128
- {logbar-0.2.4 → logbar-0.3.0}/pyproject.toml +1 -1
- logbar-0.3.0/tests/test_drawing.py +47 -0
- logbar-0.3.0/tests/test_log.py +429 -0
- {logbar-0.2.4 → logbar-0.3.0}/tests/test_progress.py +1 -1
- logbar-0.3.0/tests/test_snake_render.py +447 -0
- logbar-0.2.4/tests/test_log.py +0 -205
- {logbar-0.2.4 → logbar-0.3.0}/LICENSE +0 -0
- {logbar-0.2.4 → logbar-0.3.0}/LogBar.egg-info/dependency_links.txt +0 -0
- {logbar-0.2.4 → logbar-0.3.0}/LogBar.egg-info/top_level.txt +0 -0
- {logbar-0.2.4 → logbar-0.3.0}/logbar/__init__.py +0 -0
- {logbar-0.2.4 → logbar-0.3.0}/logbar/buffer.py +0 -0
- {logbar-0.2.4 → logbar-0.3.0}/logbar/terminal.py +0 -0
- {logbar-0.2.4 → logbar-0.3.0}/logbar/util.py +0 -0
- {logbar-0.2.4 → logbar-0.3.0}/setup.cfg +0 -0
- {logbar-0.2.4 → logbar-0.3.0}/tests/test_columns.py +0 -0
- {logbar-0.2.4 → logbar-0.3.0}/tests/test_example.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: LogBar
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: A unified Logger and ProgressBar util with zero dependencies.
|
|
5
5
|
Author-email: ModelCloud <qubitium@modelcloud.ai>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -36,6 +36,7 @@ Dynamic: license-file
|
|
|
36
36
|
- Shared singleton logger with per-level colorized output.
|
|
37
37
|
- `once` helpers prevent duplicate log spam automatically.
|
|
38
38
|
- Stackable progress bars that stay anchored while your logs flow freely.
|
|
39
|
+
- Sub-cell Unicode bar rasterization for smoother, more accurate terminal fills.
|
|
39
40
|
- Built-in styling for progress bar fills, colors, gradients, and head glyphs.
|
|
40
41
|
- Animated progress titles with a subtle sweeping highlight.
|
|
41
42
|
Set `LOGBAR_ANIMATION=0` to disable the highlight animation.
|
|
@@ -52,6 +53,46 @@ pip install logbar
|
|
|
52
53
|
|
|
53
54
|
LogBar works out-of-the-box with CPython 3.8+ on Linux, macOS, and Windows terminals.
|
|
54
55
|
|
|
56
|
+
## Renderer Design
|
|
57
|
+
|
|
58
|
+
LogBar keeps progress bars, spinners, tables, and normal log lines readable in the same terminal session. Compared with traditional loggers, it lets long-running CLI programs show live status without flooding the screen with repeated status lines or breaking the flow of regular logs.
|
|
59
|
+
|
|
60
|
+
Main rendering APIs:
|
|
61
|
+
|
|
62
|
+
- `log.pb(...)` for live progress bars
|
|
63
|
+
- `log.spinner(...)` for work with no fixed total
|
|
64
|
+
- `log.columns(...)` for aligned table output
|
|
65
|
+
|
|
66
|
+
Examples:
|
|
67
|
+
|
|
68
|
+
```py
|
|
69
|
+
from logbar import LogBar
|
|
70
|
+
|
|
71
|
+
log = LogBar.shared()
|
|
72
|
+
|
|
73
|
+
for _ in log.pb(range(5)).title("下载 📦").subtitle("phase 1"):
|
|
74
|
+
pass
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
```py
|
|
78
|
+
jobs = ["scan", "parse", "index", "flush"]
|
|
79
|
+
pb = log.pb(jobs, output_interval=1).title("Indexing").manual()
|
|
80
|
+
for job in pb:
|
|
81
|
+
log.info("processing %s", job)
|
|
82
|
+
pb.subtitle(job).draw()
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
```py
|
|
86
|
+
cols = log.columns(
|
|
87
|
+
{"label": "task", "width": "fit"},
|
|
88
|
+
{"label": "status", "width": "fit"},
|
|
89
|
+
{"label": "detail", "width": "50%"},
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
cols.info.header()
|
|
93
|
+
cols.info("render", "active", "width and alignment stay terminal-aware")
|
|
94
|
+
```
|
|
95
|
+
|
|
55
96
|
# Quick Start
|
|
56
97
|
|
|
57
98
|
```py
|
|
@@ -199,6 +240,28 @@ pb.head('>', color='82') # custom head glyph + color index
|
|
|
199
240
|
|
|
200
241
|
`ProgressBar.available_styles()` lists builtin styles, and you can register additional ones with `ProgressBar.register_style(...)` or switch defaults globally via `ProgressBar.set_default_style(...)`. Custom colors accept ANSI escape codes, 256-color indexes (e.g. `'82'`), or hex strings (`'#4c1d95'`).
|
|
201
242
|
|
|
243
|
+
For direct style registration and introspection, import the advanced style APIs from `logbar.progress`:
|
|
244
|
+
|
|
245
|
+
```py
|
|
246
|
+
from logbar.progress import ProgressBar, ProgressStyle, progress_style_names
|
|
247
|
+
|
|
248
|
+
print(ProgressBar.available_styles())
|
|
249
|
+
print(progress_style_names())
|
|
250
|
+
|
|
251
|
+
ProgressBar.register_style(
|
|
252
|
+
ProgressStyle(
|
|
253
|
+
name="ice",
|
|
254
|
+
fill_char="■",
|
|
255
|
+
empty_char="·",
|
|
256
|
+
fill_colors=("#7dd3fc", "#38bdf8"),
|
|
257
|
+
gradient=True,
|
|
258
|
+
head_char=">",
|
|
259
|
+
)
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
ProgressBar.set_default_style("ice")
|
|
263
|
+
```
|
|
264
|
+
|
|
202
265
|
Styled output (plain-text view with ANSI removed):
|
|
203
266
|
|
|
204
267
|
```
|
|
@@ -248,6 +311,14 @@ cols.update({
|
|
|
248
311
|
})
|
|
249
312
|
```
|
|
250
313
|
|
|
314
|
+
Useful column helpers:
|
|
315
|
+
|
|
316
|
+
- `cols.info.header()` or `cols.info.headers()` prints the current border + header block.
|
|
317
|
+
- `cols.info.simulate(...)` recomputes widths without emitting a row.
|
|
318
|
+
- `cols.update(...)` changes labels, spans, or widths at runtime.
|
|
319
|
+
- `cols.width()` returns the current rendered table width, including borders.
|
|
320
|
+
- `cols.widths`, `cols.padding`, and `cols.column_specs` expose the current layout.
|
|
321
|
+
|
|
251
322
|
# Replacing `tqdm`
|
|
252
323
|
|
|
253
324
|
The API mirrors common `tqdm` patterns while staying more Pythonic:
|
|
@@ -275,7 +346,7 @@ with tqdm.tqdm(total=len(items)) as pb:
|
|
|
275
346
|
with log.pb(items).manual() as pb:
|
|
276
347
|
for item in pb:
|
|
277
348
|
handle(item)
|
|
278
|
-
pb.
|
|
349
|
+
pb.draw()
|
|
279
350
|
```
|
|
280
351
|
|
|
281
352
|
# Advanced Tips
|
|
@@ -283,3 +354,69 @@ with log.pb(items).manual() as pb:
|
|
|
283
354
|
- Combine columns and progress bars by logging summaries at key checkpoints.
|
|
284
355
|
- Use `log.warn.once(...)` to keep noisy health checks readable.
|
|
285
356
|
- For multi-line messages, pre-format text and pass it as a single string; LogBar keeps borders intact.
|
|
357
|
+
|
|
358
|
+
# API Reference
|
|
359
|
+
|
|
360
|
+
## `LogBar`
|
|
361
|
+
|
|
362
|
+
- `LogBar.shared(override_logger=False)` returns the process-wide shared logger.
|
|
363
|
+
- `override_logger=True` is useful in tests or embedded environments that replaced the active `logging` logger class.
|
|
364
|
+
- Level methods: `debug`, `info`, `warn`, `error`, `critical`.
|
|
365
|
+
- Deduplicated level methods: `debug.once`, `info.once`, `warn.once`, `error.once`, `critical.once`.
|
|
366
|
+
- `setLevel(level)` accepts strings like `"INFO"`, `"WARN"`, `"CRIT"`, numeric levels, numeric strings, and constants such as `LogBar.WARNING`.
|
|
367
|
+
- `pb(iterable_or_total, output_interval=None)` creates and attaches a progress bar.
|
|
368
|
+
- `spinner(title="", interval=0.5, tail_length=4)` creates and attaches an indeterminate rolling progress bar.
|
|
369
|
+
- `columns(..., cols=None, width=None, padding=2)` creates a column printer.
|
|
370
|
+
|
|
371
|
+
## `ProgressBar`
|
|
372
|
+
|
|
373
|
+
`log.pb(...)` returns an attached `ProgressBar`. For direct imports, use:
|
|
374
|
+
|
|
375
|
+
```py
|
|
376
|
+
from logbar.progress import ProgressBar, ProgressStyle
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
Common chainable methods:
|
|
380
|
+
|
|
381
|
+
- `title(text)` and `subtitle(text)`
|
|
382
|
+
- `style(name_or_style)`
|
|
383
|
+
- `fill(fill_char, empty=None)`
|
|
384
|
+
- `colors(fill=None, empty=None, gradient=None, head=None)`
|
|
385
|
+
- `head(char=None, color=None)`
|
|
386
|
+
- `set(show_left_steps=None, left_steps_offset=None)`
|
|
387
|
+
- `output_interval(interval)`
|
|
388
|
+
- `mode(RenderMode)` if you prefer explicit mode switching over `auto()` / `manual()`
|
|
389
|
+
|
|
390
|
+
Render and lifecycle control:
|
|
391
|
+
|
|
392
|
+
- `draw(force=False)` renders the current snapshot immediately.
|
|
393
|
+
- `auto()` enables redraw-on-iteration mode.
|
|
394
|
+
- `manual()` disables automatic redraw so you can call `draw()` yourself.
|
|
395
|
+
- `attach(logger=None)` attaches the bar to a logger.
|
|
396
|
+
- `detach()` detaches the bar without destroying the object.
|
|
397
|
+
- `close()` forces a final render if needed and removes the bar from the stack.
|
|
398
|
+
- `step()` returns the current iteration index and `next()` advances once outside a `for` loop.
|
|
399
|
+
|
|
400
|
+
Style registry helpers:
|
|
401
|
+
|
|
402
|
+
- `ProgressBar.available_styles()`
|
|
403
|
+
- `ProgressBar.register_style(style)`
|
|
404
|
+
- `ProgressBar.set_default_style(style)`
|
|
405
|
+
- `ProgressBar.default_style()`
|
|
406
|
+
|
|
407
|
+
## `RollingProgressBar`
|
|
408
|
+
|
|
409
|
+
`log.spinner(...)` returns a `RollingProgressBar`, which inherits from `ProgressBar` and adds:
|
|
410
|
+
|
|
411
|
+
- `pulse()` to advance the spinner immediately between automatic ticks.
|
|
412
|
+
- `interval` and `tail_length` constructor arguments for animation speed and tail size.
|
|
413
|
+
|
|
414
|
+
## `ColumnsPrinter`
|
|
415
|
+
|
|
416
|
+
`log.columns(...)` returns a `ColumnsPrinter` with per-level proxies:
|
|
417
|
+
|
|
418
|
+
- `cols.info(...)`, `cols.warn(...)`, `cols.error(...)`, `cols.debug(...)`, `cols.critical(...)`
|
|
419
|
+
- `cols.info.header()` and `cols.info.headers()` for border + header emission
|
|
420
|
+
- `cols.info.simulate(...)` for dry-run width growth without output
|
|
421
|
+
- `cols.update(...)` for runtime schema changes
|
|
422
|
+
- `cols.width()` for the current rendered width
|
|
@@ -8,11 +8,14 @@ LogBar.egg-info/top_level.txt
|
|
|
8
8
|
logbar/__init__.py
|
|
9
9
|
logbar/buffer.py
|
|
10
10
|
logbar/columns.py
|
|
11
|
+
logbar/drawing.py
|
|
11
12
|
logbar/logbar.py
|
|
12
13
|
logbar/progress.py
|
|
13
14
|
logbar/terminal.py
|
|
14
15
|
logbar/util.py
|
|
15
16
|
tests/test_columns.py
|
|
17
|
+
tests/test_drawing.py
|
|
16
18
|
tests/test_example.py
|
|
17
19
|
tests/test_log.py
|
|
18
|
-
tests/test_progress.py
|
|
20
|
+
tests/test_progress.py
|
|
21
|
+
tests/test_snake_render.py
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: LogBar
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: A unified Logger and ProgressBar util with zero dependencies.
|
|
5
5
|
Author-email: ModelCloud <qubitium@modelcloud.ai>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -36,6 +36,7 @@ Dynamic: license-file
|
|
|
36
36
|
- Shared singleton logger with per-level colorized output.
|
|
37
37
|
- `once` helpers prevent duplicate log spam automatically.
|
|
38
38
|
- Stackable progress bars that stay anchored while your logs flow freely.
|
|
39
|
+
- Sub-cell Unicode bar rasterization for smoother, more accurate terminal fills.
|
|
39
40
|
- Built-in styling for progress bar fills, colors, gradients, and head glyphs.
|
|
40
41
|
- Animated progress titles with a subtle sweeping highlight.
|
|
41
42
|
Set `LOGBAR_ANIMATION=0` to disable the highlight animation.
|
|
@@ -52,6 +53,46 @@ pip install logbar
|
|
|
52
53
|
|
|
53
54
|
LogBar works out-of-the-box with CPython 3.8+ on Linux, macOS, and Windows terminals.
|
|
54
55
|
|
|
56
|
+
## Renderer Design
|
|
57
|
+
|
|
58
|
+
LogBar keeps progress bars, spinners, tables, and normal log lines readable in the same terminal session. Compared with traditional loggers, it lets long-running CLI programs show live status without flooding the screen with repeated status lines or breaking the flow of regular logs.
|
|
59
|
+
|
|
60
|
+
Main rendering APIs:
|
|
61
|
+
|
|
62
|
+
- `log.pb(...)` for live progress bars
|
|
63
|
+
- `log.spinner(...)` for work with no fixed total
|
|
64
|
+
- `log.columns(...)` for aligned table output
|
|
65
|
+
|
|
66
|
+
Examples:
|
|
67
|
+
|
|
68
|
+
```py
|
|
69
|
+
from logbar import LogBar
|
|
70
|
+
|
|
71
|
+
log = LogBar.shared()
|
|
72
|
+
|
|
73
|
+
for _ in log.pb(range(5)).title("下载 📦").subtitle("phase 1"):
|
|
74
|
+
pass
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
```py
|
|
78
|
+
jobs = ["scan", "parse", "index", "flush"]
|
|
79
|
+
pb = log.pb(jobs, output_interval=1).title("Indexing").manual()
|
|
80
|
+
for job in pb:
|
|
81
|
+
log.info("processing %s", job)
|
|
82
|
+
pb.subtitle(job).draw()
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
```py
|
|
86
|
+
cols = log.columns(
|
|
87
|
+
{"label": "task", "width": "fit"},
|
|
88
|
+
{"label": "status", "width": "fit"},
|
|
89
|
+
{"label": "detail", "width": "50%"},
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
cols.info.header()
|
|
93
|
+
cols.info("render", "active", "width and alignment stay terminal-aware")
|
|
94
|
+
```
|
|
95
|
+
|
|
55
96
|
# Quick Start
|
|
56
97
|
|
|
57
98
|
```py
|
|
@@ -199,6 +240,28 @@ pb.head('>', color='82') # custom head glyph + color index
|
|
|
199
240
|
|
|
200
241
|
`ProgressBar.available_styles()` lists builtin styles, and you can register additional ones with `ProgressBar.register_style(...)` or switch defaults globally via `ProgressBar.set_default_style(...)`. Custom colors accept ANSI escape codes, 256-color indexes (e.g. `'82'`), or hex strings (`'#4c1d95'`).
|
|
201
242
|
|
|
243
|
+
For direct style registration and introspection, import the advanced style APIs from `logbar.progress`:
|
|
244
|
+
|
|
245
|
+
```py
|
|
246
|
+
from logbar.progress import ProgressBar, ProgressStyle, progress_style_names
|
|
247
|
+
|
|
248
|
+
print(ProgressBar.available_styles())
|
|
249
|
+
print(progress_style_names())
|
|
250
|
+
|
|
251
|
+
ProgressBar.register_style(
|
|
252
|
+
ProgressStyle(
|
|
253
|
+
name="ice",
|
|
254
|
+
fill_char="■",
|
|
255
|
+
empty_char="·",
|
|
256
|
+
fill_colors=("#7dd3fc", "#38bdf8"),
|
|
257
|
+
gradient=True,
|
|
258
|
+
head_char=">",
|
|
259
|
+
)
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
ProgressBar.set_default_style("ice")
|
|
263
|
+
```
|
|
264
|
+
|
|
202
265
|
Styled output (plain-text view with ANSI removed):
|
|
203
266
|
|
|
204
267
|
```
|
|
@@ -248,6 +311,14 @@ cols.update({
|
|
|
248
311
|
})
|
|
249
312
|
```
|
|
250
313
|
|
|
314
|
+
Useful column helpers:
|
|
315
|
+
|
|
316
|
+
- `cols.info.header()` or `cols.info.headers()` prints the current border + header block.
|
|
317
|
+
- `cols.info.simulate(...)` recomputes widths without emitting a row.
|
|
318
|
+
- `cols.update(...)` changes labels, spans, or widths at runtime.
|
|
319
|
+
- `cols.width()` returns the current rendered table width, including borders.
|
|
320
|
+
- `cols.widths`, `cols.padding`, and `cols.column_specs` expose the current layout.
|
|
321
|
+
|
|
251
322
|
# Replacing `tqdm`
|
|
252
323
|
|
|
253
324
|
The API mirrors common `tqdm` patterns while staying more Pythonic:
|
|
@@ -275,7 +346,7 @@ with tqdm.tqdm(total=len(items)) as pb:
|
|
|
275
346
|
with log.pb(items).manual() as pb:
|
|
276
347
|
for item in pb:
|
|
277
348
|
handle(item)
|
|
278
|
-
pb.
|
|
349
|
+
pb.draw()
|
|
279
350
|
```
|
|
280
351
|
|
|
281
352
|
# Advanced Tips
|
|
@@ -283,3 +354,69 @@ with log.pb(items).manual() as pb:
|
|
|
283
354
|
- Combine columns and progress bars by logging summaries at key checkpoints.
|
|
284
355
|
- Use `log.warn.once(...)` to keep noisy health checks readable.
|
|
285
356
|
- For multi-line messages, pre-format text and pass it as a single string; LogBar keeps borders intact.
|
|
357
|
+
|
|
358
|
+
# API Reference
|
|
359
|
+
|
|
360
|
+
## `LogBar`
|
|
361
|
+
|
|
362
|
+
- `LogBar.shared(override_logger=False)` returns the process-wide shared logger.
|
|
363
|
+
- `override_logger=True` is useful in tests or embedded environments that replaced the active `logging` logger class.
|
|
364
|
+
- Level methods: `debug`, `info`, `warn`, `error`, `critical`.
|
|
365
|
+
- Deduplicated level methods: `debug.once`, `info.once`, `warn.once`, `error.once`, `critical.once`.
|
|
366
|
+
- `setLevel(level)` accepts strings like `"INFO"`, `"WARN"`, `"CRIT"`, numeric levels, numeric strings, and constants such as `LogBar.WARNING`.
|
|
367
|
+
- `pb(iterable_or_total, output_interval=None)` creates and attaches a progress bar.
|
|
368
|
+
- `spinner(title="", interval=0.5, tail_length=4)` creates and attaches an indeterminate rolling progress bar.
|
|
369
|
+
- `columns(..., cols=None, width=None, padding=2)` creates a column printer.
|
|
370
|
+
|
|
371
|
+
## `ProgressBar`
|
|
372
|
+
|
|
373
|
+
`log.pb(...)` returns an attached `ProgressBar`. For direct imports, use:
|
|
374
|
+
|
|
375
|
+
```py
|
|
376
|
+
from logbar.progress import ProgressBar, ProgressStyle
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
Common chainable methods:
|
|
380
|
+
|
|
381
|
+
- `title(text)` and `subtitle(text)`
|
|
382
|
+
- `style(name_or_style)`
|
|
383
|
+
- `fill(fill_char, empty=None)`
|
|
384
|
+
- `colors(fill=None, empty=None, gradient=None, head=None)`
|
|
385
|
+
- `head(char=None, color=None)`
|
|
386
|
+
- `set(show_left_steps=None, left_steps_offset=None)`
|
|
387
|
+
- `output_interval(interval)`
|
|
388
|
+
- `mode(RenderMode)` if you prefer explicit mode switching over `auto()` / `manual()`
|
|
389
|
+
|
|
390
|
+
Render and lifecycle control:
|
|
391
|
+
|
|
392
|
+
- `draw(force=False)` renders the current snapshot immediately.
|
|
393
|
+
- `auto()` enables redraw-on-iteration mode.
|
|
394
|
+
- `manual()` disables automatic redraw so you can call `draw()` yourself.
|
|
395
|
+
- `attach(logger=None)` attaches the bar to a logger.
|
|
396
|
+
- `detach()` detaches the bar without destroying the object.
|
|
397
|
+
- `close()` forces a final render if needed and removes the bar from the stack.
|
|
398
|
+
- `step()` returns the current iteration index and `next()` advances once outside a `for` loop.
|
|
399
|
+
|
|
400
|
+
Style registry helpers:
|
|
401
|
+
|
|
402
|
+
- `ProgressBar.available_styles()`
|
|
403
|
+
- `ProgressBar.register_style(style)`
|
|
404
|
+
- `ProgressBar.set_default_style(style)`
|
|
405
|
+
- `ProgressBar.default_style()`
|
|
406
|
+
|
|
407
|
+
## `RollingProgressBar`
|
|
408
|
+
|
|
409
|
+
`log.spinner(...)` returns a `RollingProgressBar`, which inherits from `ProgressBar` and adds:
|
|
410
|
+
|
|
411
|
+
- `pulse()` to advance the spinner immediately between automatic ticks.
|
|
412
|
+
- `interval` and `tail_length` constructor arguments for animation speed and tail size.
|
|
413
|
+
|
|
414
|
+
## `ColumnsPrinter`
|
|
415
|
+
|
|
416
|
+
`log.columns(...)` returns a `ColumnsPrinter` with per-level proxies:
|
|
417
|
+
|
|
418
|
+
- `cols.info(...)`, `cols.warn(...)`, `cols.error(...)`, `cols.debug(...)`, `cols.critical(...)`
|
|
419
|
+
- `cols.info.header()` and `cols.info.headers()` for border + header emission
|
|
420
|
+
- `cols.info.simulate(...)` for dry-run width growth without output
|
|
421
|
+
- `cols.update(...)` for runtime schema changes
|
|
422
|
+
- `cols.width()` for the current rendered width
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
- Shared singleton logger with per-level colorized output.
|
|
22
22
|
- `once` helpers prevent duplicate log spam automatically.
|
|
23
23
|
- Stackable progress bars that stay anchored while your logs flow freely.
|
|
24
|
+
- Sub-cell Unicode bar rasterization for smoother, more accurate terminal fills.
|
|
24
25
|
- Built-in styling for progress bar fills, colors, gradients, and head glyphs.
|
|
25
26
|
- Animated progress titles with a subtle sweeping highlight.
|
|
26
27
|
Set `LOGBAR_ANIMATION=0` to disable the highlight animation.
|
|
@@ -37,6 +38,46 @@ pip install logbar
|
|
|
37
38
|
|
|
38
39
|
LogBar works out-of-the-box with CPython 3.8+ on Linux, macOS, and Windows terminals.
|
|
39
40
|
|
|
41
|
+
## Renderer Design
|
|
42
|
+
|
|
43
|
+
LogBar keeps progress bars, spinners, tables, and normal log lines readable in the same terminal session. Compared with traditional loggers, it lets long-running CLI programs show live status without flooding the screen with repeated status lines or breaking the flow of regular logs.
|
|
44
|
+
|
|
45
|
+
Main rendering APIs:
|
|
46
|
+
|
|
47
|
+
- `log.pb(...)` for live progress bars
|
|
48
|
+
- `log.spinner(...)` for work with no fixed total
|
|
49
|
+
- `log.columns(...)` for aligned table output
|
|
50
|
+
|
|
51
|
+
Examples:
|
|
52
|
+
|
|
53
|
+
```py
|
|
54
|
+
from logbar import LogBar
|
|
55
|
+
|
|
56
|
+
log = LogBar.shared()
|
|
57
|
+
|
|
58
|
+
for _ in log.pb(range(5)).title("下载 📦").subtitle("phase 1"):
|
|
59
|
+
pass
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
```py
|
|
63
|
+
jobs = ["scan", "parse", "index", "flush"]
|
|
64
|
+
pb = log.pb(jobs, output_interval=1).title("Indexing").manual()
|
|
65
|
+
for job in pb:
|
|
66
|
+
log.info("processing %s", job)
|
|
67
|
+
pb.subtitle(job).draw()
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
```py
|
|
71
|
+
cols = log.columns(
|
|
72
|
+
{"label": "task", "width": "fit"},
|
|
73
|
+
{"label": "status", "width": "fit"},
|
|
74
|
+
{"label": "detail", "width": "50%"},
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
cols.info.header()
|
|
78
|
+
cols.info("render", "active", "width and alignment stay terminal-aware")
|
|
79
|
+
```
|
|
80
|
+
|
|
40
81
|
# Quick Start
|
|
41
82
|
|
|
42
83
|
```py
|
|
@@ -184,6 +225,28 @@ pb.head('>', color='82') # custom head glyph + color index
|
|
|
184
225
|
|
|
185
226
|
`ProgressBar.available_styles()` lists builtin styles, and you can register additional ones with `ProgressBar.register_style(...)` or switch defaults globally via `ProgressBar.set_default_style(...)`. Custom colors accept ANSI escape codes, 256-color indexes (e.g. `'82'`), or hex strings (`'#4c1d95'`).
|
|
186
227
|
|
|
228
|
+
For direct style registration and introspection, import the advanced style APIs from `logbar.progress`:
|
|
229
|
+
|
|
230
|
+
```py
|
|
231
|
+
from logbar.progress import ProgressBar, ProgressStyle, progress_style_names
|
|
232
|
+
|
|
233
|
+
print(ProgressBar.available_styles())
|
|
234
|
+
print(progress_style_names())
|
|
235
|
+
|
|
236
|
+
ProgressBar.register_style(
|
|
237
|
+
ProgressStyle(
|
|
238
|
+
name="ice",
|
|
239
|
+
fill_char="■",
|
|
240
|
+
empty_char="·",
|
|
241
|
+
fill_colors=("#7dd3fc", "#38bdf8"),
|
|
242
|
+
gradient=True,
|
|
243
|
+
head_char=">",
|
|
244
|
+
)
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
ProgressBar.set_default_style("ice")
|
|
248
|
+
```
|
|
249
|
+
|
|
187
250
|
Styled output (plain-text view with ANSI removed):
|
|
188
251
|
|
|
189
252
|
```
|
|
@@ -233,6 +296,14 @@ cols.update({
|
|
|
233
296
|
})
|
|
234
297
|
```
|
|
235
298
|
|
|
299
|
+
Useful column helpers:
|
|
300
|
+
|
|
301
|
+
- `cols.info.header()` or `cols.info.headers()` prints the current border + header block.
|
|
302
|
+
- `cols.info.simulate(...)` recomputes widths without emitting a row.
|
|
303
|
+
- `cols.update(...)` changes labels, spans, or widths at runtime.
|
|
304
|
+
- `cols.width()` returns the current rendered table width, including borders.
|
|
305
|
+
- `cols.widths`, `cols.padding`, and `cols.column_specs` expose the current layout.
|
|
306
|
+
|
|
236
307
|
# Replacing `tqdm`
|
|
237
308
|
|
|
238
309
|
The API mirrors common `tqdm` patterns while staying more Pythonic:
|
|
@@ -260,7 +331,7 @@ with tqdm.tqdm(total=len(items)) as pb:
|
|
|
260
331
|
with log.pb(items).manual() as pb:
|
|
261
332
|
for item in pb:
|
|
262
333
|
handle(item)
|
|
263
|
-
pb.
|
|
334
|
+
pb.draw()
|
|
264
335
|
```
|
|
265
336
|
|
|
266
337
|
# Advanced Tips
|
|
@@ -268,3 +339,69 @@ with log.pb(items).manual() as pb:
|
|
|
268
339
|
- Combine columns and progress bars by logging summaries at key checkpoints.
|
|
269
340
|
- Use `log.warn.once(...)` to keep noisy health checks readable.
|
|
270
341
|
- For multi-line messages, pre-format text and pass it as a single string; LogBar keeps borders intact.
|
|
342
|
+
|
|
343
|
+
# API Reference
|
|
344
|
+
|
|
345
|
+
## `LogBar`
|
|
346
|
+
|
|
347
|
+
- `LogBar.shared(override_logger=False)` returns the process-wide shared logger.
|
|
348
|
+
- `override_logger=True` is useful in tests or embedded environments that replaced the active `logging` logger class.
|
|
349
|
+
- Level methods: `debug`, `info`, `warn`, `error`, `critical`.
|
|
350
|
+
- Deduplicated level methods: `debug.once`, `info.once`, `warn.once`, `error.once`, `critical.once`.
|
|
351
|
+
- `setLevel(level)` accepts strings like `"INFO"`, `"WARN"`, `"CRIT"`, numeric levels, numeric strings, and constants such as `LogBar.WARNING`.
|
|
352
|
+
- `pb(iterable_or_total, output_interval=None)` creates and attaches a progress bar.
|
|
353
|
+
- `spinner(title="", interval=0.5, tail_length=4)` creates and attaches an indeterminate rolling progress bar.
|
|
354
|
+
- `columns(..., cols=None, width=None, padding=2)` creates a column printer.
|
|
355
|
+
|
|
356
|
+
## `ProgressBar`
|
|
357
|
+
|
|
358
|
+
`log.pb(...)` returns an attached `ProgressBar`. For direct imports, use:
|
|
359
|
+
|
|
360
|
+
```py
|
|
361
|
+
from logbar.progress import ProgressBar, ProgressStyle
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
Common chainable methods:
|
|
365
|
+
|
|
366
|
+
- `title(text)` and `subtitle(text)`
|
|
367
|
+
- `style(name_or_style)`
|
|
368
|
+
- `fill(fill_char, empty=None)`
|
|
369
|
+
- `colors(fill=None, empty=None, gradient=None, head=None)`
|
|
370
|
+
- `head(char=None, color=None)`
|
|
371
|
+
- `set(show_left_steps=None, left_steps_offset=None)`
|
|
372
|
+
- `output_interval(interval)`
|
|
373
|
+
- `mode(RenderMode)` if you prefer explicit mode switching over `auto()` / `manual()`
|
|
374
|
+
|
|
375
|
+
Render and lifecycle control:
|
|
376
|
+
|
|
377
|
+
- `draw(force=False)` renders the current snapshot immediately.
|
|
378
|
+
- `auto()` enables redraw-on-iteration mode.
|
|
379
|
+
- `manual()` disables automatic redraw so you can call `draw()` yourself.
|
|
380
|
+
- `attach(logger=None)` attaches the bar to a logger.
|
|
381
|
+
- `detach()` detaches the bar without destroying the object.
|
|
382
|
+
- `close()` forces a final render if needed and removes the bar from the stack.
|
|
383
|
+
- `step()` returns the current iteration index and `next()` advances once outside a `for` loop.
|
|
384
|
+
|
|
385
|
+
Style registry helpers:
|
|
386
|
+
|
|
387
|
+
- `ProgressBar.available_styles()`
|
|
388
|
+
- `ProgressBar.register_style(style)`
|
|
389
|
+
- `ProgressBar.set_default_style(style)`
|
|
390
|
+
- `ProgressBar.default_style()`
|
|
391
|
+
|
|
392
|
+
## `RollingProgressBar`
|
|
393
|
+
|
|
394
|
+
`log.spinner(...)` returns a `RollingProgressBar`, which inherits from `ProgressBar` and adds:
|
|
395
|
+
|
|
396
|
+
- `pulse()` to advance the spinner immediately between automatic ticks.
|
|
397
|
+
- `interval` and `tail_length` constructor arguments for animation speed and tail size.
|
|
398
|
+
|
|
399
|
+
## `ColumnsPrinter`
|
|
400
|
+
|
|
401
|
+
`log.columns(...)` returns a `ColumnsPrinter` with per-level proxies:
|
|
402
|
+
|
|
403
|
+
- `cols.info(...)`, `cols.warn(...)`, `cols.error(...)`, `cols.debug(...)`, `cols.critical(...)`
|
|
404
|
+
- `cols.info.header()` and `cols.info.headers()` for border + header emission
|
|
405
|
+
- `cols.info.simulate(...)` for dry-run width growth without output
|
|
406
|
+
- `cols.update(...)` for runtime schema changes
|
|
407
|
+
- `cols.width()` for the current rendered width
|
|
@@ -5,28 +5,14 @@
|
|
|
5
5
|
|
|
6
6
|
"""Column layout helpers for LogBar output."""
|
|
7
7
|
|
|
8
|
-
import re
|
|
9
8
|
from dataclasses import dataclass
|
|
10
9
|
from typing import Any, Callable, Dict, Iterable, List, Optional, Sequence, Tuple, Union
|
|
11
10
|
|
|
11
|
+
from .drawing import strip_ansi as _strip_ansi
|
|
12
|
+
from .drawing import visible_length as _visible_length
|
|
12
13
|
from .terminal import terminal_size
|
|
13
14
|
|
|
14
15
|
|
|
15
|
-
ANSI_ESCAPE_RE = re.compile(r"\x1B\[[0-9;?]*[ -/]*[@-~]")
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def _strip_ansi(text: str) -> str:
|
|
19
|
-
return ANSI_ESCAPE_RE.sub("", text)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def _visible_length(text: str) -> int:
|
|
23
|
-
if not text:
|
|
24
|
-
return 0
|
|
25
|
-
cleaned = _strip_ansi(text)
|
|
26
|
-
cleaned = cleaned.replace("\r", "").replace("\n", "")
|
|
27
|
-
return len(cleaned)
|
|
28
|
-
|
|
29
|
-
|
|
30
16
|
def _pad_visible(text: str, target: int) -> str:
|
|
31
17
|
current = _visible_length(text)
|
|
32
18
|
if current >= target:
|