Skip to content

Commit 0dfb747

Browse files
authored
Replace progress_bar with custom utility function (#461)
1 parent 622b6ed commit 0dfb747

5 files changed

Lines changed: 122 additions & 4 deletions

File tree

lib/bumblebee/utils/http.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ defmodule Bumblebee.Utils.HTTP do
121121
last_step_bucket = if step, do: div(last_percent, step), else: last_percent
122122

123123
if step_bucket > last_step_bucket or percent == 100 do
124-
ProgressBar.render(state.size, state.total_size, suffix: :bytes)
124+
Bumblebee.Utils.ProgressBar.render(state.size, state.total_size, unit: :bytes)
125125
end
126126
end
127127

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
defmodule Bumblebee.Utils.ProgressBar do
2+
@moduledoc false
3+
4+
# Reserve 2 chars for the start and end of the bar
5+
# and one to leave a gap at the end of a line, to prevent wrapping on to the next line
6+
@reserved_width 3
7+
8+
@start_end_char "|"
9+
@filled_char "="
10+
@unfilled_char " "
11+
@carriage_return "\r"
12+
13+
@default_opts [unit: :none]
14+
15+
@doc """
16+
Renders a simple progress bar to the terminal.
17+
The progress bar sizes to fill the entire width of the terminal.
18+
"""
19+
@spec render(number(), number(), keyword()) :: :ok
20+
def render(count, total, opts \\ @default_opts) do
21+
opts = Keyword.merge(@default_opts, opts)
22+
unit = Keyword.get(opts, :unit, :none)
23+
percent = min(max(count / total, 0), 1)
24+
25+
formatted_percent = String.pad_leading("#{trunc(percent * 100)}%", 4)
26+
formatted_progress = " " <> formatted_progress(count, total, unit)
27+
28+
reserved_width =
29+
@reserved_width + byte_size(formatted_progress) + byte_size(formatted_percent)
30+
31+
bar_width = max(terminal_width() - reserved_width, 0)
32+
33+
filled_char_count = trunc(percent * bar_width)
34+
unfilled_char_count = max(bar_width - filled_char_count, 0)
35+
36+
filled_chars = String.duplicate(@filled_char, filled_char_count)
37+
unfilled_chars = String.duplicate(@unfilled_char, unfilled_char_count)
38+
39+
output =
40+
@carriage_return <>
41+
@start_end_char <>
42+
filled_chars <>
43+
unfilled_chars <>
44+
@start_end_char <>
45+
formatted_percent <>
46+
formatted_progress
47+
48+
IO.write(output)
49+
end
50+
51+
defp formatted_progress(progress, total, :none) do
52+
"#{progress}/#{total}"
53+
end
54+
55+
defp formatted_progress(progress, total, :bytes) do
56+
{unit, divisor} = bytes_unit_and_divisor(total)
57+
"#{trunc(progress / divisor)}/#{trunc(total / divisor)}#{unit}"
58+
end
59+
60+
defp bytes_unit_and_divisor(count) do
61+
cond do
62+
count > 1_000_000 -> {"MB", 1_000_000}
63+
count > 1_000 -> {"KB", 1_000}
64+
true -> {"B", 1}
65+
end
66+
end
67+
68+
defp terminal_width do
69+
case :io.columns() do
70+
{:ok, width} -> width
71+
{:error, _} -> 80
72+
end
73+
end
74+
end

mix.exs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ defmodule Bumblebee.MixProject do
4545
{:safetensors, "~> 0.1.3"},
4646
{:jason, "~> 1.4.0"},
4747
{:unzip, "~> 0.12.0 or ~> 0.13.0"},
48-
{:progress_bar, "~> 3.0"},
4948
{:stb_image, "~> 0.6.0", only: :test},
5049
{:bypass, "~> 2.1", only: :test},
5150
{:ex_doc, "~> 0.28", only: :dev, runtime: false},

mix.lock

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
"cowboy": {:hex, :cowboy, "2.14.2", "4008be1df6ade45e4f2a4e9e2d22b36d0b5aba4e20b0a0d7049e28d124e34847", [:make, :rebar3], [{:cowlib, ">= 2.16.0 and < 3.0.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, ">= 1.8.0 and < 3.0.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "569081da046e7b41b5df36aa359be71a0c8874e5b9cff6f747073fc57baf1ab9"},
88
"cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"},
99
"cowlib": {:hex, :cowlib, "2.16.0", "54592074ebbbb92ee4746c8a8846e5605052f29309d3a873468d76cdf932076f", [:make, :rebar3], [], "hexpm", "7f478d80d66b747344f0ea7708c187645cfcc08b11aa424632f78e25bf05db51"},
10-
"decimal": {:hex, :decimal, "2.3.0", "3ad6255aa77b4a3c4f818171b12d237500e63525c2fd056699967a3e7ea20f62", [:mix], [], "hexpm", "a4d66355cb29cb47c3cf30e71329e58361cfcb37c34235ef3bf1d7bf3773aeac"},
1110
"earmark_parser": {:hex, :earmark_parser, "1.4.44", "f20830dd6b5c77afe2b063777ddbbff09f9759396500cdbe7523efd58d7a339c", [:mix], [], "hexpm", "4778ac752b4701a5599215f7030989c989ffdc4f6df457c5f36938cc2d2a2750"},
1211
"elixir_make": {:hex, :elixir_make, "0.9.0", "6484b3cd8c0cee58f09f05ecaf1a140a8c97670671a6a0e7ab4dc326c3109726", [:mix], [], "hexpm", "db23d4fd8b757462ad02f8aa73431a426fe6671c80b200d9710caf3d1dd0ffdb"},
1312
"ex_doc": {:hex, :ex_doc, "0.39.1", "e19d356a1ba1e8f8cfc79ce1c3f83884b6abfcb79329d435d4bbb3e97ccc286e", [:mix], [{:earmark_parser, "~> 1.4.44", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "8abf0ed3e3ca87c0847dfc4168ceab5bedfe881692f1b7c45f4a11b232806865"},
@@ -27,7 +26,6 @@
2726
"plug_cowboy": {:hex, :plug_cowboy, "2.7.4", "729c752d17cf364e2b8da5bdb34fb5804f56251e88bb602aff48ae0bd8673d11", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "9b85632bd7012615bae0a5d70084deb1b25d2bcbb32cab82d1e9a1e023168aa3"},
2827
"plug_crypto": {:hex, :plug_crypto, "2.1.1", "19bda8184399cb24afa10be734f84a16ea0a2bc65054e23a62bb10f06bc89491", [:mix], [], "hexpm", "6470bce6ffe41c8bd497612ffde1a7e4af67f36a15eea5f921af71cf3e11247c"},
2928
"polaris": {:hex, :polaris, "0.1.0", "dca61b18e3e801ecdae6ac9f0eca5f19792b44a5cb4b8d63db50fc40fc038d22", [:mix], [{:nx, "~> 0.5", [hex: :nx, repo: "hexpm", optional: false]}], "hexpm", "13ef2b166650e533cb24b10e2f3b8ab4f2f449ba4d63156e8c569527f206e2c2"},
30-
"progress_bar": {:hex, :progress_bar, "3.0.0", "f54ff038c2ac540cfbb4c2bfe97c75e7116ead044f3c2b10c9f212452194b5cd", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "6981c2b25ab24aecc91a2dc46623658e1399c21a2ae24db986b90d678530f2b7"},
3129
"ranch": {:hex, :ranch, "1.8.1", "208169e65292ac5d333d6cdbad49388c1ae198136e4697ae2f474697140f201c", [:make, :rebar3], [], "hexpm", "aed58910f4e21deea992a67bf51632b6d60114895eb03bb392bb733064594dd0"},
3230
"rustler_precompiled": {:hex, :rustler_precompiled, "0.8.3", "4e741024b0b097fe783add06e53ae9a6f23ddc78df1010f215df0c02915ef5a8", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:rustler, "~> 0.23", [hex: :rustler, repo: "hexpm", optional: true]}], "hexpm", "c23f5f33cb6608542de4d04faf0f0291458c352a4648e4d28d17ee1098cddcc4"},
3331
"safetensors": {:hex, :safetensors, "0.1.3", "7ff3c22391e213289c713898481d492c9c28a49ab1d0705b72630fb8360426b2", [:mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:nx, "~> 0.5", [hex: :nx, repo: "hexpm", optional: false]}], "hexpm", "fe50b53ea59fde4e723dd1a2e31cfdc6013e69343afac84c6be86d6d7c562c14"},
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
defmodule Bumblebee.Utils.ProgressBarTest do
2+
use ExUnit.Case, async: true
3+
4+
alias Bumblebee.Utils.ProgressBar
5+
alias ExUnit.CaptureIO
6+
7+
test "renders various widths of progress bars" do
8+
assert "\r|================================ | 50% 0.2/0.4" ==
9+
CaptureIO.capture_io(fn -> ProgressBar.render(0.2, 0.4) end)
10+
11+
assert "\r| | 0% 0/10" ==
12+
CaptureIO.capture_io(fn -> ProgressBar.render(0, 10) end)
13+
14+
assert "\r|================================== | 50% 5/10" ==
15+
CaptureIO.capture_io(fn -> ProgressBar.render(5, 10) end)
16+
17+
assert "\r|============================================================== | 95% 9.5/10" ==
18+
CaptureIO.capture_io(fn -> ProgressBar.render(9.5, 10) end)
19+
20+
assert "\r|=============================================================== | 99% 9.999/10" ==
21+
CaptureIO.capture_io(fn -> ProgressBar.render(9.999, 10) end)
22+
23+
assert "\r|===================================================================|100% 10/10" ==
24+
CaptureIO.capture_io(fn -> ProgressBar.render(10, 10) end)
25+
end
26+
27+
test "renders bars when unexpected inputs given" do
28+
assert "\r| | 0% -10/10" ==
29+
CaptureIO.capture_io(fn -> ProgressBar.render(-10, 10) end)
30+
31+
assert "\r|===================================================================|100% 20/10" ==
32+
CaptureIO.capture_io(fn -> ProgressBar.render(20, 10) end)
33+
end
34+
35+
test "formats byte counts" do
36+
assert "\r|====== | 10% 10/100B" ==
37+
CaptureIO.capture_io(fn -> ProgressBar.render(10, 100, unit: :bytes) end)
38+
39+
assert "\r|================================= | 50% 1/2KB" ==
40+
CaptureIO.capture_io(fn -> ProgressBar.render(1_000, 2_000, unit: :bytes) end)
41+
42+
assert "\r|================================= | 50% 1/2MB" ==
43+
CaptureIO.capture_io(fn ->
44+
ProgressBar.render(1_000_000, 2_000_000, unit: :bytes)
45+
end)
46+
end
47+
end

0 commit comments

Comments
 (0)