Skip to content

Commit

Permalink
bump version, Merge branch 'file-default-fix'
Browse files Browse the repository at this point in the history
  • Loading branch information
casperdcl committed May 29, 2017
2 parents 76fe211 + 204fc43 commit 01f9aa1
Show file tree
Hide file tree
Showing 7 changed files with 142 additions and 31 deletions.
4 changes: 2 additions & 2 deletions README.rst
Expand Up @@ -250,7 +250,7 @@ Documentation
"""
def __init__(self, iterable=None, desc=None, total=None, leave=True,
file=sys.stderr, ncols=None, mininterval=0.1,
file=None, ncols=None, mininterval=0.1,
maxinterval=10.0, miniters=None, ascii=None, disable=False,
unit='it', unit_scale=False, dynamic_ncols=False,
smoothing=0.3, bar_format=None, initial=0, position=None,
Expand All @@ -276,7 +276,7 @@ Parameters
upon termination of iteration.
* file : ``io.TextIOWrapper`` or ``io.StringIO``, optional
Specifies where to output the progress messages
[default: sys.stderr]. Uses ``file.write(str)`` and ``file.flush()``
(default: sys.stderr). Uses ``file.write(str)`` and ``file.flush()``
methods.
* ncols : int, optional
The width of the entire output message. If specified,
Expand Down
60 changes: 60 additions & 0 deletions examples/redirect_print.py
@@ -0,0 +1,60 @@
"""Redirecting writing
If using a library that can print messages to the console, editing the library
by replacing `print()` with `tqdm.write()` may not be desirable.
In that case, redirecting `sys.stdout` to `tqdm.write()` is an option.
To redirect `sys.stdout`, create a file-like class that will write
any input string to `tqdm.write()`, and supply the arguments
`file=sys.stdout, dynamic_ncols=True`.
A reusable canonical example is given below:
"""
from __future__ import print_function
from time import sleep
import contextlib
import sys
from tqdm import tqdm


class DummyTqdmFile(object):
"""Dummy file-like that will write to tqdm"""
file = None

def __init__(self, file):
self.file = file

def write(self, x):
# Avoid print() second call (useless \n)
if len(x.rstrip()) > 0:
tqdm.write(x, file=self.file)


@contextlib.contextmanager
def stdout_redirect_to_tqdm():
save_stdout = sys.stdout
try:
sys.stdout = DummyTqdmFile(sys.stdout)
yield save_stdout
# Relay exceptions
except Exception as exc:
raise exc
# Always restore sys.stdout if necessary
finally:
sys.stdout = save_stdout


def blabla():
print("Foo blabla")


# Redirect stdout to tqdm.write() (don't forget the `as save_stdout`)
with stdout_redirect_to_tqdm() as save_stdout:
# tqdm call need to specify sys.stdout, not sys.stderr (default)
# and dynamic_ncols=True to autodetect console width
for _ in tqdm(range(3), file=save_stdout, dynamic_ncols=True):
blabla()
sleep(.5)

# After the `with`, printing is restored
print('Done!')
13 changes: 8 additions & 5 deletions tqdm/_tqdm.py
Expand Up @@ -418,11 +418,11 @@ def _decr_instances(cls, instance):
pass

@classmethod
def write(cls, s, file=sys.stdout, end="\n"):
def write(cls, s, file=None, end="\n"):
"""
Print a message via tqdm (without overlap with bars)
"""
fp = file
fp = file if file is not None else sys.stdout

# Clear all bars
inst_cleared = []
Expand Down Expand Up @@ -548,7 +548,7 @@ def wrapper(*args, **kwargs):
GroupBy.progress_transform = inner_generator('transform')

def __init__(self, iterable=None, desc=None, total=None, leave=True,
file=sys.stderr, ncols=None, mininterval=0.1,
file=None, ncols=None, mininterval=0.1,
maxinterval=10.0, miniters=None, ascii=None, disable=False,
unit='it', unit_scale=False, dynamic_ncols=False,
smoothing=0.3, bar_format=None, initial=0, position=None,
Expand All @@ -574,7 +574,7 @@ def __init__(self, iterable=None, desc=None, total=None, leave=True,
upon termination of iteration.
file : `io.TextIOWrapper` or `io.StringIO`, optional
Specifies where to output the progress messages
[default: sys.stderr]. Uses `file.write(str)` and `file.flush()`
(default: sys.stderr). Uses `file.write(str)` and `file.flush()`
methods.
ncols : int, optional
The width of the entire output message. If specified,
Expand All @@ -600,7 +600,7 @@ def __init__(self, iterable=None, desc=None, total=None, leave=True,
ascii : bool, optional
If unspecified or False, use unicode (smooth blocks) to fill
the meter. The fallback is to use ASCII characters `1-9 #`.
disable : bool or None, optional
disable : bool, optional
Whether to disable the entire progressbar wrapper
[default: False]. If set to None, disable on non-TTY.
unit : str, optional
Expand Down Expand Up @@ -654,6 +654,9 @@ def __init__(self, iterable=None, desc=None, total=None, leave=True,
self._instances.remove(self)
return

if file is None:
file = sys.stderr

if kwargs:
self.disable = True
self.pos = self._get_free_pos(self)
Expand Down
4 changes: 3 additions & 1 deletion tqdm/_tqdm_gui.py
Expand Up @@ -28,10 +28,12 @@ class tqdm_gui(tqdm): # pragma: no cover
"""

