๐Ÿšจ Shai-Hulud Strikes Again:834 Packages Compromised.Technical Analysis โ†’
Socket
Book a DemoInstallSign in
Socket

bprogress

Package Overview
Dependencies
Maintainers
1
Versions
38
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

bprogress

Braille-based multi progress bars with ANSI colors (docker-compose style)

pipPyPI
Version
1.0.0
Maintainers
1

braille-progress

image

์ ์ž ๋ฌธ์ž๋ฅผ ์ด์šฉํ•œ ํ”„๋กœ๊ทธ๋ ˆ์Šค ๋ณด๋“œ. ์ž์œ ๋กœ์šด ๋ ˆ์ด์•„์›ƒ ์ปค์Šคํ„ฐ๋งˆ์ด์ฆˆ, ํƒœ์Šคํฌ๋ณ„ ๋กœ๊ทธ ํŒจ๋„, ํ…Œ์Šคํฌ ์—๋Ÿฌ ๋ฆฌํฌํŠธ, Windows/Linux/macOS ํ„ฐ๋ฏธ๋„ ๋ณต๊ตฌ๊นŒ์ง€ ์ง€์›.

ํŠน์ง•

  • ์ค„๋ฐ”๊ฟˆ/๋ฐ€๋ฆผ ์—†๋Š” ์•ˆ์ • ๋ Œ๋”๋ง(๊ฐ€์‹œํญ ์ปท, ๊ฐ€๋“œ ์ปฌ๋Ÿผ), Windows VT I/O ์ง€์›, ์ข…๋ฃŒ ์‹œ ์ž๋™ ๋ณต๊ตฌ(์‹œ๊ทธ๋„+atexit), ๋งˆ์šฐ์Šค ๋ชจ๋“œ ํ•ด์ œ ๋ฐ ์ž…๋ ฅ ๋ฒ„ํผ ํ”Œ๋Ÿฌ์‹œ.
  • ์ขŒ์šฐ ๋ถ„ํ• : ์™ผ์ชฝ ๋ฆฌ์ŠคํŠธ(ํƒœ์Šคํฌ), ์˜ค๋ฅธ์ชฝ ํŒจ๋„(๊ธฐ๋ณธ: ๋กœ๊ทธ) + ์„ธ๋กœ ๊ตฌ๋ถ„์„ .
  • ์ธํ„ฐ๋ž™์…˜: ๋งˆ์šฐ์Šค ํด๋ฆญ, ํ‚ค๋ณด๋“œ w/s ๋˜๋Š” โ†‘/โ†“, Home/End. (Linux๊ณ„์—ด ํ™˜๊ฒฝ ํ•œ์ •)
  • ํ–‰(Row) ๋ ˆ์ด์•„์›ƒ ์ž์œ  ๊ตฌ์„ฑ: ์ด๋ฆ„, ๋ฉ”์ธ/์„œ๋ธŒ ๋ฐ”, ํผ์„ผํŠธ, ์ƒํƒœ, ์นด์šดํ„ฐ, ๋ผ๋ฒจ, ๊ฒฝ๊ณผ์‹œ๊ฐ„, ํ‰๊ท  ์ฒ˜๋ฆฌ์†๋„, ETA, ์ž„์˜ ํ…์ŠคํŠธ ๋“ฑ.
  • ํ—ค๋”/ํ‘ธํ„ฐ ์ˆ˜์ง ๋ ˆ์ด์•„์›ƒ.
  • ์˜ค๋ฅธ์ชฝ ํŒจ๋„ ๋ Œ๋”๋Ÿฌ ์ปค์Šคํ…€(๋ฉ”ํŠธ๋ฆญ, ํ‘œ ๋“ฑ ์ž„์˜ UI).
  • ํƒœ์Šคํฌ ๋Ÿฐํƒ€์ž„ ํ†ต๊ณ„(EWMA ํ‰๊ท  ์ฒ˜๋ฆฌ์†๋„, ETA).
  • ์ข…๋ฃŒ ์‹œ ์‹คํŒจ ํƒœ์Šคํฌ์— ๋Œ€ํ•œ ์ปฌ๋Ÿฌ ํŠธ๋ ˆ์ด์Šค๋ฐฑ ๋ฆฌํฌํŠธ.
  • ๋ฉ€ํ‹ฐํ”„๋กœ์„ธ์‹ฑ์šฉ ํ ๋ฐ”์ธ๋” ๋ฐ ๋ฉ”์‹œ์ง€ ์Šคํ‚ค๋งˆ.
  • ANSI/์ „๊ฐํญ(CJK, โฃฟ) ์•ˆ์ „ํ•œ ๊ฐ€์‹œํญ ์ฒ˜๋ฆฌ.
  • ์„ธ๋กœ ์ •์ฑ…: fit(๋‚ด์šฉ๋งŒ), full(ํ„ฐ๋ฏธ๋„ ๋†’์ด ์ฑ„์›€). ALT ์Šคํฌ๋ฆฐ ์˜ต์…˜.

