Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-tqdm for openSUSE:Factory checked in at 2021-05-05 20:39:05 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-tqdm (Old) and /work/SRC/openSUSE:Factory/.python-tqdm.new.2988 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-tqdm" Wed May 5 20:39:05 2021 rev:43 rq:889727 version:4.60.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-tqdm/python-tqdm.changes 2021-04-08 21:03:13.281969060 +0200 +++ /work/SRC/openSUSE:Factory/.python-tqdm.new.2988/python-tqdm.changes 2021-05-05 20:39:06.303146948 +0200 @@ -1,0 +2,11 @@ +Fri Apr 30 19:43:53 UTC 2021 - Arun Persaud <a...@gmx.de> + +- update to version 4.60.0: + * add contrib.logging helpers for redirecting to tqdm.write() (#1155 + <- #786) + * support delay in notebook (#1142) + * fix contrib.tmap, tzip not using tqdm_class (#1148) + * add notebook tests (#1143) + * updates & misc minor fixes for documentation + +------------------------------------------------------------------- Old: ---- tqdm-4.59.0.tar.gz New: ---- tqdm-4.60.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-tqdm.spec ++++++ --- /var/tmp/diff_new_pack.duEwJy/_old 2021-05-05 20:39:06.751145026 +0200 +++ /var/tmp/diff_new_pack.duEwJy/_new 2021-05-05 20:39:06.751145026 +0200 @@ -28,7 +28,7 @@ %bcond_with test %endif Name: python-tqdm%{pkg_suffix} -Version: 4.59.0 +Version: 4.60.0 Release: 0 Summary: An extensible progress meter License: MIT AND MPL-2.0 ++++++ tqdm-4.59.0.tar.gz -> tqdm-4.60.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tqdm-4.59.0/.pre-commit-config.yaml new/tqdm-4.60.0/.pre-commit-config.yaml --- old/tqdm-4.59.0/.pre-commit-config.yaml 2021-03-05 21:14:01.000000000 +0100 +++ new/tqdm-4.60.0/.pre-commit-config.yaml 2021-04-06 03:22:58.000000000 +0200 @@ -52,3 +52,8 @@ rev: 5.7.0 hooks: - id: isort +- repo: https://github.com/kynan/nbstripout + rev: 0.3.9 + hooks: + - id: nbstripout + args: ['--keep-count', '--keep-output'] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tqdm-4.59.0/DEMO.ipynb new/tqdm-4.60.0/DEMO.ipynb --- old/tqdm-4.59.0/DEMO.ipynb 2021-03-05 21:14:01.000000000 +0100 +++ new/tqdm-4.60.0/DEMO.ipynb 2021-04-06 03:22:58.000000000 +0200 @@ -1714,15 +1714,12 @@ }, "language_info": { "codemirror_mode": { - "name": "ipython", - "version": 3 + "name": "ipython" }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.8" + "nbconvert_exporter": "python" } }, "nbformat": 4, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tqdm-4.59.0/Makefile new/tqdm-4.60.0/Makefile --- old/tqdm-4.59.0/Makefile 2021-03-05 21:14:01.000000000 +0100 +++ new/tqdm-4.60.0/Makefile 2021-04-06 03:22:58.000000000 +0200 @@ -10,6 +10,7 @@ test pytest testsetup + testnb testcoverage testperf testtimer @@ -59,9 +60,13 @@ python setup.py check --metadata --restructuredtext --strict python setup.py make none +testnb: + pytest tests_notebook.ipynb --nbval --current-env -W=ignore --sanitize-with=setup.cfg --cov=tqdm.notebook --cov-report=term + testcoverage: @make coverclean - pytest -k "not perf" --cov=tqdm --cov-report=xml --cov-report=term --cov-fail-under=80 + pytest tests_notebook.ipynb --cov=tqdm --cov-report= --nbval --current-env --sanitize-with=setup.cfg -W=ignore + pytest -k "not perf" --cov=tqdm --cov-report=xml --cov-report=term --cov-append --cov-fail-under=80 testperf: # do not use coverage (which is extremely slow) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tqdm-4.59.0/PKG-INFO new/tqdm-4.60.0/PKG-INFO --- old/tqdm-4.59.0/PKG-INFO 2021-03-05 21:14:25.801780700 +0100 +++ new/tqdm-4.60.0/PKG-INFO 2021-04-06 03:23:18.062384800 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: tqdm -Version: 4.59.0 +Version: 4.60.0 Summary: Fast, Extensible Progress Meter Home-page: https://github.com/tqdm/tqdm Maintainer: tqdm developers @@ -1333,6 +1333,33 @@ # After the `with`, printing is restored print("Done!") + Redirecting ``logging`` + ~~~~~~~~~~~~~~~~~~~~~~~ + + Similar to ``sys.stdout``/``sys.stderr`` as detailed above, console ``logging`` + may also be redirected to ``tqdm.write()``. + + Warning: if also redirecting ``sys.stdout``/``sys.stderr``, make sure to + redirect ``logging`` first if needed. + + Helper methods are available in ``tqdm.contrib.logging``. For example: + + .. code:: python + + import logging + from tqdm import trange + from tqdm.contrib.logging import logging_redirect_tqdm + + LOG = logging.getLogger(__name__) + + if __name__ == '__main__': + logging.basicConfig(level=logging.INFO) + with logging_redirect_tqdm(): + for i in trange(9): + if i == 4: + LOG.info("console logging redirected to `tqdm.write()`") + # logging restored + Monitoring thread, intervals and miniters ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tqdm-4.59.0/README.rst new/tqdm-4.60.0/README.rst --- old/tqdm-4.59.0/README.rst 2021-03-05 21:14:21.000000000 +0100 +++ new/tqdm-4.60.0/README.rst 2021-04-06 03:23:14.000000000 +0200 @@ -1321,6 +1321,33 @@ # After the `with`, printing is restored print("Done!") +Redirecting ``logging`` +~~~~~~~~~~~~~~~~~~~~~~~ + +Similar to ``sys.stdout``/``sys.stderr`` as detailed above, console ``logging`` +may also be redirected to ``tqdm.write()``. + +Warning: if also redirecting ``sys.stdout``/``sys.stderr``, make sure to +redirect ``logging`` first if needed. + +Helper methods are available in ``tqdm.contrib.logging``. For example: + +.. code:: python + + import logging + from tqdm import trange + from tqdm.contrib.logging import logging_redirect_tqdm + + LOG = logging.getLogger(__name__) + + if __name__ == '__main__': + logging.basicConfig(level=logging.INFO) + with logging_redirect_tqdm(): + for i in trange(9): + if i == 4: + LOG.info("console logging redirected to `tqdm.write()`") + # logging restored + Monitoring thread, intervals and miniters ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tqdm-4.59.0/environment.yml new/tqdm-4.60.0/environment.yml --- old/tqdm-4.59.0/environment.yml 2021-03-05 21:14:01.000000000 +0100 +++ new/tqdm-4.60.0/environment.yml 2021-04-06 03:22:58.000000000 +0200 @@ -8,6 +8,7 @@ - python=3 - pip - ipykernel +- ipywidgets - setuptools - setuptools_scm - toml @@ -20,6 +21,7 @@ - pytest-cov - pytest-timeout - pytest-asyncio # [py>=3.7] +- nbval - flake8 - flake8-bugbear - flake8-comprehensions @@ -27,6 +29,7 @@ # extras - dask # dask - matplotlib # gui +- nbstripout # notebook editing - numpy # pandas, keras, contrib.tenumerate - pandas - tensorflow # keras diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tqdm-4.59.0/setup.cfg new/tqdm-4.60.0/setup.cfg --- old/tqdm-4.59.0/setup.cfg 2021-03-05 21:14:25.801780700 +0100 +++ new/tqdm-4.60.0/setup.cfg 2021-04-06 03:23:18.062384800 +0200 @@ -122,10 +122,18 @@ markers = asyncio slow -python_files = tests_*.py +python_files = tests_*.py tests_*.ipynb testpaths = tests addopts = -v --tb=short -rxs -W=error --durations=0 --durations-min=0.1 +[regex1] +regex = (?<= )[\s\d.]+(it/s|s/it) +replace = ??.??it/s + +[regex2] +regex = 00:0[01]<00:0[01] +replace = 00:00<00:00 + [coverage:run] branch = True include = tqdm/* diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tqdm-4.59.0/tests/tests_contrib.py new/tqdm-4.60.0/tests/tests_contrib.py --- old/tqdm-4.59.0/tests/tests_contrib.py 2021-03-05 21:14:01.000000000 +0100 +++ new/tqdm-4.60.0/tests/tests_contrib.py 2021-04-06 03:22:58.000000000 +0200 @@ -3,6 +3,9 @@ """ import sys +import pytest + +from tqdm import tqdm from tqdm.contrib import tenumerate, tmap, tzip from .tests_tqdm import StringIO, closing, importorskip @@ -13,49 +16,56 @@ return x + 1 -def test_enumerate(): +@pytest.mark.parametrize("tqdm_kwargs", [{}, {"tqdm_class": tqdm}]) +def test_enumerate(tqdm_kwargs): """Test contrib.tenumerate""" with closing(StringIO()) as our_file: a = range(9) - assert list(tenumerate(a, file=our_file)) == list(enumerate(a)) - assert list(tenumerate(a, 42, file=our_file)) == list(enumerate(a, 42)) + assert list(tenumerate(a, file=our_file, **tqdm_kwargs)) == list(enumerate(a)) + assert list(tenumerate(a, 42, file=our_file, **tqdm_kwargs)) == list( + enumerate(a, 42) + ) with closing(StringIO()) as our_file: - _ = list(tenumerate((i for i in a), file=our_file)) + _ = list(tenumerate((i for i in a), file=our_file, **tqdm_kwargs)) assert "100%" not in our_file.getvalue() with closing(StringIO()) as our_file: - _ = list(tenumerate((i for i in a), file=our_file, total=len(a))) + _ = list(tenumerate((i for i in a), file=our_file, total=len(a), **tqdm_kwargs)) assert "100%" in our_file.getvalue() def test_enumerate_numpy(): """Test contrib.tenumerate(numpy.ndarray)""" - np = importorskip('numpy') + np = importorskip("numpy") with closing(StringIO()) as our_file: a = np.random.random((42, 7)) assert list(tenumerate(a, file=our_file)) == list(np.ndenumerate(a)) -def test_zip(): +@pytest.mark.parametrize("tqdm_kwargs", [{}, {"tqdm_class": tqdm}]) +def test_zip(tqdm_kwargs): """Test contrib.tzip""" with closing(StringIO()) as our_file: a = range(9) b = [i + 1 for i in a] if sys.version_info[:1] < (3,): - assert tzip(a, b, file=our_file) == zip(a, b) + assert tzip(a, b, file=our_file, **tqdm_kwargs) == zip(a, b) else: - gen = tzip(a, b, file=our_file) + gen = tzip(a, b, file=our_file, **tqdm_kwargs) assert gen != list(zip(a, b)) assert list(gen) == list(zip(a, b)) -def test_map(): +@pytest.mark.parametrize("tqdm_kwargs", [{}, {"tqdm_class": tqdm}]) +def test_map(tqdm_kwargs): """Test contrib.tmap""" with closing(StringIO()) as our_file: a = range(9) b = [i + 1 for i in a] if sys.version_info[:1] < (3,): - assert tmap(lambda x: x + 1, a, file=our_file) == map(incr, a) + assert tmap(lambda x: x + 1, a, file=our_file, **tqdm_kwargs) == map( + incr, a + ) else: - gen = tmap(lambda x: x + 1, a, file=our_file) + gen = tmap(lambda x: x + 1, a, file=our_file, **tqdm_kwargs) assert gen != b assert list(gen) == b diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tqdm-4.59.0/tests/tests_contrib_logging.py new/tqdm-4.60.0/tests/tests_contrib_logging.py --- old/tqdm-4.59.0/tests/tests_contrib_logging.py 1970-01-01 01:00:00.000000000 +0100 +++ new/tqdm-4.60.0/tests/tests_contrib_logging.py 2021-04-06 03:22:58.000000000 +0200 @@ -0,0 +1,181 @@ +# pylint: disable=missing-module-docstring, missing-class-docstring +# pylint: disable=missing-function-docstring, no-self-use +from __future__ import absolute_import + +import logging +import logging.handlers +import sys +from io import StringIO + +import pytest + +from tqdm import tqdm +from tqdm.contrib.logging import _get_first_found_console_logging_formatter +from tqdm.contrib.logging import _TqdmLoggingHandler as TqdmLoggingHandler +from tqdm.contrib.logging import logging_redirect_tqdm, tqdm_logging_redirect + +from .tests_tqdm import importorskip + +LOGGER = logging.getLogger(__name__) + +TEST_LOGGING_FORMATTER = logging.Formatter() + + +class CustomTqdm(tqdm): + messages = [] + + @classmethod + def write(cls, s, **__): # pylint: disable=arguments-differ + CustomTqdm.messages.append(s) + + +class ErrorRaisingTqdm(tqdm): + exception_class = RuntimeError + + @classmethod + def write(cls, s, **__): # pylint: disable=arguments-differ + raise ErrorRaisingTqdm.exception_class('fail fast') + + +class TestTqdmLoggingHandler: + def test_should_call_tqdm_write(self): + CustomTqdm.messages = [] + logger = logging.Logger('test') + logger.handlers = [TqdmLoggingHandler(CustomTqdm)] + logger.info('test') + assert CustomTqdm.messages == ['test'] + + def test_should_call_handle_error_if_exception_was_thrown(self): + patch = importorskip('unittest.mock').patch + logger = logging.Logger('test') + ErrorRaisingTqdm.exception_class = RuntimeError + handler = TqdmLoggingHandler(ErrorRaisingTqdm) + logger.handlers = [handler] + with patch.object(handler, 'handleError') as mock: + logger.info('test') + assert mock.called + + @pytest.mark.parametrize('exception_class', [ + KeyboardInterrupt, + SystemExit + ]) + def test_should_not_swallow_certain_exceptions(self, exception_class): + logger = logging.Logger('test') + ErrorRaisingTqdm.exception_class = exception_class + handler = TqdmLoggingHandler(ErrorRaisingTqdm) + logger.handlers = [handler] + with pytest.raises(exception_class): + logger.info('test') + + +class TestGetFirstFoundConsoleLoggingFormatter: + def test_should_return_none_for_no_handlers(self): + assert _get_first_found_console_logging_formatter([]) is None + + def test_should_return_none_without_stream_handler(self): + handler = logging.handlers.MemoryHandler(capacity=1) + handler.formatter = TEST_LOGGING_FORMATTER + assert _get_first_found_console_logging_formatter([handler]) is None + + def test_should_return_none_for_stream_handler_not_stdout_or_stderr(self): + handler = logging.StreamHandler(StringIO()) + handler.formatter = TEST_LOGGING_FORMATTER + assert _get_first_found_console_logging_formatter([handler]) is None + + def test_should_return_stream_handler_formatter_if_stream_is_stdout(self): + handler = logging.StreamHandler(sys.stdout) + handler.formatter = TEST_LOGGING_FORMATTER + assert _get_first_found_console_logging_formatter( + [handler] + ) == TEST_LOGGING_FORMATTER + + def test_should_return_stream_handler_formatter_if_stream_is_stderr(self): + handler = logging.StreamHandler(sys.stderr) + handler.formatter = TEST_LOGGING_FORMATTER + assert _get_first_found_console_logging_formatter( + [handler] + ) == TEST_LOGGING_FORMATTER + + +class TestRedirectLoggingToTqdm: + def test_should_add_and_remove_tqdm_handler(self): + logger = logging.Logger('test') + with logging_redirect_tqdm(loggers=[logger]): + assert len(logger.handlers) == 1 + assert isinstance(logger.handlers[0], TqdmLoggingHandler) + assert not logger.handlers + + def test_should_remove_and_restore_console_handlers(self): + logger = logging.Logger('test') + stderr_console_handler = logging.StreamHandler(sys.stderr) + stdout_console_handler = logging.StreamHandler(sys.stderr) + logger.handlers = [stderr_console_handler, stdout_console_handler] + with logging_redirect_tqdm(loggers=[logger]): + assert len(logger.handlers) == 1 + assert isinstance(logger.handlers[0], TqdmLoggingHandler) + assert logger.handlers == [stderr_console_handler, stdout_console_handler] + + def test_should_inherit_console_logger_formatter(self): + logger = logging.Logger('test') + formatter = logging.Formatter('custom: %(message)s') + console_handler = logging.StreamHandler(sys.stderr) + console_handler.setFormatter(formatter) + logger.handlers = [console_handler] + with logging_redirect_tqdm(loggers=[logger]): + assert logger.handlers[0].formatter == formatter + + def test_should_not_remove_stream_handlers_not_fot_stdout_or_stderr(self): + logger = logging.Logger('test') + stream_handler = logging.StreamHandler(StringIO()) + logger.addHandler(stream_handler) + with logging_redirect_tqdm(loggers=[logger]): + assert len(logger.handlers) == 2 + assert logger.handlers[0] == stream_handler + assert isinstance(logger.handlers[1], TqdmLoggingHandler) + assert logger.handlers == [stream_handler] + + +class TestTqdmWithLoggingRedirect: + def test_should_add_and_remove_handler_from_root_logger_by_default(self): + original_handlers = list(logging.root.handlers) + with tqdm_logging_redirect(total=1) as pbar: + assert isinstance(logging.root.handlers[-1], TqdmLoggingHandler) + LOGGER.info('test') + pbar.update(1) + assert logging.root.handlers == original_handlers + + def test_should_add_and_remove_handler_from_custom_logger(self): + logger = logging.Logger('test') + with tqdm_logging_redirect(total=1, loggers=[logger]) as pbar: + assert len(logger.handlers) == 1 + assert isinstance(logger.handlers[0], TqdmLoggingHandler) + logger.info('test') + pbar.update(1) + assert not logger.handlers + + def test_should_not_fail_with_logger_without_console_handler(self): + logger = logging.Logger('test') + logger.handlers = [] + with tqdm_logging_redirect(total=1, loggers=[logger]): + logger.info('test') + assert not logger.handlers + + def test_should_format_message(self): + logger = logging.Logger('test') + console_handler = logging.StreamHandler(sys.stdout) + console_handler.setFormatter(logging.Formatter( + r'prefix:%(message)s' + )) + logger.handlers = [console_handler] + CustomTqdm.messages = [] + with tqdm_logging_redirect(loggers=[logger], tqdm_class=CustomTqdm): + logger.info('test') + assert CustomTqdm.messages == ['prefix:test'] + + def test_use_root_logger_by_default_and_write_to_custom_tqdm(self): + logger = logging.root + CustomTqdm.messages = [] + with tqdm_logging_redirect(total=1, tqdm_class=CustomTqdm) as pbar: + assert isinstance(pbar, CustomTqdm) + logger.info('test') + assert CustomTqdm.messages == ['test'] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tqdm-4.59.0/tests_notebook.ipynb new/tqdm-4.60.0/tests_notebook.ipynb --- old/tqdm-4.59.0/tests_notebook.ipynb 1970-01-01 01:00:00.000000000 +0100 +++ new/tqdm-4.60.0/tests_notebook.ipynb 2021-04-06 03:22:58.000000000 +0200 @@ -0,0 +1,512 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This file is part of the [test suite](./tests) and will be moved there when [nbval#116](https://github.com/computationalmodelling/nbval/issues/116#issuecomment-793148404) is fixed.\n", + "\n", + "See [DEMO.ipynb](DEMO.ipynb) instead for notebook examples." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from functools import partial\n", + "from time import sleep\n", + "\n", + "from tqdm.notebook import tqdm_notebook\n", + "from tqdm.notebook import tnrange\n", + "\n", + "# avoid displaying widgets by default (pollutes output cells)\n", + "tqdm = partial(tqdm_notebook, display=False)\n", + "trange = partial(tnrange, display=False)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Help on function display in module tqdm.notebook:\n", + "\n", + "display(self, msg=None, pos=None, close=False, bar_style=None, check_delay=True)\n", + " Use `self.sp` to display `msg` in the specified `pos`.\n", + " \n", + " Consider overloading this function when inheriting to use e.g.:\n", + " `self.some_frontend(**self.format_dict)` instead of `self.sp`.\n", + " \n", + " Parameters\n", + " ----------\n", + " msg : str, optional. What to display (default: `repr(self)`).\n", + " pos : int, optional. Position to `moveto`\n", + " (default: `abs(self.pos)`).\n", + "\n" + ] + } + ], + "source": [ + "help(tqdm_notebook.display)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "7c18c038bf964b55941e228503292506", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + " 0%| | 0/9 [00:00<?, ?it/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0\n", + "1\n", + "2\n", + "3\n", + "4\n", + "5\n", + "6\n", + "7\n", + "8\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "e29668be41ca4e40b16fb98572b333a5", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + " 0%| | 0/9 [00:00<?, ?it/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# NBVAL_TEST_NAME: basic use\n", + "with tqdm_notebook(range(9)) as t:\n", + " for i in t:\n", + " print(i)\n", + "assert t.container.children[1].bar_style == 'success'\n", + "\n", + "t = tqdm_notebook(total=9)\n", + "t.update()\n", + "t.refresh()" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " 11%|??? | 1/9 [00:00<00:00, 17.73it/s]\n", + " 20%|?????? | 1/5 [00:00<00:00, 341.50it/s]\n" + ] + } + ], + "source": [ + "# NBVAL_TEST_NAME: reset\n", + "print(t)\n", + "t.reset(total=5)\n", + "t.update(1)\n", + "print(t)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "# NBVAL_TEST_NAME: bar_style\n", + "assert t.container.children[1].bar_style != 'danger'\n", + "t.close()\n", + "assert t.container.children[1].bar_style == 'danger'" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " 0%| | 0/8 [00:00<?, ?it/s]\n", + " 0%| | 0/8 [00:00<?, ?it/s]\n", + "1\n", + " 0%| | 0/8 [00:00<?, ?it/s]\n", + " 0%| | 0/8 [00:00<?, ?it/s]\n" + ] + } + ], + "source": [ + "# NBVAL_TEST_NAME: repr\n", + "with trange(1, 9) as t:\n", + " print(t)\n", + " print(t.container)\n", + " it = iter(t)\n", + " print(next(it))\n", + " print(t)\n", + " print(t.container)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "t = trange(9)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " 0%| | 0/9 [00:00<?, ?it/s]\n", + " 0%| | 0/9 [00:00<?, ?it/s]\n" + ] + } + ], + "source": [ + "# NBVAL_TEST_NAME: display pre\n", + "print(t)\n", + "print(t.container)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "for i in t:\n", + " pass" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "100%|??????????????????????????????| 9/9 [00:00<00:00, 132.23it/s]\n", + "100%|##########| 9/9 [00:00<00:00, 131.02it/s]\n" + ] + } + ], + "source": [ + "# NBVAL_TEST_NAME: display post\n", + "print(t)\n", + "print(t.container)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "no total: 0it [00:00, ?it/s]\n", + "no total: 1it [00:00, 47.83it/s]\n" + ] + } + ], + "source": [ + "# NBVAL_TEST_NAME: no total\n", + "with tqdm(desc=\"no total\") as t:\n", + " print(t)\n", + " t.update()\n", + " print(t)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " 0%| | 0/9 [00:00<?, ?it/s]\n", + " 11%|???????????? | 1/9 [00:00<00:00, 45.06it/s]\n" + ] + } + ], + "source": [ + "# NBVAL_TEST_NAME: ncols\n", + "with trange(9, ncols=66) as t:\n", + " print(t)\n", + " for i in t:\n", + " if i == 1:\n", + " break\n", + " print(t)" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " 0%| | 0/1 [00:00<?, ?it/s]\n", + "100%|??????????????????????????????| 1/1 [00:00<00:00, 54.60it/s]\n", + " 0%| | 0/9 [00:00<?, ?it/s]\n", + " 11%|??? | 1/9 [00:00<00:00, 57.43it/s]\n" + ] + } + ], + "source": [ + "# NBVAL_TEST_NAME: leave\n", + "assert (False, None) != (getattr(t.container, \"visible\", False), getattr(t.container, \"_ipython_display_\", None))\n", + "for total in (1, 9):\n", + " with tqdm(total=total, leave=False) as t:\n", + " print(t)\n", + " t.update()\n", + " print(t)\n", + " assert total != 1 or (False, None) == (\n", + " getattr(t.container, \"visible\", False), getattr(t.container, \"_ipython_display_\", None)\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0it [00:00, ?it/s]\n", + "1it [00:00, 47.87it/s]\n" + ] + } + ], + "source": [ + "# NBVAL_TEST_NAME: no total\n", + "with tqdm() as t:\n", + " print(t)\n", + " t.update()\n", + " print(t)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "disable: None\n", + " 0%| | 0/1 [00:00<?, ?it/s]\n", + "100%|??????????????????????????????| 1/1 [00:00<00:00, 53.24it/s]\n", + " 0%| | 0/9 [00:00<?, ?it/s]\n", + " 11%|??? | 1/9 [00:00<00:00, 1752.01it/s]\n", + "0it [00:00, ?it/s]\n", + "1it [00:00, 35.88it/s]\n", + " 0%| | 0/1 [00:00<?, ?it/s]\n", + "100%|??????????????????????????????| 1/1 [00:00<00:00, 1880.85it/s]\n", + "disable: True\n", + " 0%| | 0/1 [00:00<?, ?it/s]\n", + " 0%| | 0/1 [00:00<?, ?it/s]\n", + " 0%| | 0/9 [00:00<?, ?it/s]\n", + " 0%| | 0/9 [00:00<?, ?it/s]\n", + "0it [00:00, ?it/s]\n", + "0it [00:00, ?it/s]\n", + " 0%| | 0/1 [00:00<?, ?it/s]\n", + " 0%| | 0/1 [00:00<?, ?it/s]\n" + ] + } + ], + "source": [ + "# NBVAL_TEST_NAME: reset and disable\n", + "for disable in (None, True):\n", + " print(\"disable:\", disable)\n", + " with tqdm(total=1, disable=disable) as t:\n", + " print(t)\n", + " t.update()\n", + " print(t)\n", + "\n", + " t.reset(total=9)\n", + " print(t)\n", + " t.update()\n", + " print(t)\n", + " with tqdm(disable=disable) as t:\n", + " print(t)\n", + " t.update()\n", + " print(t)\n", + "\n", + " t.reset(total=1)\n", + " print(t)\n", + " t.update()\n", + " print(t)" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " 0%|| 0/1 [00:00<?, ?it/s]\n", + "100%|| 1/1 [00:00<00:00, 32.57it/s]\n", + " 0%| \n", + "100%|??????????????????????????????\n" + ] + } + ], + "source": [ + "# NBVAL_TEST_NAME: bar_format\n", + "with tqdm(total=1, bar_format='{l_bar}{r_bar}') as t:\n", + " print(t)\n", + " t.update()\n", + " print(t)\n", + "\n", + "with tqdm(total=1, bar_format='{l_bar}{bar}') as t:\n", + " print(t)\n", + " t.update()\n", + " print(t)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " 0%|\u001b[33m \u001b[0m| 0/1 [00:00<?, ?it/s]\n", + "100%|\u001b[33m??????????????????????????????\u001b[0m| 1/1 [00:00<00:00, 47.14it/s]\n" + ] + } + ], + "source": [ + "# NBVAL_TEST_NAME: colour\n", + "assert t.colour != 'yellow'\n", + "with tqdm(total=1, colour='yellow') as t:\n", + " print(t)\n", + " t.update()\n", + " print(t)\n", + " assert t.colour == 'yellow'" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [], + "source": [ + "# NBVAL_TEST_NAME: delay no trigger\n", + "with tqdm_notebook(total=1, delay=10) as t:\n", + " t.update()" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "fe102eedbb4f437783fbd0cff32f6613", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "100%|##########| 1/1 [00:00<00:00, 7.68it/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# NBVAL_TEST_NAME: delay trigger\n", + "with tqdm_notebook(total=1, delay=0.1) as t:\n", + " sleep(0.1)\n", + " t.update()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python [conda env:tqdm]", + "language": "python", + "name": "conda-env-tqdm-py" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython" + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tqdm-4.59.0/tox.ini new/tqdm-4.60.0/tox.ini --- old/tqdm-4.59.0/tox.ini 2021-03-05 21:14:01.000000000 +0100 +++ new/tqdm-4.60.0/tox.ini 2021-04-06 03:22:58.000000000 +0200 @@ -4,7 +4,7 @@ # and then run "tox" from this directory. [tox] -# deprecation warning: py{27,py2,34,35} +# deprecation warning: py{27,py2,34,35,36} envlist=py{27,34,35,36,37,38,39,py2,py3}{,-tf}{,-keras}, perf, setup.py isolated_build=True @@ -13,7 +13,9 @@ pytest pytest-cov pytest-timeout - py{37,38,39}: pytest-asyncio + py3{7,8,9}: pytest-asyncio + py3{6,7,8,9}: ipywidgets + py3{6,7,8,9}: git+https://github.com/casperdcl/nbval.git@named_cells#egg=nbval coverage coveralls codecov @@ -32,10 +34,11 @@ numpy pandas keras: keras - py{36,37,38,39}: rich + py3{6,7,8,9}: rich tf: tensorflow commands= - pytest --cov=tqdm --cov-report=xml --cov-report=term -k "not perf" + py3{6,7,8,9}: pytest --cov=tqdm --cov-report= tests_notebook.ipynb --nbval --current-env -W=ignore --sanitize-with=setup.cfg + pytest --cov=tqdm --cov-report=xml --cov-report=term --cov-append -k "not perf" {[core]commands} allowlist_externals=codacy diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tqdm-4.59.0/tqdm/_dist_ver.py new/tqdm-4.60.0/tqdm/_dist_ver.py --- old/tqdm-4.59.0/tqdm/_dist_ver.py 2021-03-05 21:14:25.000000000 +0100 +++ new/tqdm-4.60.0/tqdm/_dist_ver.py 2021-04-06 03:23:17.000000000 +0200 @@ -1 +1 @@ -__version__ = '4.59.0' +__version__ = '4.60.0' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tqdm-4.59.0/tqdm/contrib/__init__.py new/tqdm-4.60.0/tqdm/contrib/__init__.py --- old/tqdm-4.59.0/tqdm/contrib/__init__.py 2021-03-05 21:14:01.000000000 +0100 +++ new/tqdm-4.60.0/tqdm/contrib/__init__.py 2021-04-06 03:22:58.000000000 +0200 @@ -16,6 +16,7 @@ class DummyTqdmFile(ObjectWrapper): """Dummy file-like that will write to tqdm""" + def __init__(self, wrapped): super(DummyTqdmFile, self).__init__(wrapped) self._buf = [] @@ -80,7 +81,7 @@ """ kwargs = tqdm_kwargs.copy() tqdm_class = kwargs.pop("tqdm_class", tqdm_auto) - for i in zip(tqdm_class(iter1, **tqdm_kwargs), *iter2plus): + for i in zip(tqdm_class(iter1, **kwargs), *iter2plus): yield i diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tqdm-4.59.0/tqdm/contrib/logging.py new/tqdm-4.60.0/tqdm/contrib/logging.py --- old/tqdm-4.59.0/tqdm/contrib/logging.py 1970-01-01 01:00:00.000000000 +0100 +++ new/tqdm-4.60.0/tqdm/contrib/logging.py 2021-04-06 03:22:58.000000000 +0200 @@ -0,0 +1,126 @@ +""" +Helper functionality for interoperability with stdlib `logging`. +""" +from __future__ import absolute_import + +import logging +import sys +from contextlib import contextmanager + +try: + from typing import Iterator, List, Optional, Type # pylint: disable=unused-import +except ImportError: + pass + +from ..std import tqdm as std_tqdm + + +class _TqdmLoggingHandler(logging.StreamHandler): + def __init__( + self, + tqdm_class=std_tqdm # type: Type[std_tqdm] + ): + super(_TqdmLoggingHandler, self).__init__() + self.tqdm_class = tqdm_class + + def emit(self, record): + try: + msg = self.format(record) + self.tqdm_class.write(msg) + self.flush() + except (KeyboardInterrupt, SystemExit): + raise + except: # noqa pylint: disable=bare-except + self.handleError(record) + + +def _is_console_logging_handler(handler): + return (isinstance(handler, logging.StreamHandler) + and handler.stream in {sys.stdout, sys.stderr}) + + +def _get_first_found_console_logging_formatter(handlers): + for handler in handlers: + if _is_console_logging_handler(handler): + return handler.formatter + + +@contextmanager +def logging_redirect_tqdm( + loggers=None, # type: Optional[List[logging.Logger]], + tqdm_class=std_tqdm # type: Type[std_tqdm] +): + # type: (...) -> Iterator[None] + """ + Context manager redirecting console logging to `tqdm.write()`, leaving + other logging handlers (e.g. log files) unaffected. + + Parameters + ---------- + loggers : list, optional + Which handlers to redirect (default: [logging.root]). + tqdm_class : optional + + Example + ------- + ```python + import logging + from tqdm import trange + from tqdm.contrib.logging import logging_redirect_tqdm + + LOG = logging.getLogger(__name__) + + if __name__ == '__main__': + logging.basicConfig(level=logging.INFO) + with logging_redirect_tqdm(): + for i in trange(9): + if i == 4: + LOG.info("console logging redirected to `tqdm.write()`") + # logging restored + ``` + """ + if loggers is None: + loggers = [logging.root] + original_handlers_list = [logger.handlers for logger in loggers] + try: + for logger in loggers: + tqdm_handler = _TqdmLoggingHandler(tqdm_class) + tqdm_handler.setFormatter( + _get_first_found_console_logging_formatter(logger.handlers)) + logger.handlers = [ + handler for handler in logger.handlers + if not _is_console_logging_handler(handler)] + [tqdm_handler] + yield + finally: + for logger, original_handlers in zip(loggers, original_handlers_list): + logger.handlers = original_handlers + + +@contextmanager +def tqdm_logging_redirect( + *args, + # loggers=None, # type: Optional[List[logging.Logger]] + # tqdm=None, # type: Optional[Type[tqdm.tqdm]] + **kwargs +): + # type: (...) -> Iterator[None] + """ + Convenience shortcut for: + ```python + with tqdm_class(*args, **tqdm_kwargs) as pbar: + with logging_redirect_tqdm(loggers=loggers, tqdm_class=tqdm_class): + yield pbar + ``` + + Parameters + ---------- + tqdm_class : optional, (default: tqdm.std.tqdm). + loggers : optional, list. + **tqdm_kwargs : passed to `tqdm_class`. + """ + tqdm_kwargs = kwargs.copy() + loggers = tqdm_kwargs.pop('loggers', None) + tqdm_class = tqdm_kwargs.pop('tqdm_class', std_tqdm) + with tqdm_class(*args, **tqdm_kwargs) as pbar: + with logging_redirect_tqdm(loggers=loggers, tqdm_class=tqdm_class): + yield pbar diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tqdm-4.59.0/tqdm/dask.py new/tqdm-4.60.0/tqdm/dask.py --- old/tqdm-4.59.0/tqdm/dask.py 2021-03-05 21:14:01.000000000 +0100 +++ new/tqdm-4.60.0/tqdm/dask.py 2021-04-06 03:22:58.000000000 +0200 @@ -17,7 +17,7 @@ """ Parameters ---------- - tqdm_class : optional + tqdm_class : optional `tqdm` class to use for bars [default: `tqdm.auto.tqdm`]. tqdm_kwargs : optional Any other arguments used for all bars. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tqdm-4.59.0/tqdm/keras.py new/tqdm-4.60.0/tqdm/keras.py --- old/tqdm-4.59.0/tqdm/keras.py 2021-03-05 21:14:01.000000000 +0100 +++ new/tqdm-4.60.0/tqdm/keras.py 2021-04-06 03:22:58.000000000 +0200 @@ -45,7 +45,7 @@ 0: epoch, 1: batch (transient), 2: batch. [default: 1]. Will be set to `0` unless both `data_size` and `batch_size` are given. - tqdm_class : optional + tqdm_class : optional `tqdm` class to use for bars [default: `tqdm.auto.tqdm`]. tqdm_kwargs : optional Any other arguments used for all bars. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tqdm-4.59.0/tqdm/notebook.py new/tqdm-4.60.0/tqdm/notebook.py --- old/tqdm-4.59.0/tqdm/notebook.py 2021-03-05 21:14:01.000000000 +0100 +++ new/tqdm-4.60.0/tqdm/notebook.py 2021-04-06 03:22:58.000000000 +0200 @@ -145,7 +145,7 @@ def display(self, msg=None, pos=None, # additional signals - close=False, bar_style=None): + close=False, bar_style=None, check_delay=True): # Note: contrary to native tqdm, msg='' does NOT clear bar # goal is to keep all infos if error happens so user knows # at which iteration the loop failed. @@ -190,6 +190,10 @@ except AttributeError: self.container.visible = False + if check_delay and self.delay > 0 and not self.displayed: + display(self.container) + self.displayed = True + @property def colour(self): if hasattr(self, 'container'): @@ -234,14 +238,16 @@ total = self.total * unit_scale if self.total else self.total self.container = self.status_printer(self.fp, total, self.desc, self.ncols) self.container.pbar = self - if display_here: + self.displayed = False + if display_here and self.delay <= 0: display(self.container) + self.displayed = True self.disp = self.display self.colour = colour # Print initial bar state if not self.disable: - self.display() + self.display(check_delay=False) def __iter__(self): try: @@ -268,16 +274,18 @@ # since this could be a shared bar which the user will `reset()` def close(self): + if self.disable: + return super(tqdm_notebook, self).close() # Try to detect if there was an error or KeyboardInterrupt # in manual mode: if n < total, things probably got wrong if self.total and self.n < self.total: - self.disp(bar_style='danger') + self.disp(bar_style='danger', check_delay=False) else: if self.leave: - self.disp(bar_style='success') + self.disp(bar_style='success', check_delay=False) else: - self.disp(close=True) + self.disp(close=True, check_delay=False) def clear(self, *_, **__): pass diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tqdm-4.59.0/tqdm.egg-info/PKG-INFO new/tqdm-4.60.0/tqdm.egg-info/PKG-INFO --- old/tqdm-4.59.0/tqdm.egg-info/PKG-INFO 2021-03-05 21:14:25.000000000 +0100 +++ new/tqdm-4.60.0/tqdm.egg-info/PKG-INFO 2021-04-06 03:23:17.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: tqdm -Version: 4.59.0 +Version: 4.60.0 Summary: Fast, Extensible Progress Meter Home-page: https://github.com/tqdm/tqdm Maintainer: tqdm developers @@ -1333,6 +1333,33 @@ # After the `with`, printing is restored print("Done!") + Redirecting ``logging`` + ~~~~~~~~~~~~~~~~~~~~~~~ + + Similar to ``sys.stdout``/``sys.stderr`` as detailed above, console ``logging`` + may also be redirected to ``tqdm.write()``. + + Warning: if also redirecting ``sys.stdout``/``sys.stderr``, make sure to + redirect ``logging`` first if needed. + + Helper methods are available in ``tqdm.contrib.logging``. For example: + + .. code:: python + + import logging + from tqdm import trange + from tqdm.contrib.logging import logging_redirect_tqdm + + LOG = logging.getLogger(__name__) + + if __name__ == '__main__': + logging.basicConfig(level=logging.INFO) + with logging_redirect_tqdm(): + for i in trange(9): + if i == 4: + LOG.info("console logging redirected to `tqdm.write()`") + # logging restored + Monitoring thread, intervals and miniters ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tqdm-4.59.0/tqdm.egg-info/SOURCES.txt new/tqdm-4.60.0/tqdm.egg-info/SOURCES.txt --- old/tqdm-4.59.0/tqdm.egg-info/SOURCES.txt 2021-03-05 21:14:25.000000000 +0100 +++ new/tqdm-4.60.0/tqdm.egg-info/SOURCES.txt 2021-04-06 03:23:17.000000000 +0200 @@ -11,6 +11,7 @@ pyproject.toml setup.cfg setup.py +tests_notebook.ipynb tox.ini examples/7zx.py examples/async_coroutines.py @@ -31,6 +32,7 @@ tests/tests_asyncio.py tests/tests_concurrent.py tests/tests_contrib.py +tests/tests_contrib_logging.py tests/tests_dask.py tests/tests_gui.py tests/tests_itertools.py @@ -80,5 +82,6 @@ tqdm/contrib/concurrent.py tqdm/contrib/discord.py tqdm/contrib/itertools.py +tqdm/contrib/logging.py tqdm/contrib/telegram.py tqdm/contrib/utils_worker.py \ No newline at end of file