Is this a valid use case for a context manager?
-
19-06-2021 - |
Question
I'm making a progress indicator for some long-running console process with intent to use it like this:
pi = ProgressIndicator()
for x in somelongstuff:
do stuff
pi.update()
pi.print_totals()
Basically, it should output some kind of a progress bar with dots and dashes, and something like "234234 bytes processed" at the end.
I thought it would be nice to use it as a context manager:
with ProgressIndicator() as pi:
for x in somelongstuff:
do stuff
pi.update()
However there are a few things that concern me about this solution:
- extra indentation makes the indicator feature appear more important than it actually is
- I don't want
ProgressIndicator
to handle any exceptions that might occur in the loop
Is this a valid use case for a context manager? What other solutions can you suggest?
Solution
It definitely seems a valid use case. The context manager doesn't have to handle exceptions if you don't want it to, although you would want to end the line that the progress bar is output on to prevent it being confused with the traceback, and have it not print the totals if it is exited through an exception.
With regard to indentation, I'd argue that letting the user see progress is actually a very important feature so it's fine for it to take up an indentation level.
OTHER TIPS
There's a GUI application which has a very similar ProgressTask
API, which you use like this:
def slow_func():
t = nuke.ProgressTask()
t.setMessage("Doing something")
for x in range(100):
do_something()
t.setProgress(x+1)
When ProgressTask.__del__
is called, the progress bar UI disappears. This works nicely for the most part, however if an exception is raised (e.g by do_something()
), the traceback object keeps a reference to the ProgressTask
object, so the progress-bar gets stuck (until another traceback occurs)
The ProgressTask
implemented the context-manager protocol, it could use the __exit__
method to ensure the progress bar has been hidden.
For a command-line UI (which is sounds like you are writing), this may not be an issue, but you could do similar cleanup tasks, e.g to display an ######### 100% (error)
type bar, and ensure the traceback output isn't messed up etc
There's no reason your progress-bar class couldn't be usable in both manners - most context-managers are perfectly usable as both regular objects and context-managers, e.g:
lock = threading.Lock()
lock.acquire()
lock.release()
# or:
with lock:
pass