์„ค์น˜

pip install braille-progress  # ๋˜๋Š” ์ €์žฅ์†Œ ๊ฒฝ๋กœ์—์„œ ์„ค์น˜

Python โ‰ฅ 3.8. Windows๋Š” Windows Terminal ๋“ฑ VT ์ง€์› ์ฝ˜์†” ๊ถŒ์žฅ.

๋น ๋ฅธ ์‹œ์ž‘

๋น ๋ฅธ ์‹œ์ž‘

from braille_progress import Progress, ProgressTheme

p = Progress(
    row_policy="fit",          # ์œ„ ์ถœ๋ ฅ ๋ณด์กด, ํ•„์š”ํ•œ ์ค„๋งŒ ์•„๋ž˜์— ํ™•๋ณด
    split_ratio=0.58,          # ์ขŒ์ธก:์šฐ์ธก ํญ ๋น„์œจ
    show_vsep=True,            # ์„ธ๋กœ ๊ตฌ๋ถ„์„  ํ‘œ์‹œ
)

t1 = p.add("download", total=100)
t2 = p.add("extract", total=30)

for i in range(10):
    t1.advance(1, stage="writing", label=f"part {i}")
    p.log(t1, f"chunk {i} ok")

p.loop()   # w/s ๋˜๋Š” โ†‘/โ†“๋กœ ์„ ํƒ, ๋งˆ์šฐ์Šค ํด๋ฆญ์œผ๋กœ๋„ ์„ ํƒ ๊ฐ€๋Šฅ
p.close()  # ์ข…๋ฃŒ ๋ฐ ์—๋Ÿฌ ๋ฆฌํฌํŠธ/ํ„ฐ๋ฏธ๋„ ์ƒํƒœ ๋ณต๊ตฌ

์ขŒ์šฐ ๋ถ„ํ•  + ์„ ํƒ + ์˜ค๋ฅธ์ชฝ ๋กœ๊ทธ

from braille_progress import Progress

p = Progress(split_ratio=0.55, show_vsep=True)
a = p.add("prepare", total=10)
b = p.add("train", total=500)

for i in range(10):
    a.advance(1, stage="writing"); p.log(a, f"prepare step {i}")
for i in range(50):
    b.advance(1, stage="writing"); p.log(b, f"loss={1/(i+1):.4f}")

p.loop()   # ๋งˆ์šฐ์Šค ํด๋ฆญ ๋˜๋Š” w/s, โ†‘/โ†“๋กœ ์„ ํƒ; ์˜ค๋ฅธ์ชฝ์— ํ•ด๋‹น ํƒœ์Šคํฌ ๋กœ๊ทธ ํ‘œ์‹œ
p.close()

์กฐ์ž‘:

  • ํ‚ค๋ณด๋“œ: w/โ†‘ ์œ„, s/โ†“ ์•„๋ž˜, Home/End, q ๋˜๋Š” Ctrl+C ์ข…๋ฃŒ
  • ๋งˆ์šฐ์Šค: ํƒœ์Šคํฌ ๋ผ์ธ ํด๋ฆญ(SGR ๋งˆ์šฐ์Šค ๋ชจ๋“œ ์ž๋™ ํ™œ์„ฑํ™”)

stdout/stderr๋ฅผ ๋กœ๊ทธ ํŒจ๋„์— ์ถœ๋ ฅ

from braille_progress import Progress

p = Progress()
h = p.add("convert", total=3)

with p.hijack_stdio(h):     # print()/traceback์ด ํ•ด๋‹น ํƒœ์Šคํฌ ๋กœ๊ทธ ํŒจ๋„๋กœ ๋“ค์–ด๊ฐ
    print("converting...")
    try:
        1/0
    except Exception as e:
        h.fail(error=e)     # ์ข…๋ฃŒ ์‹œ ์˜ˆ์œ ํŠธ๋ ˆ์ด์Šค๋ฐฑ๋„ ์ตœ์ข… ๋ฆฌํฌํŠธ๋กœ ์ถœ๋ ฅ

p.close()

