Reputation: 594
Here is a minimal reproducible example:
import logging
from tqdm import tqdm
for i in tqdm(range(1000)):
for _ in range(2000):
a = i+8
if i % 100 == 0:
logging.warning("test")
This outputs this:
0%| | 0/1000 [00:00<?, ?it/s]WARNING:root:test
WARNING:root:test
WARNING:root:test
WARNING:root:test
WARNING:root:test
WARNING:root:test
WARNING:root:test
WARNING:root:test
WARNING:root:test
WARNING:root:test
100%|██████████| 1000/1000 [00:00<00:00, 996982.17it/s]
I'm fine with the bar refreshing and printing again (without the argument leave=True
basically) but there is no linebreak for the first warning. It doesn't work very well either with a print("test")
:
0%| | 0/1000 [00:00<?, ?it/s]test
test
test
test
test
test
test
test
test
test
100%|██████████| 1000/1000 [00:00<00:00, 20526.40it/s]
Any idea how to have a proper linebreak for the first print / logging? I can't add "\n" because otherwise i'm gonna have way too many linebreaks for the following print messages. And adding a "if this is the first message then add "\n" otherwise don't" sounds like a dirty way of bypassing the problem.
I've seen this post (tqdm with logger on separate lines) but this was no answers apart from "do your own tqdm" which i don't really like because i like just having to do tqdm(my_iterable)
.
Upvotes: 3
Views: 1489
Reputation: 106995
You can clear the status bar by calling the clear
method of the tqdm
object before writing output to stdout or stderr.
To make the call to the clear
method happen automatically for each output, you can create a context manager that patches both sys.stdout
and sys.stderr
with a wrapper function that calls the clear
method of a given tqdm
object before writing a message to the original sys.stderr
:
import sys
from unittest.mock import patch
from contextlib import contextmanager
@contextmanager
def tqdm_output(tqdm, write=sys.stderr.write):
def wrapper(message):
if message != '\n':
tqdm.clear()
write(message)
if '\n' in message:
tqdm.display()
with patch('sys.stdout', sys.stderr), patch('sys.stderr.write', wrapper):
yield tqdm
So that you can put the iteration within the context manager and use the logger normally:
with tqdm_output(tqdm(range(1000))) as trange:
for i in trange:
if i % 100 == 0:
logging.warning("test")
And it would output:
WARNING:root:test
WARNING:root:test
WARNING:root:test
WARNING:root:test
WARNING:root:test
WARNING:root:test
WARNING:root:test
WARNING:root:test
WARNING:root:test
WARNING:root:test
100%|██████████████████████████████| 1000/1000 [00:00<00:00, 1797.66it/s]
Same with using print
:
with tqdm_output(tqdm(range(1000))) as trange:
for i in trange:
if i % 100 == 0:
print('test')
which outputs:
test
test
test
test
test
test
test
test
test
test
100%|███████████████████████████████| 1000/1000 [00:05<00:00, 181.55it/s]
Demo: https://replit.com/@blhsing/GentleShallowScan
Upvotes: 2