@classmethod
def write(cls, s, file=sys.stdout, end="\n"):
def write(cls, s, file=None, end="\n"):
"""
Print a message via tqdm_gui (just an alias for print)
"""
if file is None:
file = sys.stdout
# TODO: print text on GUI?
file.write(s)
file.write(end)
Expand Down
4 changes: 3 additions & 1 deletion tqdm/_tqdm_notebook.py
Expand Up @@ -148,10 +148,12 @@ def print_status(s='', close=False, bar_style=None):
return print_status

@classmethod
def write(cls, s, file=sys.stdout, end="\n"):
def write(cls, s, file=None, end="\n"):
"""
Print a message via tqdm_notebook (just an alias for print)
"""
if file is None:
file = sys.stdout
# Just an alias for print because overlap is impossible with ipywidgets
file.write(s)
file.write(end)
Expand Down
2 changes: 1 addition & 1 deletion tqdm/_version.py
Expand Up @@ -5,7 +5,7 @@
__all__ = ["__version__"]

# major, minor, patch, -extra
version_info = 4, 12, 2
version_info = 4, 13, 0

# Nice string for the version
__version__ = '.'.join(map(str, version_info))
Expand Down
86 changes: 65 additions & 21 deletions tqdm/tests/tests_tqdm.py
Expand Up @@ -11,6 +11,7 @@
from nose.plugins.skip import SkipTest
from nose.tools import assert_raises
from time import sleep
from contextlib import contextmanager

from tqdm import tqdm
from tqdm import trange
Expand Down Expand Up @@ -467,19 +468,17 @@ def test_max_interval():

# Fast iterations, check if dynamic_miniters triggers
timer.sleep(mininterval) # to force update for t1
tm1.update(total/2)
tm2.update(total/2)
assert int(tm1.miniters) == tm2.miniters == total/2
tm1.update(total / 2)
tm2.update(total / 2)
assert int(tm1.miniters) == tm2.miniters == total / 2

# Slow iterations, check different miniters if mininterval
timer.sleep(maxinterval*2)
tm1.update(total/2)
tm2.update(total/2)
timer.sleep(maxinterval * 2)
tm1.update(total / 2)
tm2.update(total / 2)
res = [tm1.miniters, tm2.miniters]
assert res == [
(total/2)*mininterval/(maxinterval*2),
(total/2)*maxinterval/(maxinterval*2)
]
assert res == [(total / 2) * mininterval / (maxinterval * 2),
(total / 2) * maxinterval / (maxinterval * 2)]