์ปค์Šคํ…€ ํ—ค๋”/ํ‘ธํ„ฐ/ํ–‰(Row) ๋ ˆ์ด์•„์›ƒ

from braille_progress import (
    Progress, ProgressTheme, Layout, Name, Bar, Percent, Status,
    MiniBar, Counter, Label, Text, Gap, Elapsed, AvgRate, ETA,
    Rule, Now, VLayout, VGap, default_layout
)

theme = ProgressTheme.auto_fit()

row = Layout([
    Name(width=22),
    Text(" | "),
    Bar(cells=18),
    Percent(width=5),
    Gap(2),
    Status(width=16),
    Text(" | "),
    MiniBar(cells=10),
    Counter(),
    Gap(2),
    Label(width="flex")
], theme=theme)

header = VLayout([ Rule(), Text(" Jobs"), VGap(1) ])
footer = VLayout([ VGap(1), Rule(), Text(" Ready  "), Now() ])

p = Progress(layout=row, header=header, footer=footer, split_ratio=0.6, show_vsep=True)
a = p.add("aa.zip", total=100)
b = p.add("bb.zip", total=100)
a.advance(30, stage="writing", label="downloading")
b.advance(70, stage="writing", label="processing")
p.loop()
p.close()

๋ฉ”๋ชจ:

  • Label(width="flex") ๊ฐ€๋ณ€ ํญ์œผ๋กœ ๋‚จ์€ ๊ณต๊ฐ„์„ ์ฐจ์ง€ํ•˜๋ฉฐ, ์ค„๋งž์ถค ํ•„์š” ์‹œ ๋งˆ์ง€๋ง‰์— ์ถ•์†Œ๋ฉ๋‹ˆ๋‹ค.
  • Label์ด ์—†๋”๋ผ๋„ ๋งˆ์ง€๋ง‰ ์„ธ๊ทธ๋จผํŠธ๋ฅผ ์ถ•์†Œํ•ด ์ค„๋ฐ”๊ฟˆ์„ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค.
  • ๊ฐ€์‹œํญ(ANSI ์ œ๊ฑฐ, ์ „๊ฐํญ ๋ฐ˜์˜) ๊ธฐ์ค€์œผ๋กœ ํญ์„ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค.

์„ธ๋กœ ํฌ๊ธฐ ์ •์ฑ…

# ํ„ฐ๋ฏธ๋„ ์ „์ฒด ๋†’์ด ์ฑ„์›€(๋Œ€์‹œ๋ณด๋“œ)
p = Progress(row_policy="full", min_body_rows=8, use_alt_screen=True)

# ๋‚ด์šฉ๋งŒ ํ‘œ์‹œ(ํ™”๋ฉด ์ „์ฒด ์ ์œ  ์—†์Œ)
p = Progress(row_policy="fit", max_body_rows=12, use_alt_screen=False)

row_policy:

  • "full": ํ—ค๋”+๋ฐ”๋””+ํ‘ธํ„ฐ๊ฐ€ ํ„ฐ๋ฏธ๋„ ๋†’์ด๋ฅผ ์ฑ„์›€
  • "fit": ์ฝ˜ํ…์ธ ์— ๋งž๊ฒŒ ๋ฐ”๋”” ํ–‰ ๊ฐœ์ˆ˜๋ฅผ ๊ฒฐ์ •(min_body_rows/max_body_rows๋กœ ์ƒยทํ•˜ํ•œ ์ œ์–ด)

์˜ค๋ฅธ์ชฝ ํŒจ๋„ ๋ Œ๋”๋Ÿฌ ์ปค์Šคํ…€

from braille_progress import Progress, DetailRenderer

class MetricsPanel(DetailRenderer):
    def render(self, *, width, height, styler, title, lines):
        out = [styler.color(f"[{title}] metrics", fg="bright_magenta").ljust(width)[:width]]
        for i in range(1, height):
            out.append(f"logs={len(lines)} row={i}".ljust(width)[:width])
        return out

p = Progress(right_renderer=MetricsPanel(), split_ratio=0.5)
h = p.add("task", total=10)
for i in range(10): p.log(h, f"event {i}")
p.loop(); p.close()

๋‚ด์žฅ ๋ Œ๋”๋Ÿฌ:

  • ConsoleRenderer(๊ธฐ๋ณธ): ์ œ๋ชฉ + ๋๋ถ€๋ถ„ ๋กœ๊ทธ
  • StaticRenderer(lines): ๊ณ ์ • ๋ฌธ์ž์—ด ๋ฆฌ์ŠคํŠธ

์—๋Ÿฌ ๋ฆฌํฌํŠธ

