[ANN] Progress 0.1.0

I’m pleased to announce the first release of Progress, now available on Opam.

Progress is a small library for quickly defining and using progress bars in OCaml programs. It aims to provide the following:

  • support for rendering multiple progress bars simultaneously;
  • responds dynamically to changes in terminal size;
  • allows user-defined progress bar layouts.

Defining your own progress bars

The example animation above uses a pre-provided progress bar layout that should meet many needs (Progress_unix.counter), but it’s fairly easy to re-define it ourselves using the low-level Progress.Segment API:

let counter filename =
  let proportion i = Int64.to_float i /. 1_000_000. in
  let open Progress in
  Segment.(
    list
      [
        const filename;
        Units.bytes of_pp;
        Progress_unix.stopwatch ();
        bar ~mode:`ASCII proportion;
        using proportion (Units.percentage of_pp);
      ]
    |> box_winsize ~fallback:80  (* Dynamically scale to window size *)
    |> periodic 100              (* Re-render once every 100 updates *)
    |> accumulator Int64.add 0L  (* Accumulate progress updates *))
  |> make ~init:0L

The Segment combinators are similar to those of general-purpose pretty-printing libraries (e.g. pp and fmt), but are equipped with extra logic for “stateful” segments and segments that can have dynamic width. Together, these make for a convenient way to express common patterns when pretty-printing progress bars. For instance, the stateful segment periodic seen above can be used to ensure that very frequent updates from a hot-loop do not result in too much time spent re-rendering the output.

The library is not yet feature-complete, but should still be reasonably useful :slightly_smiling_face: Happy hacking!

19 Likes

Looks pretty!

At some point I made something like that which would also display logs on top of the progress bars. You would have logs like any terminal application and, at the bottom, the progress bars. It was a bit tricky to get right (you don’t want the progress bar to hide the logs, and you need the logs to remove the progress bar to draw over it, and then redraw the progress bar). Can Progress do that?

2 Likes

Thanks!

This would be a very valuable feature, and is the main thing I was thinking about when I said that the library is not feature-complete :slight_smile: I have a draft feature that allows “protecting” a Format.formatter by wrapping its flush function with a prefix that moves the cursor to the appropriate place and a suffix that then re-renders the bar. It works, but is somewhat tricky to test properly given the various edge cases involved. I expect this will be part of the next release.

(It would also be nice to integrate that wrapping functionality with the logs library in some way, so that this protected display mode can happen seamlessly for users of that library.)

2 Likes

Looks cool!
One useful thing for very long running computations is to show the Estimated Time of Arrival (ETA)
as some clock time and update it as the computation is progressing.
Like:

[###......] 33% 1h05m55s
4 Likes