Rockhopper-Technologies / enlighten

Enlighten Progress Bar for Python Console Apps
https://python-enlighten.readthedocs.io
Mozilla Public License 2.0
416 stars 25 forks source link

doctest returns also the bar (string) #6

Closed Borda closed 5 years ago

Borda commented 5 years ago

I have an integrated enlighten in a function which I ma testing in doctest. I have been using tqdm before and it was passing, but with switching to enlighten it returns an extra line

import enlighten

def counter(n):
    """
    >>> list(counter(5))
    [0, 1, 2, 3, 4]
    """
    pbar = enlighten.Counter(total=n, desc='abc')
    for i in range(n):
        yield i
        pbar.update()

but I am getting:

Failed example:
    list(counter(5))
Expected:
    [0, 1, 2, 3, 4]
Got:
abc 100%|███████████████████████████████████████| 5/5 [00:00<00:00, 252668.92/s][0, 1, 2, 3, 4]
avylove commented 5 years ago

I'm seeing the same behavior with TQDM, with the only difference being TQDM adds a line feed at the end of the progress bar and Enlighten doesn't. I'm not sure how your doctests work, but if they are just looking for the last line in STDOUT, this should provide the same behavior you saw before.

import sys
import enlighten

def counter(n):
    pbar = enlighten.Counter(total=n, desc='abc')
    for i in range(n):
        yield i
        pbar.update()
    sys.stdout.write('\n')

You'd have to see if having the linefeed makes sense in normal operation. An alternative would be to support disabling the progress bar.

def counter(n, enable_pbar=True):
    pbar = enlighten.Counter(total=n, desc='abc', enabled=enable_pbar)
    for i in range(n):
        yield i
        pbar.update()

Then in your doctests, disable the progress bar

list(counter(5, enable_pbar=False))
Borda commented 5 years ago

TQDM behaves fine, as you can see here:

def counter_tqdm(n):
    """
    >>> list(counter_tqdm(5))
    [0, 1, 2, 3, 4]
    """
    pbar = tqdm.tqdm(total=n, desc='abc')
    for i in range(n):
        yield i
        pbar.update()

and you can call it with any standard testing tool like nose or pytest

talking about your proposal enabled=False but this drops using enlighten progress bar, right? then I do not need to have it there...

Borda commented 5 years ago

it seems that stream=sys.__stdout__ helps, https://stackoverflow.com/a/8777172/4521646

avylove commented 5 years ago

Thanks for providing some more context.

The doctest is checking what is received on sys.stdout. The default stream for Enlighten is sys.stdout where for TQDM it's sys.stderr. So if you want to have the same behavior with Enlighten, just use sys.stderr for your stream.

import sys
import enlighten

def counter(n):
    """
    >>> list(counter(5))
    [0, 1, 2, 3, 4]
    """
    pbar = enlighten.Counter(total=n, desc='abc', stream=sys.stderr)
    for i in range(n):
        yield i
        pbar.update()
Borda commented 5 years ago

is sys.stderr a terminal screen? what I want is to see the progress bar in terminal but not in the logs...

avylove commented 5 years ago

stderr and stdout are data output streams connected to your program. This or this might give you more information. In the most basic configuration they both write to the terminal and you won't see a difference unless one or both of them is redirected.

As far as if the progress bar goes to the logs or not, it really depends on if the logs are capturing stderr and/or stdout. By default, the Python logger will write to stderr and show up on the console. If you redirect stderr to a file (example: python test.py 2> test.log), then anything written to stderr will go to the file.

As far as the doctests, they seem to be redirecting stdout to capture it for comparison and ignoring anything written to stderr.

Borda commented 5 years ago

I got following error from Windows testing:

UNEXPECTED EXCEPTION: ValueError('bad file descriptor',)
Traceback (most recent call last):
  File "c:\python35\lib\site-packages\enlighten\_terminal.py", line 77, in _height_and_width
    return self._cache['height_and_width']
KeyError: 'height_and_width'
Borda commented 5 years ago

On Shippable for py2 gets AttributeError: 'Tee' object has no attribute 'encoding' ohhh, it seems after your last fixes it is fine now... :)