์‹คํŒจํ•œ ํƒœ์Šคํฌ๋Š” Progress.close() ์‹œ ์ปฌ๋Ÿฌ ํŠธ๋ ˆ์ด์Šค๋ฐฑ๊ณผ ํ•จ๊ป˜ ์š”์•ฝ๋ฉ๋‹ˆ๋‹ค.

from braille_progress import Progress

p = Progress()
try:
    with p.task("upload", total=3) as h:
        raise RuntimeError("remote closed")
except Exception:
    pass

p.close()  # ์‹คํŒจ ํƒœ์Šคํฌ์˜ ํŒŒ์ผ/๋ผ์ธ/ํ•จ์ˆ˜/์ฝ”๋“œ๊ฐ€ ๊ฐ•์กฐ๋œ ํŠธ๋ ˆ์ด์Šค๋ฐฑ ์ถœ๋ ฅ

fail(error=..., error_tb=True)์— ์˜ˆ์™ธ๋ฅผ ๋„˜๊ธฐ๋ฉด ํŠธ๋ ˆ์ด์Šค๋ฐฑ์ด ์˜ˆ์˜๊ฒŒ ํฌ๋งคํŒ…๋ฉ๋‹ˆ๋‹ค.

ํ ๋ฐ”์ธ๋”(๋ฉ€ํ‹ฐํ”„๋กœ์„ธ์‹ฑ)

from braille_progress import Progress, QueueBinder, progress_message

p = Progress()
h = p.add("worker-0", total=100)

# ๋ถ€๋ชจ ํ”„๋กœ์„ธ์Šค
binder = p.bind_queue(my_queue)
while True:
    changed = binder.drain()
    if changed: p.render(throttle=False)
    if p.all_finished(): break

# ์›Œ์ปค ํ”„๋กœ์„ธ์Šค
my_queue.put(progress_message(0, stage="writing", done=5, total=100, label="chunk-5"))
my_queue.put(progress_message(0, final=True))  # DONE

๋ฉ”์‹œ์ง€ ์Šคํ‚ค๋งˆ๋Š” ๊ธฐ๋ณธ ํ‚ค(i, stage, case_done, case_total, case_label)๋ฅผ ์‚ฌ์šฉํ•˜๋ฉฐ, QueueBinder ์ƒ์„ฑ ์‹œ ํ‚ค๋ฅผ ์˜ค๋ฒ„๋ผ์ด๋“œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Progress ๊ฐ์ฒด API

์ƒ์„ฑ์ž(ํŒŒ๋ผ๋ฏธํ„ฐ)

Progress(
  theme: Optional[ProgressTheme]=None,
  *,
  auto_vt: bool=True,
  auto_refresh: bool=True,
  refresh_interval: float=0.05,
  force_tty: Optional[bool]=None,
  force_color: Optional[bool]=None,
  ratio_strategy: Optional[RatioStrategy]=None,

  layout: Optional[Layout]=None,
  header: Optional[Union[VLayout, Layout, Sequence[Row]]]=None,
  footer: Optional[Union[VLayout, Layout, Sequence[Row]]]=None,

  # ์ขŒ์šฐ ๋ถ„ํ• /์šฐ์ธก ํŒจ๋„
  split_ratio: float=0.55,
  show_vsep: bool=True,
  right_renderer: Optional[DetailRenderer]=None,

  # ์„ธ๋กœ ์ •์ฑ…
  row_policy: str="fit",          # "fit" | "full"
  min_body_rows: int=0,           # ์ตœ์†Œ ํ‘œ์‹œ ์ค„ ์ˆ˜(fit/full ๊ณตํ†ต)
  max_body_rows: Optional[int]=None,  # fit ๋ชจ๋“œ์—์„œ ์ตœ๋Œ€ ์ค„ ์ˆ˜ ์ œํ•œ

  # ์Šคํฌ๋ฆฐ/์‹œ๊ทธ๋„
  use_alt_screen: bool=False,
  handle_signals: bool=True
)

