rsalmei / alive-progress

A new kind of Progress Bar, with real-time throughput, ETA, and very cool animations!
MIT License
5.53k stars 206 forks source link

Debugging in PDB #94

Closed hellovai closed 2 years ago

hellovai commented 3 years ago

It's virtually impossible to use alive progress bar while attached to the PDB. It would be great if we were able to pause the progress bar thread when we detected PDB connected.

The thread object is not exposed via an API atm.

Alternatively, alive-progress could produce a bar.enable_if(func: Callable[bool]) method which an external caller could use to set the conditions in which the bar update gets disabled.

rsalmei commented 3 years ago

Hey @hellovai, please tell me more. What do you mean by "attached" to the pdb? Care to share some example code?

Anyway, alive-progress do have a pause mechanism (the only one on the market, as far as I know). So, you could do something like this, and it should work flawlessly:

❯ ipython
Python 3.9.0 (default, Dec  7 2020, 00:06:59)
Type 'copyright', 'credits' or 'license' for more information
IPython 7.19.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: from alive_progress import alive_bar
   ...: import time
   ...: import pdb

In [2]: def func():
   ...:     with alive_bar(1000) as bar:
   ...:         for i in range(1000):
   ...:             time.sleep(.01)
   ...:             if i == 100:  # <<<<<<<< your condition to enter pdb.
   ...:                 with bar.pause():  # pause alive_bar, and enter pdb.
   ...:                     pdb.set_trace()
   ...:             bar()
   ...:

In [3]: func()
|████                                    | 100/1000 [10%] in 1s (85.5/s, eta: 11s)
--Call--
> /Users/rogeriosampaioalmeida/.pyenv/versions/3.9.0/lib/python3.9/contextlib.py(121)__exit__()
-> def __exit__(self, type, value, traceback):
(Pdb)

You can now press u to go up, and you're on the original func:

(Pdb) u
> <ipython-input-2-dba7c829852f>(7)func()
-> pdb.set_trace()
(Pdb) l
  2         with alive_bar(1000) as bar:
  3             for i in range(1000):
  4                 time.sleep(.01)
  5                 if i == 100:  # <<<<<<<< your condition to enter pdb.
  6                     with bar.pause():  # pause alive_bar, and enter pdb.
  7  ->                     pdb.set_trace()
  8                 bar()
  9
[EOF]
(Pdb)

And as soon as you continue, everything resumes also flawlessly:

(Pdb) c
|████████████████████████████████████████| 1000/1000 [100%] in 11.5s (87.21/s)

In [4]:

Does that work for you?

hellovai commented 3 years ago

Thanks for the prompt update! The pause mechanism is definitely great! but in this scenario the issue is:

# foo.py
with alive_bar(1000) as bar:
   for I in range(1000):
    bar()

If I wanted to do python -m pdb foo.py, pdb becomes unusable due to the progress bar rewriting the last line in terminal.

While that's definitely desired behavior, if alive_bar has a mode which could pause the internal thread if say sys.gettrace() was not None (i.e. in PDB), then we would be in a slightly easier place.

We currently check if pdb is attached at the beginning and are able to not create the progress bar in some of those scenarios, but sometimes we attach the pdb after the script is launched as well.

Does that help clarify the issue?

rsalmei commented 3 years ago

Hey @hellovai! Humm, nice, now I got it. I'm going to implement it in 2.0, since it does look useful. Thanks!

rsalmei commented 3 years ago

Ok, it's there! Care to test it in the 2.0?

pip install git+https://github.com/rsalmei/alive-progress.git@rsa-refac
rsalmei commented 3 years ago

Humm, I think I misunderstood it... It seems the sys.gettrace() is only non-None when the debugger is actually active at that time, right? Not when it is only available at that session...

That makes it way harder to block the thread... I can't test it once at start up, I would have to:

  1. test sys.gettrace() for non-None in every refresh to block the main thread;
  2. and how to enable it again?? I'd have to create another thread to monitor sys.gettrace() and unblock the main thread when it is None again...

Does that seem correct?

rsalmei commented 2 years ago

Well, closing this one. I'm not sure what I could do to improve this, without jeopardizing the performance by testing in every single refresh. If you do have some other considerations, please reply.