# Same with iterable based tqdm
timer1 = DiscreteTimer() # need 2 timers for each bar because zip not work
Expand All @@ -497,16 +496,16 @@ def test_max_interval():
cpu_timify(t2, timer2)

for i in t1:
if i == ((total/2)-2):
if i == ((total / 2) - 2):
timer1.sleep(mininterval)
if i == (total-1):
timer1.sleep(maxinterval*2)
if i == (total - 1):
timer1.sleep(maxinterval * 2)

for i in t2:
if i == ((total/2)-2):
if i == ((total / 2) - 2):
timer2.sleep(mininterval)
if i == (total-1):
timer2.sleep(maxinterval*2)
if i == (total - 1):
timer2.sleep(maxinterval * 2)

assert t1.miniters == 0.255
assert t2.miniters == 0.5
Expand Down Expand Up @@ -1423,7 +1422,7 @@ class fake_tqdm(object):
# Test if alive, then killed
assert monitor.report()
monitor.exit()
timer.sleep(maxinterval*2) # need to go out of the sleep to die
timer.sleep(maxinterval * 2) # need to go out of the sleep to die
assert not monitor.report()
# assert not monitor.is_alive() # not working dunno why, thread not killed
del monitor
Expand All @@ -1445,12 +1444,12 @@ class fake_tqdm(object):
cpu_timify(t, timer)
# Do a lot of iterations in a small timeframe
# (smaller than monitor interval)
timer.sleep(maxinterval/2) # monitor won't wake up
timer.sleep(maxinterval / 2) # monitor won't wake up
t.update(500)
# check that our fixed miniters is still there
assert t.miniters == 500
# Then do 1 it after monitor interval, so that monitor kicks in
timer.sleep(maxinterval*2)
timer.sleep(maxinterval * 2)
t.update(1)
# Wait for the monitor to get out of sleep's loop and update tqdm..
timeend = timer.time()
Expand All @@ -1464,7 +1463,7 @@ class fake_tqdm(object):
# to ensure that monitor wakes up at some point.

# Try again but already at miniters = 1 so nothing will be done
timer.sleep(maxinterval*2)
timer.sleep(maxinterval * 2)
t.update(2)
timeend = timer.time()
while not (t.monitor.woken >= timeend):
Expand Down Expand Up @@ -1500,7 +1499,7 @@ class fake_tqdm(object):
assert t1.miniters == 500
assert t2.miniters == 500
# Then do 1 it after monitor interval, so that monitor kicks in
timer.sleep(maxinterval*2)
timer.sleep(maxinterval * 2)
t1.update(1)
t2.update(1)
# Wait for the monitor to get out of sleep and update tqdm
Expand Down Expand Up @@ -1551,3 +1550,48 @@ def test_postfix():

out3 = out3[1:-1].split(', ')[3:]
assert out3 == expected_order


class DummyTqdmFile(object):
"""Dummy file-like that will write to tqdm"""
file = None

def __init__(self, file):
self.file = file

def write(self, x):
# Avoid print() second call (useless \n)
if len(x.rstrip()) > 0:
tqdm.write(x, file=self.file)


@contextmanager
def stdout_stderr_redirect_to_tqdm(tqdm_file=sys.stderr):
save_stdout = sys.stdout
save_stderr = sys.stderr
try:
sys.stdout = DummyTqdmFile(tqdm_file)
sys.stderr = DummyTqdmFile(tqdm_file)
yield save_stdout, save_stderr
# Relay exceptions
except Exception as exc:
raise exc
# Always restore sys.stdout if necessary
finally:
sys.stdout = save_stdout
sys.stderr = save_stderr


@with_setup(pretest, posttest)
def test_file_redirection():
"""Test redirection of output"""
with closing(StringIO()) as our_file:
# Redirect stdout to tqdm.write()
with stdout_stderr_redirect_to_tqdm(tqdm_file=our_file):
# as (stdout, stderr)
for _ in trange(3):
print("Such fun")
res = our_file.getvalue()
assert res.count("Such fun\n") == 3
assert "0/3" in res
assert "3/3" in res

0 comments on commit 01f9aa1

Please sign in to comment.