ํ‘œ์‹œ/๊ฐฑ์‹ 

  • auto_refresh: ์ƒํƒœ ๋ณ€๊ฒฝ ์‹œ ์ž๋™ ๋ฆฌ๋ Œ๋”.
  • refresh_interval: ์ž๋™ ๋ฆฌ๋ Œ๋” ์ตœ์†Œ ๊ฐ„๊ฒฉ(์ดˆ).
  • theme: ํญ ๊ณ„์‚ฐ/์ƒ‰์ƒ ํŒ”๋ ˆํŠธ. ProgressTheme.auto_fit() ๊ถŒ์žฅ.
  • force_color: True๋ฉด ANSI ์ƒ‰ ๊ฐ•์ œ, False๋ฉด ๋น„ํ™œ์„ฑ.
  • force_tty: ๊ฐ•์ œ๋กœ TTY ๋ชจ๋“œ๋กœ ๋ Œ๋”(ํŒŒ์ดํ”„ ํ™˜๊ฒฝ ํ…Œ์ŠคํŠธ์šฉ).
  • ratio_strategy: ์ง„ํ–‰๋ฅ  ๊ณ„์‚ฐ ์ „๋žต ์ปค์Šคํ„ฐ๋งˆ์ด์ฆˆ.

๋ ˆ์ด์•„์›ƒ(์ขŒ์ธก ๋ฆฌ์ŠคํŠธ ๋ผ์ธ)

  • layout: ํ•œ ์ค„์„ ๊ตฌ์„ฑํ•˜๋Š” ๋นŒ๋”ฉ๋ธ”๋ก(Layout DSL). ๋ฏธ์ง€์ • ์‹œ ๊ธฐ๋ณธ ๋ ˆ์ด์•„์›ƒ.
  • header/footer: ์ƒ/ํ•˜๋‹จ์— ์„ธ๋กœ ๋ ˆ์ด์•„์›ƒ ์ถ”๊ฐ€. VLayout, Layout, Row ์‹œํ€€์Šค ์ง€์›.

์ขŒ์šฐ ๋ถ„ํ• /์šฐ์ธก ํŒจ๋„

  • split_ratio: ์ขŒ์ธก ๋ฆฌ์ŠคํŠธ ํญ ๋น„์œจ(0.1~0.9).
  • show_vsep: ์ขŒ์šฐ ์‚ฌ์ด์— โ”‚ ํ‘œ์‹œ.
  • right_renderer: ์šฐ์ธก ํŒจ๋„ ์ฝ˜ํ…์ธ  ๋ Œ๋”๋Ÿฌ. ๊ธฐ๋ณธ์€ ์ฝ˜์†” ๋กœ๊ทธ(ConsoleRenderer).

์„ธ๋กœ ์ •์ฑ…

  • row_policy="fit": ์œ„ ๊ธฐ์กด ์ถœ๋ ฅ์€ ๊ทธ๋Œ€๋กœ ๋‘๊ณ , ์•„๋ž˜์— ํ•„์š”ํ•œ ์ค„๋งŒ ํ™•๋ณดํ•˜์—ฌ ๊ทธ ์•ˆ์—์„œ ๊ฐฑ์‹ (์Šคํฌ๋ฆฐ ์ „์ฒด๋ฅผ ์ฑ„์šฐ์ง€ ์•Š์Œ).
  • row_policy="full": ํ˜„์žฌ ํ„ฐ๋ฏธ๋„ ๋†’์ด์— ๋งž์ถฐ ๋ณธ๋ฌธ ์˜์—ญ์„ ์ฑ„์›€.
  • min_body_rows: ์ตœ์†Œ ์ค„ ๋ณด์žฅ.
  • max_body_rows: fit ๋ชจ๋“œ์—์„œ ์ตœ๋Œ€ ์ค„ ์ œํ•œ.

์Šคํฌ๋ฆฐ/์‹œ๊ทธ๋„

  • use_alt_screen: True๋ฉด ALT ์Šคํฌ๋ฆฐ(๋ณ„๋„ ๋ฒ„ํผ) ์‚ฌ์šฉ.
  • handle_signals: SIGINT/SIGTERM/SIGHUP์—์„œ ํ„ฐ๋ฏธ๋„/์ž…๋ ฅ ๋ชจ๋“œ ์•ˆ์ „ ๋ณต๊ตฌ.

๋ฉ”์„œ๋“œ

ํƒœ์Šคํฌ ์ƒ์„ฑ/๊ฐฑ์‹ 

h = p.add(name: str, total: int = 0) -> TaskHandle
  • ์ƒˆ ํƒœ์Šคํฌ ์ถ”๊ฐ€. ๋ฐ˜ํ™˜๋˜๋Š” TaskHandle๋กœ ๊ฐฑ์‹ .
p.update(handle_or_id, *, advance=0, done=None, total=None,
         stage=None, label=None, finished=None, failed=None) -> None
  • ํƒœ์Šคํฌ ์ƒํƒœ ๊ฐฑ์‹ . advance๋Š” done์„ ์ฆ๊ฐ€์‹œํ‚ด.
h.advance(n: int=1, *, label: Optional[str]=None, stage: Optional[str]=None) -> TaskHandle
  • ์ง„ํ–‰ ์ˆ˜์น˜ ๊ฐ„ํŽธ ์ฆ๊ฐ€.
p.done(handle_or_id) -> None
h.complete() -> None
  • ํƒœ์Šคํฌ ์™„๋ฃŒ ์ฒ˜๋ฆฌ(ํ‘œ์‹œ์ƒ stage="done").
p.fail(handle_or_id, *, stage: str="error",
       error: Optional[Any]=None, error_tb: bool=True) -> None
h.fail(stage: str="error") -> None
  • ์‹คํŒจ ์ฒ˜๋ฆฌ. error์— ์˜ˆ์™ธ ๊ฐ์ฒด๋ฅผ ๋„˜๊ธฐ๋ฉด ์ข…๋ฃŒ ์‹œ ์˜ˆ์œ traceback ํฌํ•จ ์—๋Ÿฌ ๋ฆฌํฌํŠธ ์ถœ๋ ฅ.
p.all_finished() -> bool
  • ๋ชจ๋“  ํƒœ์Šคํฌ ์ข…๋ฃŒ ์—ฌ๋ถ€.

์ปจํ…์ŠคํŠธ API

with p.task("name", total=10) as h:
    ...
  • ๋ธ”๋ก ๋‚ด ์˜ˆ์™ธ ๋ฐœ์ƒ ์‹œ ์ž๋™ fail(), ์ •์ƒ ์ข…๋ฃŒ ์‹œ ์ž๋™ done().

๋ฐ˜๋ณต ๋„์šฐ๋ฏธ

for item in p.track(iterable, total=None, description=None, label_from=None):
    ...
  • ๋ฐ˜๋ณต ์ค‘ ์ž๋™ ์ง„ํ–‰/๋ผ๋ฒจ ๊ฐฑ์‹ . ์˜ˆ์™ธ ์‹œ ์ž๋™ fail().

๋กœ๊ทธ/์šฐ์ธก ํŒจ๋„

p.log(handle_or_id, msg: str) -> None
  • ํ•ด๋‹น ํƒœ์Šคํฌ์˜ ๋กœ๊ทธ๋ฅผ ์šฐ์ธก ํŒจ๋„์— ์ถ”๊ฐ€(์ตœ๋Œ€ ์ตœ๊ทผ 2000์ค„ ์œ ์ง€).
p.set_right_renderer(renderer: DetailRenderer) -> None
  • ์šฐ์ธก ํŒจ๋„ ๋ Œ๋”๋Ÿฌ ๊ต์ฒด.
with p.hijack_stdio(handle_or_id):
    # ์ด ๋ธ”๋ก์˜ stdout/stderr๋Š” ์šฐ์ธก ํŒจ๋„ ๋กœ๊ทธ๋กœ ์œ ์ž…
    print("captured")

๋ Œ๋”/๋ฃจํ”„/์ข…๋ฃŒ

p.render(throttle: bool=False) -> None
  • ์ˆ˜๋™ ๋ Œ๋”. throttle=True๋ฉด refresh_interval์„ ์กด์ค‘.
p.loop() -> None
  • ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒ UI ๋ฃจํ”„ ์‹œ์ž‘.

    • ํ‚ค๋ณด๋“œ: w/s ๋˜๋Š” โ†‘/โ†“๋กœ ์ขŒ์ธก ์„ ํƒ ์ด๋™.
    • ๋งˆ์šฐ์Šค: ์ขŒ์ธก ๋ฆฌ์ŠคํŠธ ์˜์—ญ ํด๋ฆญ์œผ๋กœ ์„ ํƒ.
    • q ๋˜๋Š” Ctrl+C๋กœ ์ข…๋ฃŒ.
p.close() -> None
  • ํ™”๋ฉด ์ •๋ฆฌ, ์‹คํŒจ ํƒœ์Šคํฌ ์—๋Ÿฌ ๋ฆฌํฌํŠธ ์ถœ๋ ฅ, ๋งˆ์šฐ์Šค/์ž…๋ ฅ ๋ชจ๋“œ/ALT ์Šคํฌ๋ฆฐ/VT ๋ชจ๋“œ ๋“ฑ ์™„์ „ ๋ณต๊ตฌ.

ํ ๋ฐ”์ธ๋”(์„ ํƒ)

qb = p.bind_queue(queue, id_key="i", stage_key="stage",
                  done_key="case_done", total_key="case_total",
                  label_key="case_label")
changed = qb.drain()
  • ์™ธ๋ถ€ ์›Œ์ปค ๋ฉ”์‹œ์ง€๋ฅผ UI์— ๋ฐ˜์˜.

์šฐ์ธก ํŒจ๋„ ์ปค์Šคํ…€ ๋ Œ๋”๋Ÿฌ

from braille_progress import DetailRenderer

class MyPanel(DetailRenderer):
    def render(self, *, width, height, styler, title, lines):
        out = [styler.color(f"[{title}] metrics", fg="bright_magenta").ljust(width)[:width]]
        for i in range(1, height):
            txt = f"rows={height}, logs={len(lines)}"
            out.append(txt.ljust(width)[:width])
        return out
  • width/height ๋‚ด์—์„œ๋งŒ ์ถœ๋ ฅํ•˜๋„๋ก ๋ฐ˜๋“œ์‹œ ํŒจ๋”ฉ/์ ˆ๋‹จ ์ฒ˜๋ฆฌ.

์ขŒ์ธก ๋ผ์ธ ๋ ˆ์ด์•„์›ƒ ๊ต์ฒด(์š”์•ฝ)

  • DSL ๊ตฌ์„ฑ์š”์†Œ ์˜ˆ: Name(), Bar(), Percent(), Status(), MiniBar(), Counter(), Label(), Elapsed(), AvgRate(), ETA(), Text(" | "), Gap(w) ๋“ฑ.
  • ์˜ˆ:
from braille_progress import Layout, Name, Text, Bar, Percent, Status, MiniBar, Counter, Label

layout = Layout([
  Name(w=20), Text(" | "),
  Bar(cells=18), Percent(), Text("  "),
  Status(w=16), Text(" | "),
  MiniBar(cells=10), Counter(), Text("  "),
  Label(w=32)
])
p = Progress(layout=layout)

๋ชจ๋“œ/ํ™˜๊ฒฝ ๋ณ€์ˆ˜

  • use_alt_screen=True: ๋ฉ”์ธ ํ„ฐ๋ฏธ๋„๊ณผ ๋ถ„๋ฆฌ๋œ ๋ฒ„ํผ์— ๋ Œ๋”(์™ธ๋ถ€ ์ถœ๋ ฅ ๊ฐ„์„ญ ์ค„์ž„).
  • NO_COLOR=1: ๊ฐ•์ œ๋กœ ๋ฌด์ฑ„์ƒ‰.
  • BP_FORCE_TTY=1: ๊ฐ•์ œ๋กœ TTY ๋ชจ๋“œ.
  • BP_FORCE_COLOR=1: ๊ฐ•์ œ๋กœ ์ปฌ๋Ÿฌ ํ™œ์„ฑ.

์—๋Ÿฌ ๋ฆฌํฌํŠธ

  • p.close() ์‹œ ์‹คํŒจ ํƒœ์Šคํฌ๋ฅผ ์ˆ˜์ง‘ํ•˜์—ฌ:

    • ํƒœ์Šคํฌ๋ช…/์Šคํ…Œ์ด์ง€/์ง„ํ–‰/๊ฒฝ๊ณผ/์†๋„
    • ์ „๋‹ฌ๋ฐ›์€ error ์˜ˆ์™ธ์˜ ์ปฌ๋Ÿฌ ํŠธ๋ ˆ์ด์Šค๋ฐฑ(ํŒŒ์ผ/๋ผ์ธ/ํ•จ์ˆ˜/์ฝ”๋“œ) ์ถœ๋ ฅ.

์‚ฌ์šฉ ํŒ

  • ๋ฃจํ”„ ๋™์•ˆ ์™ธ๋ถ€ print()๊ฐ€ ํ•„์š”ํ•˜๋ฉด ๋ฐ˜๋“œ์‹œ hijack_stdio()๋กœ ๊ฐ์‹ธ ์šฐ์ธก ๋กœ๊ทธ๋กœ ๋ณด๋‚ด๋ผ(๋ ˆ์ด์•„์›ƒ ๊นจ์ง ๋ฐฉ์ง€).
  • row_policy="fit"์€ ๊ธฐ์กด ์ƒ๋‹จ ์ถœ๋ ฅ ๋ณด์กด. ํ•„์š” ์ค„์ด ๋Š˜๋ฉด ์•„๋ž˜๋กœ๋งŒ ํ™•์žฅํ•œ๋‹ค. row_policy="full"์€ ํ„ฐ๋ฏธ๋„ ๋†’์ด๋ฅผ ์ฑ„์šฐ๋ฉฐ ํ—ค๋”/ํ‘ธํ„ฐ์™€ ํ•จ๊ป˜ ์Šคํ…Œ์ด๋ธ”ํ•˜๊ฒŒ ๊ฐฑ์‹ ํ•œ๋‹ค.
  • Windows์—์„œ๋Š” Windows Terminal + ๊ณ ์ •ํญ ํฐํŠธ ์‚ฌ์šฉ์„ ๊ถŒ์žฅ.

๋ Œ๋”๋ง/ํ„ฐ๋ฏธ๋„ ๋™์ž‘

  • ๋ Œ๋”๋ง ์ค‘ ์ž๋™์ค„๋ฐ”๊ฟˆ์„ ๋„๊ณ , ๋ชจ๋“  ์ค„์„ cols-1 ๋‚ด๋กœ ๊ฐ€์‹œํญ ๊ธฐ์ค€ ์ ˆ๋‹จ. ๋งˆ์ง€๋ง‰ ์ค„์€ ๊ฐœํ–‰ ์—†์ด ์ถœ๋ ฅํ•ด ์Šคํฌ๋กค์„ ๋ง‰์Šต๋‹ˆ๋‹ค.
  • loop()์—์„œ VT ์ž…๋ ฅยท๋งˆ์šฐ์Šค ํ™œ์„ฑํ™”, close()/์ข…๋ฃŒ ์‹œ ๋งˆ์šฐ์Šค/ํฌ์ปค์Šค/๋ธŒ๋ž˜ํ‚ท๋“œ ํŽ˜์ด์ŠคํŠธ ๋ชจ๋“œ ํ•ด์ œ(?1006/?1002/?1000/?1015/?1004/?2004), ์ปค์„œ ๋ณด์ด๊ธฐยทwrap ๋ณต๊ตฌ, ์ž…๋ ฅ ๋ฒ„ํผ ํ”Œ๋Ÿฌ์‹œ(Windows FlushConsoleInputBuffer, POSIX tcflush), ALT ์Šคํฌ๋ฆฐ ์ข…๋ฃŒ, ์ฝ˜์†” ๋ชจ๋“œ ์›๋ณต.
  • ๋ Œ๋”๋ง ์ค‘ print() ํ˜ธ์ถœ์€ ํ™”๋ฉด์„ ํ”๋“ญ๋‹ˆ๋‹ค. p.log(...) ๋˜๋Š” p.hijack_stdio(handle) ์‚ฌ์šฉ์„ ๊ถŒ์žฅ.

ํ™˜๊ฒฝ ๋ณ€์ˆ˜

  • BP_FORCE_TTY=1 : TTY ๋ชจ๋“œ ๊ฐ•์ œ
  • BP_FORCE_COLOR=1 : ์ปฌ๋Ÿฌ ๊ฐ•์ œ
  • NO_COLOR=1 : ์ปฌ๋Ÿฌ ๋น„ํ™œ์„ฑํ™”

์˜ˆ์‹œ

์—๋Ÿฌ ํฌํ•จ ์ตœ์†Œ ์˜ˆ์‹œ

from braille_progress import Progress

p = Progress()
h = p.add("upload", total=3)
try:
    for i in range(3):
        if i == 2: raise RuntimeError("remote closed")
        h.advance(1, stage="writing")
except Exception as e:
    h.fail(error=e)
p.close()

Fit ๋ชจ๋“œ ๋Œ€์‹œ๋ณด๋“œ(ALT ์Šคํฌ๋ฆฐ ์—†์ด)

from braille_progress import Progress, default_layout, ProgressTheme

p = Progress(
    layout=default_layout(ProgressTheme.auto_fit()),
    row_policy="fit",
    max_body_rows=10,
    split_ratio=0.6,
    show_vsep=True,
    use_alt_screen=False
)
# ... ํƒœ์Šคํฌ/๋กœ๊ทธ ์ถ”๊ฐ€ ...
p.loop(); p.close()

๊ณต๊ฐœ ์‹ฌ๋ณผ

from braille_progress import (
  Progress, ProgressTheme, TaskHandle, TaskState,
  RatioStrategy, DefaultRatio, QueueBinder, progress_message,
  Layout, default_layout, RenderContext,
  Name, Bar, Percent, Status, MiniBar, Counter, Label, Text, Gap,
  Elapsed, AvgRate, ETA, Spacer, Rule, Now, VGap, VLayout,
  DetailRenderer, ConsoleRenderer, StaticRenderer
)

Keywords

ansi

FAQs

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts