1
0
Fork 0

Compare commits

..

No commits in common. "9769cec2871249ca9aa8967f712bce45f0c98659" and "2da88b2fbca9960da1ed63ddc6663dcb80096191" have entirely different histories.

62 changed files with 1398 additions and 1155 deletions

View file

@ -2,7 +2,7 @@ default_language_version:
python: python3 python: python3
repos: repos:
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0 rev: v4.1.0
hooks: hooks:
- id: check-added-large-files - id: check-added-large-files
- id: check-case-conflict - id: check-case-conflict
@ -37,9 +37,9 @@ repos:
- numpy - numpy
- pandas - pandas
- pytest-timeout - pytest-timeout
- pytest-asyncio>=0.24 - pytest-asyncio
- repo: https://github.com/PyCQA/flake8 - repo: https://gitlab.com/pycqa/flake8
rev: 7.1.1 rev: 3.9.2
hooks: hooks:
- id: flake8 - id: flake8
args: [-j8] args: [-j8]
@ -49,14 +49,14 @@ repos:
- flake8-comprehensions - flake8-comprehensions
- flake8-debugger - flake8-debugger
- flake8-isort - flake8-isort
- flake8-pyproject
- flake8-string-format - flake8-string-format
- flake8-type-annotations
- repo: https://github.com/PyCQA/isort - repo: https://github.com/PyCQA/isort
rev: 5.13.2 rev: 5.10.1
hooks: hooks:
- id: isort - id: isort
- repo: https://github.com/kynan/nbstripout - repo: https://github.com/kynan/nbstripout
rev: 0.7.1 rev: 0.5.0
hooks: hooks:
- id: nbstripout - id: nbstripout
args: [--keep-count, --keep-output] args: [--keep-count, --keep-output]

View file

@ -12,7 +12,7 @@ Makefile:
``` ```
make [<alias>] # on UNIX-like environments make [<alias>] # on UNIX-like environments
python -m pymake [<alias>] # if make is unavailable python setup.py make [<alias>] # if make is unavailable
``` ```
The latter depends on [`py-make>=0.1.0`](https://github.com/tqdm/py-make). The latter depends on [`py-make>=0.1.0`](https://github.com/tqdm/py-make).
@ -51,7 +51,7 @@ However it would be helpful to bear in mind:
* use two spaces between variable name and colon, specify a type, and most likely state that it's optional: `VAR<space><space>:<space>TYPE[, optional]` * use two spaces between variable name and colon, specify a type, and most likely state that it's optional: `VAR<space><space>:<space>TYPE[, optional]`
* use [default: ...] for default values of keyword arguments * use [default: ...] for default values of keyword arguments
+ will not break backward compatibility unless there is a very good reason + will not break backward compatibility unless there is a very good reason
* e.g. breaking py26 compatibility purely in favour of minor readability changes (such as converting `dict(a=1)` to `{'a': 1}`) is not a good enough reason * e.g. breaking py26 compatibility purely in favour of readability (such as converting `dict(a=1)` to `{'a': 1}`) is not a good enough reason
+ API changes should be discussed carefully + API changes should be discussed carefully
+ remember, with millions of downloads per month, `tqdm` must be extremely fast and reliable + remember, with millions of downloads per month, `tqdm` must be extremely fast and reliable
- Any other kind of change may be included in a (possibly new) submodule - Any other kind of change may be included in a (possibly new) submodule
@ -85,7 +85,7 @@ The standard way to run the tests:
- run the following command: - run the following command:
``` ```
[python -m py]make test [python setup.py] make test
# or: # or:
tox --skip-missing-interpreters tox --skip-missing-interpreters
``` ```
@ -97,19 +97,19 @@ versions of Python.)
Note: to install all versions of the Python interpreter that are specified Note: to install all versions of the Python interpreter that are specified
in [tox.ini](https://github.com/tqdm/tqdm/blob/master/tox.ini), in [tox.ini](https://github.com/tqdm/tqdm/blob/master/tox.ini),
you can use `MiniConda` to install a minimal setup. You must also ensure you can use `MiniConda` to install a minimal setup. You must also make sure
that each distribution has an alias to call the Python interpreter that each distribution has an alias to call the Python interpreter:
(e.g. `python312` for Python 3.12's interpreter). `python27` for Python 2.7's interpreter, `python32` for Python 3.2's, etc.
### Alternative unit tests with pytest ### Alternative unit tests with pytest
Alternatively, use `pytest` to run the tests just for the current Python version: Alternatively, use `pytest` to run the tests just for the current Python version:
- install test requirements: `[python -m py]make install_test` - install test requirements: `[python setup.py] make install_test`
- run the following command: - run the following command:
``` ```
[python -m py]make alltests [python setup.py] make alltests
``` ```
@ -118,9 +118,9 @@ Alternatively, use `pytest` to run the tests just for the current Python version
This section is intended for the project's maintainers and describes This section is intended for the project's maintainers and describes
how to build and upload a new release. Once again, how to build and upload a new release. Once again,
`[python -m py]make [<alias>]` will help. `[python setup.py] make [<alias>]` will help.
Also consider `pip install`ing development utilities: Also consider `pip install`ing development utilities:
`[python -m py]make install_build` at a minimum, or a more thorough `conda env create`. `[python setup.py] make install_build` at a minimum, or a more thorough `conda env create`.
## Pre-commit Hook ## Pre-commit Hook
@ -137,20 +137,20 @@ The `tqdm` repository managers should:
- follow the [Semantic Versioning](https://semver.org) convention for tagging - follow the [Semantic Versioning](https://semver.org) convention for tagging
## Checking `pyproject.toml` ## Checking setup.py
To check that the `pyproject.toml` file is compliant with PyPI To check that the `setup.py`/`setup.cfg`/`pyproject.toml` file is compliant with PyPI
requirements (e.g. version number; reStructuredText in `README.rst`) use: requirements (e.g. version number; reStructuredText in `README.rst`) use:
``` ```
[python -m py]make testsetup [python setup.py] make testsetup
``` ```
To upload just metadata (including overwriting mistakenly uploaded metadata) To upload just metadata (including overwriting mistakenly uploaded metadata)
to PyPI, use: to PyPI, use:
``` ```
[python -m py]make pypimeta [python setup.py] make pypimeta
``` ```
@ -199,7 +199,7 @@ git merge --no-ff pr-branch-name
### 4 Test ### 4 Test
``` ```
[python -m py]make alltests [python setup.py] make alltests
``` ```
### 5 Push to master ### 5 Push to master
@ -233,7 +233,7 @@ Manual instructions are given below in case of failure.
Build `tqdm` into a distributable python package: Build `tqdm` into a distributable python package:
``` ```
[python -m py]make build [python setup.py] make build
``` ```
This will generate several builds in the `dist/` folder. On non-windows This will generate several builds in the `dist/` folder. On non-windows
@ -243,13 +243,13 @@ Finally, upload everything to PyPI. This can be done easily using the
[twine](https://github.com/pypa/twine) module: [twine](https://github.com/pypa/twine) module:
``` ```
[python -m py]make pypi [python setup.py] make pypi
``` ```
Also, the new release can (should) be added to GitHub by creating a new Also, the new release can (should) be added to GitHub by creating a new
release from the [web interface](https://github.com/tqdm/tqdm/releases); release from the [web interface](https://github.com/tqdm/tqdm/releases);
uploading packages from the `dist/` folder uploading packages from the `dist/` folder
created by `[python -m py]make build`. created by `[python setup.py] make build`.
The [wiki] can be automatically updated with GitHub release notes by The [wiki] can be automatically updated with GitHub release notes by
running `make` within the wiki repository. running `make` within the wiki repository.
@ -282,7 +282,7 @@ before the real deployment
- in case of a mistake, you can delete an uploaded release on PyPI, but you - in case of a mistake, you can delete an uploaded release on PyPI, but you
cannot re-upload another with the same version number cannot re-upload another with the same version number
- in case of a mistake in the metadata on PyPI (e.g. bad README), - in case of a mistake in the metadata on PyPI (e.g. bad README),
updating just the metadata is possible: `[python -m py]make pypimeta` updating just the metadata is possible: `[python setup.py] make pypimeta`
## Updating Websites ## Updating Websites
@ -333,16 +333,16 @@ to assist with maintenance.
For experienced devs, once happy with local master, follow the steps below. For experienced devs, once happy with local master, follow the steps below.
Much is automated so really it's steps 1-5, then 11(a). Much is automated so really it's steps 1-5, then 11(a).
1. test (`[python -m py]make alltests` or rely on `pre-commit`) 1. test (`[python setup.py] make alltests` or rely on `pre-commit`)
2. `git commit [--amend] # -m "bump version"` 2. `git commit [--amend] # -m "bump version"`
3. `git push` 3. `git push`
4. wait for tests to pass 4. wait for tests to pass
a) in case of failure, fix and go back to (1) a) in case of failure, fix and go back to (1)
5. `git tag vM.m.p && git push --tags` or comment `/tag vM.m.p commit_hash` 5. `git tag vM.m.p && git push --tags` or comment `/tag vM.m.p commit_hash`
6. **`[AUTO:GHA]`** `[python -m py]make distclean` 6. **`[AUTO:GHA]`** `[python setup.py] make distclean`
7. **`[AUTO:GHA]`** `[python -m py]make build` 7. **`[AUTO:GHA]`** `[python setup.py] make build`
8. **`[AUTO:GHA]`** upload to PyPI. either: 8. **`[AUTO:GHA]`** upload to PyPI. either:
a) `[python -m py]make pypi`, or a) `[python setup.py] make pypi`, or
b) `twine upload -s -i $(git config user.signingkey) dist/tqdm-*` b) `twine upload -s -i $(git config user.signingkey) dist/tqdm-*`
9. **`[AUTO:GHA]`** upload to docker hub: 9. **`[AUTO:GHA]`** upload to docker hub:
a) `make -B docker` a) `make -B docker`
@ -359,7 +359,7 @@ Much is automated so really it's steps 1-5, then 11(a).
13. **`[SUB][AUTO:GHA-rel]`** run `make deploy` in the `docs` submodule to update website 13. **`[SUB][AUTO:GHA-rel]`** run `make deploy` in the `docs` submodule to update website
14. **`[SUB][AUTO:GHA-rel]`** accept the automated PR in the `feedstock` submodule to update conda 14. **`[SUB][AUTO:GHA-rel]`** accept the automated PR in the `feedstock` submodule to update conda
15. **`[AUTO:GHA-rel]`** update the [gh-pages project] benchmarks 15. **`[AUTO:GHA-rel]`** update the [gh-pages project] benchmarks
a) `[python -m py]make testasvfull` a) `[python setup.py] make testasvfull`
b) `asv gh-pages` b) `asv gh-pages`
Key: Key:

View file

@ -5,12 +5,12 @@
"metadata": {}, "metadata": {},
"source": [ "source": [
"<h1 align=\"center\">tqdm</h1>\n", "<h1 align=\"center\">tqdm</h1>\n",
"<img src=\"https://tqdm.github.io/img/logo.gif\" align=\"left\" />\n", "<img src=\"https://img.tqdm.ml/logo.gif\" align=\"left\" />\n",
"\n", "\n",
"[![Py-Versions](https://img.shields.io/pypi/pyversions/tqdm.svg?logo=python&logoColor=white)](https://pypi.org/project/tqdm)|[![Versions](https://img.shields.io/pypi/v/tqdm.svg)](https://tqdm.github.io/releases)|[![Conda-Forge-Status](https://img.shields.io/conda/v/conda-forge/tqdm.svg?label=conda-forge&logo=conda-forge)](https://anaconda.org/conda-forge/tqdm)|[![Docker](https://img.shields.io/badge/docker-pull-blue.svg?logo=docker&logoColor=white)](https://hub.docker.com/r/tqdm/tqdm)|[![Snapcraft](https://img.shields.io/badge/snap-install-82BEA0.svg?logo=snapcraft)](https://snapcraft.io/tqdm)\n", "[![Py-Versions](https://img.shields.io/pypi/pyversions/tqdm.svg?logo=python&logoColor=white)](https://pypi.org/project/tqdm)|[![Versions](https://img.shields.io/pypi/v/tqdm.svg)](https://tqdm.github.io/releases)|[![Conda-Forge-Status](https://img.shields.io/conda/v/conda-forge/tqdm.svg?label=conda-forge&logo=conda-forge)](https://anaconda.org/conda-forge/tqdm)|[![Docker](https://img.shields.io/badge/docker-pull-blue.svg?logo=docker&logoColor=white)](https://hub.docker.com/r/tqdm/tqdm)|[![Snapcraft](https://img.shields.io/badge/snap-install-82BEA0.svg?logo=snapcraft)](https://snapcraft.io/tqdm)\n",
"-|-|-|-|-\n", "-|-|-|-|-\n",
"\n", "\n",
"[![Build-Status](https://img.shields.io/github/actions/workflow/status/tqdm/tqdm/test.yml?branch=master&label=tqdm&logo=GitHub)](https://github.com/tqdm/tqdm/actions/workflows/test.yml)|[![Coverage-Status](https://img.shields.io/coveralls/github/tqdm/tqdm/master?logo=coveralls)](https://coveralls.io/github/tqdm/tqdm)|[![Branch-Coverage-Status](https://codecov.io/gh/tqdm/tqdm/branch/master/graph/badge.svg)](https://codecov.io/gh/tqdm/tqdm)|[![Codacy-Grade](https://app.codacy.com/project/badge/Grade/3f965571598f44549c7818f29cdcf177)](https://www.codacy.com/gh/tqdm/tqdm/dashboard)|[![Libraries-Rank](https://img.shields.io/librariesio/sourcerank/pypi/tqdm.svg?logo=koding&logoColor=white)](https://libraries.io/pypi/tqdm)|[![PyPI-Downloads](https://img.shields.io/pypi/dm/tqdm.svg?label=pypi%20downloads&logo=PyPI&logoColor=white)](https://pepy.tech/project/tqdm)\n", "[![Build-Status](https://img.shields.io/github/workflow/status/tqdm/tqdm/Test/master?logo=GitHub)](https://github.com/tqdm/tqdm/actions?query=workflow%3ATest)|[![Coverage-Status](https://img.shields.io/coveralls/github/tqdm/tqdm/master?logo=coveralls)](https://coveralls.io/github/tqdm/tqdm)|[![Branch-Coverage-Status](https://codecov.io/gh/tqdm/tqdm/branch/master/graph/badge.svg)](https://codecov.io/gh/tqdm/tqdm)|[![Codacy-Grade](https://app.codacy.com/project/badge/Grade/3f965571598f44549c7818f29cdcf177)](https://www.codacy.com/gh/tqdm/tqdm/dashboard)|[![Libraries-Rank](https://img.shields.io/librariesio/sourcerank/pypi/tqdm.svg?logo=koding&logoColor=white)](https://libraries.io/pypi/tqdm)|[![PyPI-Downloads](https://img.shields.io/pypi/dm/tqdm.svg?label=pypi%20downloads&logo=PyPI&logoColor=white)](https://pepy.tech/project/tqdm)\n",
"-|-|-|-|-|-\n", "-|-|-|-|-|-\n",
"\n", "\n",
"[![DOI](https://img.shields.io/badge/DOI-10.5281/zenodo.595120-blue.svg)](https://doi.org/10.5281/zenodo.595120)|[![LICENCE](https://img.shields.io/pypi/l/tqdm.svg)](https://raw.githubusercontent.com/tqdm/tqdm/master/LICENCE)|[![OpenHub-Status](https://www.openhub.net/p/tqdm/widgets/project_thin_badge?format=gif)](https://www.openhub.net/p/tqdm?ref=Thin+badge)|[![binder-demo](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/tqdm/tqdm/master?filepath=DEMO.ipynb)|[![awesome-python](https://awesome.re/mentioned-badge.svg)](https://github.com/vinta/awesome-python)\n", "[![DOI](https://img.shields.io/badge/DOI-10.5281/zenodo.595120-blue.svg)](https://doi.org/10.5281/zenodo.595120)|[![LICENCE](https://img.shields.io/pypi/l/tqdm.svg)](https://raw.githubusercontent.com/tqdm/tqdm/master/LICENCE)|[![OpenHub-Status](https://www.openhub.net/p/tqdm/widgets/project_thin_badge?format=gif)](https://www.openhub.net/p/tqdm?ref=Thin+badge)|[![binder-demo](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/tqdm/tqdm/master?filepath=DEMO.ipynb)|[![awesome-python](https://awesome.re/mentioned-badge.svg)](https://github.com/vinta/awesome-python)\n",
@ -40,7 +40,7 @@
"metadata": {}, "metadata": {},
"source": [ "source": [
"`trange(N)` can be also used as a convenient shortcut for\n", "`trange(N)` can be also used as a convenient shortcut for\n",
"`tqdm(range(N))`." "`tqdm(xrange(N))`."
] ]
}, },
{ {
@ -58,7 +58,7 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
"![Screenshot](https://tqdm.github.io/img/tqdm.gif)|[![Video](https://tqdm.github.io/img/video.jpg)](https://tqdm.github.io/video) [![Slides](https://tqdm.github.io/img/slides.jpg)](https://tqdm.github.io/PyData2019/slides.html) [![Merch](https://tqdm.github.io/img/merch.jpg)](https://tqdm.github.io/merch)\n", "![Screenshot](https://img.tqdm.ml/tqdm.gif)|[![Video](https://img.tqdm.ml/video.jpg)](https://tqdm.github.io/video) [![Slides](https://img.tqdm.ml/slides.jpg)](https://tqdm.github.io/PyData2019/slides.html) [![Merch](https://img.tqdm.ml/merch.jpg)](https://tqdm.github.io/merch)\n",
"-|-\n", "-|-\n",
"\n", "\n",
"It can also be executed as a module with pipes:" "It can also be executed as a module with pipes:"
@ -413,7 +413,7 @@
" \"\"\"Provides a `total_time` format parameter\"\"\"\n", " \"\"\"Provides a `total_time` format parameter\"\"\"\n",
" @property\n", " @property\n",
" def format_dict(self):\n", " def format_dict(self):\n",
" d = super().format_dict\n", " d = super(TqdmExtraFormat, self).format_dict\n",
" total_time = d[\"elapsed\"] * (d[\"total\"] or 0) / max(d[\"n\"], 1)\n", " total_time = d[\"elapsed\"] * (d[\"total\"] or 0) / max(d[\"n\"], 1)\n",
" d.update(total_time=self.format_interval(total_time) + \" in total\")\n", " d.update(total_time=self.format_interval(total_time) + \" in total\")\n",
" return d\n", " return d\n",
@ -737,7 +737,7 @@
"bars and colour hints (blue: normal, green: completed, red:\n", "bars and colour hints (blue: normal, green: completed, red:\n",
"error/interrupt, light blue: no ETA); as demonstrated below.\n", "error/interrupt, light blue: no ETA); as demonstrated below.\n",
"\n", "\n",
"![Screenshot-Jupyter3](https://tqdm.github.io/img/jupyter-3.gif)\n", "![Screenshot-Jupyter3](https://img.tqdm.ml/jupyter-3.gif)\n",
"\n", "\n",
"The `notebook` version supports percentage or pixels for overall width\n", "The `notebook` version supports percentage or pixels for overall width\n",
"(e.g.: `ncols='100%'` or `ncols='480px'`).\n", "(e.g.: `ncols='100%'` or `ncols='480px'`).\n",
@ -843,7 +843,7 @@
"specify any file-like object using the `file` argument. For example,\n", "specify any file-like object using the `file` argument. For example,\n",
"this can be used to redirect the messages writing to a log file or class.\n", "this can be used to redirect the messages writing to a log file or class.\n",
"\n", "\n",
"[![README-Hits](https://cgi.cdcl.ml/hits?q=tqdm&style=social&r=https://github.com/tqdm/tqdm&l=https://tqdm.github.io/img/favicon.png&f=https://tqdm.github.io/img/logo.gif)](https://cgi.cdcl.ml/hits?q=tqdm&a=plot&r=https://github.com/tqdm/tqdm&l=https://tqdm.github.io/img/favicon.png&f=https://tqdm.github.io/img/logo.gif&style=social)|(Since 19 May 2016)\n", "[![README-Hits](https://caspersci.uk.to/cgi-bin/hits.cgi?q=tqdm&style=social&r=https://github.com/tqdm/tqdm&l=https://img.tqdm.ml/favicon.png&f=https://img.tqdm.ml/logo.gif)](https://caspersci.uk.to/cgi-bin/hits.cgi?q=tqdm&a=plot&r=https://github.com/tqdm/tqdm&l=https://img.tqdm.ml/favicon.png&f=https://img.tqdm.ml/logo.gif&style=social)|(Since 19 May 2016)\n",
"-|-" "-|-"
] ]
}, },

View file

@ -7,11 +7,11 @@ Exceptions or notable authors are listed below
in reverse chronological order: in reverse chronological order:
* files: * * files: *
MPL-2.0 2015-2024 (c) Casper da Costa-Luis MPLv2.0 2015-2021 (c) Casper da Costa-Luis
[casperdcl](https://github.com/casperdcl). [casperdcl](https://github.com/casperdcl).
* files: tqdm/_tqdm.py * files: tqdm/_tqdm.py
MIT 2016 (c) [PR #96] on behalf of Google Inc. MIT 2016 (c) [PR #96] on behalf of Google Inc.
* files: tqdm/_tqdm.py README.rst .gitignore * files: tqdm/_tqdm.py setup.py README.rst MANIFEST.in .gitignore
MIT 2013 (c) Noam Yorav-Raphael, original author. MIT 2013 (c) Noam Yorav-Raphael, original author.
[PR #96]: https://github.com/tqdm/tqdm/pull/96 [PR #96]: https://github.com/tqdm/tqdm/pull/96

View file

@ -1,4 +1,4 @@
# IMPORTANT: for compatibility with `python -m pymake [alias]`, ensure: # IMPORTANT: for compatibility with `python setup.py make [alias]`, ensure:
# 1. Every alias is preceded by @[+]make (eg: @make alias) # 1. Every alias is preceded by @[+]make (eg: @make alias)
# 2. A maximum of one @make alias or command per line # 2. A maximum of one @make alias or command per line
# see: https://github.com/tqdm/py-make/issues/1 # see: https://github.com/tqdm/py-make/issues/1
@ -31,7 +31,7 @@
run run
help: help:
@python -m pymake -p @python setup.py make -p
alltests: alltests:
@+make testcoverage @+make testcoverage
@ -58,14 +58,15 @@ testsetup:
@make README.rst @make README.rst
@make tqdm/tqdm.1 @make tqdm/tqdm.1
@make tqdm/completion.sh @make tqdm/completion.sh
@make help python setup.py check --metadata --restructuredtext --strict
python setup.py make none
testnb: testnb:
pytest tests_notebook.ipynb --cov=tqdm.notebook --cov-report=term -W=ignore --nbval --nbval-current-env --nbval-sanitize-with=.meta/nbval.ini pytest tests_notebook.ipynb --nbval --nbval-current-env -W=ignore --nbval-sanitize-with=setup.cfg --cov=tqdm.notebook --cov-report=term
testcoverage: testcoverage:
@make coverclean @make coverclean
pytest tests_notebook.ipynb --cov=tqdm --cov-report= -W=ignore --nbval --nbval-current-env --nbval-sanitize-with=.meta/nbval.ini pytest tests_notebook.ipynb --cov=tqdm --cov-report= --nbval --nbval-current-env --nbval-sanitize-with=setup.cfg -W=ignore
pytest -k "not perf" --cov=tqdm --cov-report=xml --cov-report=term --cov-append --cov-fail-under=80 pytest -k "not perf" --cov=tqdm --cov-report=xml --cov-report=term --cov-append --cov-fail-under=80
testperf: testperf:
@ -137,9 +138,9 @@ clean:
@+python -c "import os, glob; [os.remove(i) for i in glob.glob('*.py[co]')]" @+python -c "import os, glob; [os.remove(i) for i in glob.glob('*.py[co]')]"
@+python -c "import os, glob; [os.remove(i) for i in glob.glob('tests/*.py[co]')]" @+python -c "import os, glob; [os.remove(i) for i in glob.glob('tests/*.py[co]')]"
@+python -c "import os, glob; [os.remove(i) for i in glob.glob('benchmarks/*.py[co]')]" @+python -c "import os, glob; [os.remove(i) for i in glob.glob('benchmarks/*.py[co]')]"
@+python -c "import os, glob; [os.remove(i) for i in glob.glob('examples/*.py[co]')]"
@+python -c "import os, glob; [os.remove(i) for i in glob.glob('tqdm/*.py[co]')]" @+python -c "import os, glob; [os.remove(i) for i in glob.glob('tqdm/*.py[co]')]"
@+python -c "import os, glob; [os.remove(i) for i in glob.glob('tqdm/contrib/*.py[co]')]" @+python -c "import os, glob; [os.remove(i) for i in glob.glob('tqdm/contrib/*.py[co]')]"
@+python -c "import os, glob; [os.remove(i) for i in glob.glob('tqdm/examples/*.py[co]')]"
toxclean: toxclean:
@+python -c "import shutil; shutil.rmtree('.tox', True)" @+python -c "import shutil; shutil.rmtree('.tox', True)"
@ -151,11 +152,12 @@ submodules:
cd feedstock && git remote add autotick-bot git@github.com:regro-cf-autotick-bot/tqdm-feedstock cd feedstock && git remote add autotick-bot git@github.com:regro-cf-autotick-bot/tqdm-feedstock
install: install:
python -m pip install . python setup.py install
install_dev: install_dev:
python -m pip install -e . python setup.py develop --uninstall
python setup.py develop
install_build: install_build:
python -m pip install -r .meta/requirements-build.txt python -m pip install -r .meta/requirements-dev.txt
install_test: install_test:
python -m pip install -r .meta/requirements-test.txt python -m pip install -r .meta/requirements-test.txt
pre-commit install pre-commit install
@ -163,11 +165,11 @@ install_test:
build: build:
@make prebuildclean @make prebuildclean
@make testsetup @make testsetup
python -m build python setup.py sdist bdist_wheel
python -m twine check dist/* # python setup.py bdist_wininst
pypi: pypi:
python -m twine upload dist/* twine upload dist/*
buildupload: buildupload:
@make build @make build

1581
PKG-INFO Normal file

File diff suppressed because it is too large Load diff

View file

@ -255,7 +255,7 @@ This can be beautified further:
.. code:: sh .. code:: sh
$ BYTES=$(du -sb docs/ | cut -f1) $ BYTES="$(du -sb docs/ | cut -f1)"
$ tar -cf - docs/ \ $ tar -cf - docs/ \
| tqdm --bytes --total "$BYTES" --desc Processing | gzip \ | tqdm --bytes --total "$BYTES" --desc Processing | gzip \
| tqdm --bytes --total "$BYTES" --desc Compressed --position 1 \ | tqdm --bytes --total "$BYTES" --desc Compressed --position 1 \
@ -291,12 +291,6 @@ The most common issues relate to excessive output on multiple lines, instead
of a neat one-line progress bar. of a neat one-line progress bar.
- Consoles in general: require support for carriage return (``CR``, ``\r``). - Consoles in general: require support for carriage return (``CR``, ``\r``).
* Some cloud logging consoles which don't support ``\r`` properly
(`cloudwatch <https://github.com/tqdm/tqdm/issues/966>`__,
`K8s <https://github.com/tqdm/tqdm/issues/1319>`__) may benefit from
``export TQDM_POSITION=-1``.
- Nested progress bars: - Nested progress bars:
* Consoles in general: require support for moving cursors up to the * Consoles in general: require support for moving cursors up to the
@ -333,14 +327,12 @@ of a neat one-line progress bar.
* The same applies to ``itertools``. * The same applies to ``itertools``.
* Some useful convenience functions can be found under ``tqdm.contrib``. * Some useful convenience functions can be found under ``tqdm.contrib``.
- `Hanging pipes in python2 <https://github.com/tqdm/tqdm/issues/359>`__:
when using ``tqdm`` on the CLI, you may need to use Python 3.5+ for correct
buffering.
- `No intermediate output in docker-compose <https://github.com/tqdm/tqdm/issues/771>`__: - `No intermediate output in docker-compose <https://github.com/tqdm/tqdm/issues/771>`__:
use ``docker-compose run`` instead of ``docker-compose up`` and ``tty: true``. use ``docker-compose run`` instead of ``docker-compose up`` and ``tty: true``.
- Overriding defaults via environment variables:
e.g. in CI/cloud jobs, ``export TQDM_MININTERVAL=5`` to avoid log spam.
This override logic is handled by the ``tqdm.utils.envwrap`` decorator
(useful independent of ``tqdm``).
If you come across any other difficulties, browse and file |GitHub-Issues|. If you come across any other difficulties, browse and file |GitHub-Issues|.
Documentation Documentation
@ -357,14 +349,12 @@ Documentation
progressbar every time a value is requested. progressbar every time a value is requested.
""" """
@envwrap("TQDM_") # override defaults via env vars
def __init__(self, iterable=None, desc=None, total=None, leave=True, def __init__(self, iterable=None, desc=None, total=None, leave=True,
file=None, ncols=None, mininterval=0.1, file=None, ncols=None, mininterval=0.1,
maxinterval=10.0, miniters=None, ascii=None, disable=False, maxinterval=10.0, miniters=None, ascii=None, disable=False,
unit='it', unit_scale=False, dynamic_ncols=False, unit='it', unit_scale=False, dynamic_ncols=False,
smoothing=0.3, bar_format=None, initial=0, position=None, smoothing=0.3, bar_format=None, initial=0, position=None,
postfix=None, unit_divisor=1000, write_bytes=False, postfix=None, unit_divisor=1000):
lock_args=None, nrows=None, colour=None, delay=0):
Parameters Parameters
~~~~~~~~~~ ~~~~~~~~~~
@ -460,7 +450,9 @@ Parameters
* unit_divisor : float, optional * unit_divisor : float, optional
[default: 1000], ignored unless ``unit_scale`` is True. [default: 1000], ignored unless ``unit_scale`` is True.
* write_bytes : bool, optional * write_bytes : bool, optional
Whether to write bytes. If (default: False) will write unicode. If (default: None) and ``file`` is unspecified,
bytes will be written in Python 2. If ``True`` will also write
bytes. In all other cases will default to unicode.
* lock_args : tuple, optional * lock_args : tuple, optional
Passed to ``refresh`` for intermediate output Passed to ``refresh`` for intermediate output
(initialisation, iterating, and updating). (initialisation, iterating, and updating).
@ -639,7 +631,10 @@ Returns
"""Registers the current `tqdm` class with `pandas`.""" """Registers the current `tqdm` class with `pandas`."""
def trange(*args, **tqdm_kwargs): def trange(*args, **tqdm_kwargs):
"""Shortcut for `tqdm(range(*args), **tqdm_kwargs)`.""" """
A shortcut for `tqdm(xrange(*args), **tqdm_kwargs)`.
On Python3+, `range` is used instead of `xrange`.
"""
Convenience Functions Convenience Functions
~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~
@ -742,7 +737,7 @@ with the ``desc`` and ``postfix`` arguments:
sleep(0.1) sleep(0.1)
with tqdm(total=10, bar_format="{postfix[0]} {postfix[1][value]:>8.2g}", with tqdm(total=10, bar_format="{postfix[0]} {postfix[1][value]:>8.2g}",
postfix=["Batch", {"value": 0}]) as t: postfix=["Batch", dict(value=0)]) as t:
for i in range(10): for i in range(10):
sleep(0.1) sleep(0.1)
t.postfix[1]["value"] = i / 2 t.postfix[1]["value"] = i / 2
@ -766,7 +761,7 @@ Additional ``bar_format`` parameters may also be defined by overriding
"""Provides a `total_time` format parameter""" """Provides a `total_time` format parameter"""
@property @property
def format_dict(self): def format_dict(self):
d = super().format_dict d = super(TqdmExtraFormat, self).format_dict
total_time = d["elapsed"] * (d["total"] or 0) / max(d["n"], 1) total_time = d["elapsed"] * (d["total"] or 0) / max(d["n"], 1)
d.update(total_time=self.format_interval(total_time) + " in total") d.update(total_time=self.format_interval(total_time) + " in total")
return d return d
@ -830,7 +825,7 @@ first.
def progresser(n): def progresser(n):
interval = 0.001 / (n + 2) interval = 0.001 / (n + 2)
total = 5000 total = 5000
text = f"#{n}, est. {interval * total:<04.2}s" text = "#{}, est. {:<04.2}s".format(n, interval * total)
for _ in trange(total, desc=text, position=n): for _ in trange(total, desc=text, position=n):
sleep(interval) sleep(interval)
@ -853,7 +848,7 @@ Note that in Python 3, ``tqdm.write`` is thread-safe:
def progresser(n): def progresser(n):
interval = 0.001 / (n + 2) interval = 0.001 / (n + 2)
total = 5000 total = 5000
text = f"#{n}, est. {interval * total:<04.2}s" text = "#{}, est. {:<04.2}s".format(n, interval * total)
for _ in trange(total, desc=text): for _ in trange(total, desc=text):
sleep(interval) sleep(interval)
if n == 6: if n == 6:
@ -982,7 +977,7 @@ custom callback take advantage of this, simply use the return value of
class TqdmExt(std_tqdm): class TqdmExt(std_tqdm):
def update(self, n=1): def update(self, n=1):
displayed = super().update(n) displayed = super(TqdmExt, self).update(n)
if displayed: if displayed:
external_callback(**self.format_dict) external_callback(**self.format_dict)
return displayed return displayed
@ -1410,17 +1405,16 @@ are:
==================== ======================================================== ==== ================================ ==================== ======================================================== ==== ================================
Name ID SLoC Notes Name ID SLoC Notes
==================== ======================================================== ==== ================================ ==================== ======================================================== ==== ================================
Casper da Costa-Luis `casperdcl <https://github.com/casperdcl>`__ ~80% primary maintainer |Gift-Casper| Casper da Costa-Luis `casperdcl <https://github.com/casperdcl>`__ ~78% primary maintainer |Gift-Casper|
Stephen Larroque `lrq3000 <https://github.com/lrq3000>`__ ~9% team member Stephen Larroque `lrq3000 <https://github.com/lrq3000>`__ ~10% team member
Martin Zugnoni `martinzugnoni <https://github.com/martinzugnoni>`__ ~3% Martin Zugnoni `martinzugnoni <https://github.com/martinzugnoni>`__ ~4%
Daniel Ecer `de-code <https://github.com/de-code>`__ ~2% Daniel Ecer `de-code <https://github.com/de-code>`__ ~2%
Richard Sheridan `richardsheridan <https://github.com/richardsheridan>`__ ~1% Richard Sheridan `richardsheridan <https://github.com/richardsheridan>`__ ~1%
Guangshuo Chen `chengs <https://github.com/chengs>`__ ~1% Guangshuo Chen `chengs <https://github.com/chengs>`__ ~1%
Helio Machado `0x2b3bfa0 <https://github.com/0x2b3bfa0>`__ ~1%
Kyle Altendorf `altendky <https://github.com/altendky>`__ <1% Kyle Altendorf `altendky <https://github.com/altendky>`__ <1%
Noam Yorav-Raphael `noamraph <https://github.com/noamraph>`__ <1% original author
Matthew Stevens `mjstevens777 <https://github.com/mjstevens777>`__ <1% Matthew Stevens `mjstevens777 <https://github.com/mjstevens777>`__ <1%
Hadrien Mary `hadim <https://github.com/hadim>`__ <1% team member Hadrien Mary `hadim <https://github.com/hadim>`__ <1% team member
Noam Yorav-Raphael `noamraph <https://github.com/noamraph>`__ <1% original author
Mikhail Korobov `kmike <https://github.com/kmike>`__ <1% team member Mikhail Korobov `kmike <https://github.com/kmike>`__ <1% team member
==================== ======================================================== ==== ================================ ==================== ======================================================== ==== ================================
@ -1440,16 +1434,16 @@ Citation information: |DOI|
|README-Hits| (Since 19 May 2016) |README-Hits| (Since 19 May 2016)
.. |Logo| image:: https://tqdm.github.io/img/logo.gif .. |Logo| image:: https://img.tqdm.ml/logo.gif
.. |Screenshot| image:: https://tqdm.github.io/img/tqdm.gif .. |Screenshot| image:: https://img.tqdm.ml/tqdm.gif
.. |Video| image:: https://tqdm.github.io/img/video.jpg .. |Video| image:: https://img.tqdm.ml/video.jpg
:target: https://tqdm.github.io/video :target: https://tqdm.github.io/video
.. |Slides| image:: https://tqdm.github.io/img/slides.jpg .. |Slides| image:: https://img.tqdm.ml/slides.jpg
:target: https://tqdm.github.io/PyData2019/slides.html :target: https://tqdm.github.io/PyData2019/slides.html
.. |Merch| image:: https://tqdm.github.io/img/merch.jpg .. |Merch| image:: https://img.tqdm.ml/merch.jpg
:target: https://tqdm.github.io/merch :target: https://tqdm.github.io/merch
.. |Build-Status| image:: https://img.shields.io/github/actions/workflow/status/tqdm/tqdm/test.yml?branch=master&label=tqdm&logo=GitHub .. |Build-Status| image:: https://img.shields.io/github/workflow/status/tqdm/tqdm/Test/master?logo=GitHub
:target: https://github.com/tqdm/tqdm/actions/workflows/test.yml :target: https://github.com/tqdm/tqdm/actions?query=workflow%3ATest
.. |Coverage-Status| image:: https://img.shields.io/coveralls/github/tqdm/tqdm/master?logo=coveralls .. |Coverage-Status| image:: https://img.shields.io/coveralls/github/tqdm/tqdm/master?logo=coveralls
:target: https://coveralls.io/github/tqdm/tqdm :target: https://coveralls.io/github/tqdm/tqdm
.. |Branch-Coverage-Status| image:: https://codecov.io/gh/tqdm/tqdm/branch/master/graph/badge.svg .. |Branch-Coverage-Status| image:: https://codecov.io/gh/tqdm/tqdm/branch/master/graph/badge.svg
@ -1502,8 +1496,8 @@ Citation information: |DOI|
:target: https://doi.org/10.5281/zenodo.595120 :target: https://doi.org/10.5281/zenodo.595120
.. |binder-demo| image:: https://mybinder.org/badge_logo.svg .. |binder-demo| image:: https://mybinder.org/badge_logo.svg
:target: https://mybinder.org/v2/gh/tqdm/tqdm/master?filepath=DEMO.ipynb :target: https://mybinder.org/v2/gh/tqdm/tqdm/master?filepath=DEMO.ipynb
.. |Screenshot-Jupyter1| image:: https://tqdm.github.io/img/jupyter-1.gif .. |Screenshot-Jupyter1| image:: https://img.tqdm.ml/jupyter-1.gif
.. |Screenshot-Jupyter2| image:: https://tqdm.github.io/img/jupyter-2.gif .. |Screenshot-Jupyter2| image:: https://img.tqdm.ml/jupyter-2.gif
.. |Screenshot-Jupyter3| image:: https://tqdm.github.io/img/jupyter-3.gif .. |Screenshot-Jupyter3| image:: https://img.tqdm.ml/jupyter-3.gif
.. |README-Hits| image:: https://cgi.cdcl.ml/hits?q=tqdm&style=social&r=https://github.com/tqdm/tqdm&l=https://tqdm.github.io/img/favicon.png&f=https://tqdm.github.io/img/logo.gif .. |README-Hits| image:: https://caspersci.uk.to/cgi-bin/hits.cgi?q=tqdm&style=social&r=https://github.com/tqdm/tqdm&l=https://img.tqdm.ml/favicon.png&f=https://img.tqdm.ml/logo.gif
:target: https://cgi.cdcl.ml/hits?q=tqdm&a=plot&r=https://github.com/tqdm/tqdm&l=https://tqdm.github.io/img/favicon.png&f=https://tqdm.github.io/img/logo.gif&style=social :target: https://caspersci.uk.to/cgi-bin/hits.cgi?q=tqdm&a=plot&r=https://github.com/tqdm/tqdm&l=https://img.tqdm.ml/favicon.png&f=https://img.tqdm.ml/logo.gif&style=social

View file

@ -5,7 +5,7 @@ channels:
- defaults - defaults
dependencies: dependencies:
# base # base
- python >=3.7 - python=3
- pip - pip
- ipykernel - ipykernel
- ipywidgets - ipywidgets
@ -20,28 +20,27 @@ dependencies:
- pytest - pytest
- pytest-cov - pytest-cov
- pytest-timeout - pytest-timeout
- pytest-asyncio>=0.24 - pytest-asyncio # [py>=3.7]
- nbval
- coverage - coverage
# extras # extras
- dask # dask - dask # dask
- matplotlib # gui - matplotlib # gui
- numpy # pandas, keras, contrib.tenumerate - numpy # pandas, keras, contrib.tenumerate
- pandas - pandas
- pyarrow # pandas
- tensorflow # keras - tensorflow # keras
- slack-sdk # contrib.slack - slack-sdk # contrib.slack
- requests # contrib.telegram - requests # contrib.telegram
- rich # rich - rich # rich
- argopt # `cd wiki && pymake` - argopt # `cd wiki && pymake`
- twine # `pymake check pypi` - twine # `pymake pypi`
- python-build # `python -m build` - wheel # `setup.py bdist_wheel`
# `cd docs && pymake` # `cd docs && pymake`
- mkdocs-material - mkdocs-material
- pydoc-markdown
- pygments - pygments
- pymdown-extensions - pymdown-extensions
- pip: - pip:
- git+https://github.com/casperdcl/nbval.git@master#egg=nbval # tests (native) - py-make >=0.1.0 # `setup.py make/pymake`
- py-make >=0.1.0 # `make/pymake`
- mkdocs-minify-plugin # `cd docs && pymake` - mkdocs-minify-plugin # `cd docs && pymake`
- git+https://github.com/tqdm/jsmin@fix-pip#egg=jsmin # `cd docs && pymake` - git+https://github.com/tqdm/jsmin@python3-only#egg=jsmin # `cd docs && pymake`
- pydoc-markdown >=4.6 # `cd docs && pymake`

View file

@ -18,6 +18,8 @@ Options:
NOTSET NOTSET
-d, --debug-trace Print lots of debugging information (-D NOTSET) -d, --debug-trace Print lots of debugging information (-D NOTSET)
""" """
from __future__ import print_function
import io import io
import logging import logging
import os import os

View file

@ -1,4 +1,6 @@
"""Asynchronous examples using `asyncio`, `async` and `await`.""" """
Asynchronous examples using `asyncio`, `async` and `await` on `python>=3.7`.
"""
import asyncio import asyncio
from tqdm.asyncio import tqdm, trange from tqdm.asyncio import tqdm, trange

View file

@ -126,7 +126,7 @@
@misc{hits, @misc{hits,
year="2019", year="2019",
title="{tqdm} hits", title="{tqdm} hits",
url="https://cgi.cdcl.ml/hits?q=tqdm&a=plot", url="https://caspersci.uk.to/cgi-bin/hits.cgi?q=tqdm&a=plot",
author="Casper O. {da Costa-Luis}" author="Casper O. {da Costa-Luis}"
} }
@book{miller, @book{miller,

View file

@ -1,3 +1,6 @@
from __future__ import print_function
import sys
from concurrent.futures import ThreadPoolExecutor from concurrent.futures import ThreadPoolExecutor
from functools import partial from functools import partial
from multiprocessing import Pool, RLock, freeze_support from multiprocessing import Pool, RLock, freeze_support
@ -9,19 +12,21 @@ from tqdm.auto import tqdm, trange
from tqdm.contrib.concurrent import process_map, thread_map from tqdm.contrib.concurrent import process_map, thread_map
NUM_SUBITERS = 9 NUM_SUBITERS = 9
PY2 = sys.version_info[:1] <= (2,)
def progresser(n, auto_position=True, write_safe=False, blocking=True, progress=False): def progresser(n, auto_position=True, write_safe=False, blocking=True, progress=False):
interval = random() * 0.002 / (NUM_SUBITERS - n + 2) # nosec interval = random() * 0.002 / (NUM_SUBITERS - n + 2) # nosec
total = 5000 total = 5000
text = f"#{n}, est. {interval * total:<04.2g}s" text = "#{0}, est. {1:<04.2}s".format(n, interval * total)
for _ in trange(total, desc=text, disable=not progress, for _ in trange(total, desc=text, disable=not progress,
lock_args=None if blocking else (False,), lock_args=None if blocking else (False,),
position=None if auto_position else n): position=None if auto_position else n):
sleep(interval) sleep(interval)
# NB: may not clear instances with higher `position` upon completion # NB: may not clear instances with higher `position` upon completion
# since this worker may not know about other bars #796 # since this worker may not know about other bars #796
if write_safe: # we think we know about other bars if write_safe:
# we think we know about other bars (currently only py3 threading)
if n == 6: if n == 6:
tqdm.write("n == 6 completed") tqdm.write("n == 6 completed")
return n + 1 return n + 1
@ -32,7 +37,7 @@ if __name__ == '__main__':
L = list(range(NUM_SUBITERS))[::-1] L = list(range(NUM_SUBITERS))[::-1]
print("Simple thread mapping") print("Simple thread mapping")
thread_map(partial(progresser, write_safe=True), L, max_workers=4) thread_map(partial(progresser, write_safe=not PY2), L, max_workers=4)
print("Simple process mapping") print("Simple process mapping")
process_map(partial(progresser), L, max_workers=4) process_map(partial(progresser), L, max_workers=4)
@ -49,5 +54,8 @@ if __name__ == '__main__':
print("Multi-threading") print("Multi-threading")
tqdm.set_lock(TRLock()) tqdm.set_lock(TRLock())
with ThreadPoolExecutor(initializer=tqdm.set_lock, initargs=(tqdm.get_lock(),)) as p: pool_args = {}
p.map(partial(progresser, progress=True, write_safe=True, blocking=False), L) if not PY2:
pool_args.update(initializer=tqdm.set_lock, initargs=(tqdm.get_lock(),))
with ThreadPoolExecutor(**pool_args) as p:
p.map(partial(progresser, progress=True, write_safe=not PY2, blocking=False), L)

View file

@ -10,6 +10,8 @@ any input string to `tqdm.write()`, and supply the arguments
A reusable canonical example is given below: A reusable canonical example is given below:
""" """
from __future__ import print_function
import contextlib import contextlib
import sys import sys
from time import sleep from time import sleep

View file

@ -2,7 +2,7 @@
# Simple tqdm examples and profiling # Simple tqdm examples and profiling
# Benchmark # Benchmark
for i in range(int(1e8)): for i in _range(int(1e8)):
pass pass
# Basic demo # Basic demo
@ -33,7 +33,7 @@ try:
except ImportError: except ImportError:
pass pass
else: else:
for i in ProgressBar()(range(int(1e8))): for i in ProgressBar()(_range(int(1e8))):
pass pass
# Dynamic miniters benchmark # Dynamic miniters benchmark
@ -61,4 +61,5 @@ for _ in trange(16, leave=True):
stmts = filter(None, re.split(r'\n\s*#.*?\n', __doc__)) stmts = filter(None, re.split(r'\n\s*#.*?\n', __doc__))
for s in stmts: for s in stmts:
print(s.replace('import tqdm\n', '')) print(s.replace('import tqdm\n', ''))
print(timeit(stmt=s, number=1), 'seconds') print(timeit(stmt='try:\n\t_range = xrange'
'\nexcept:\n\t_range = range\n' + s, number=1), 'seconds')

View file

@ -20,8 +20,11 @@ Options:
The local file path in which to save the url [default: /dev/null]. The local file path in which to save the url [default: /dev/null].
""" """
from os import devnull try:
from urllib import request as urllib from urllib import request as urllib
except ImportError: # py2
import urllib
from os import devnull
from docopt import docopt from docopt import docopt

View file

@ -5,135 +5,3 @@ build-backend = "setuptools.build_meta"
[tool.setuptools_scm] [tool.setuptools_scm]
write_to = "tqdm/_dist_ver.py" write_to = "tqdm/_dist_ver.py"
write_to_template = "__version__ = '{version}'\n" write_to_template = "__version__ = '{version}'\n"
[tool.setuptools.packages.find]
exclude = ["benchmarks", "examples", "tests", "wiki", "docs", "feedstock"]
[project.urls]
homepage = "https://tqdm.github.io"
repository = "https://github.com/tqdm/tqdm"
changelog = "https://tqdm.github.io/releases"
wiki = "https://github.com/tqdm/tqdm/wiki"
[project]
name = "tqdm"
dynamic = ["version"]
maintainers = [{name = "tqdm developers", email = "devs@tqdm.ml"}]
description = "Fast, Extensible Progress Meter"
readme = "README.rst"
requires-python = ">=3.7"
keywords = ["progressbar", "progressmeter", "progress", "bar", "meter", "rate", "eta", "console", "terminal", "time"]
license = {text = "MPL-2.0 AND MIT"}
# Trove classifiers (https://pypi.org/pypi?%3Aaction=list_classifiers)
classifiers = [
"Development Status :: 5 - Production/Stable",
"Environment :: Console",
"Environment :: MacOS X",
"Environment :: Other Environment",
"Environment :: Win32 (MS Windows)",
"Environment :: X11 Applications",
"Framework :: IPython",
"Framework :: Jupyter",
"Intended Audience :: Developers",
"Intended Audience :: Education",
"Intended Audience :: End Users/Desktop",
"Intended Audience :: Other Audience",
"Intended Audience :: System Administrators",
"License :: OSI Approved :: MIT License",
"License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)",
"Operating System :: MacOS",
"Operating System :: MacOS :: MacOS X",
"Operating System :: Microsoft",
"Operating System :: Microsoft :: MS-DOS",
"Operating System :: Microsoft :: Windows",
"Operating System :: POSIX",
"Operating System :: POSIX :: BSD",
"Operating System :: POSIX :: BSD :: FreeBSD",
"Operating System :: POSIX :: Linux",
"Operating System :: POSIX :: SunOS/Solaris",
"Operating System :: Unix",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: Implementation",
"Programming Language :: Python :: Implementation :: IronPython",
"Programming Language :: Python :: Implementation :: PyPy",
"Programming Language :: Unix Shell",
"Topic :: Desktop Environment",
"Topic :: Education :: Computer Aided Instruction (CAI)",
"Topic :: Education :: Testing",
"Topic :: Office/Business",
"Topic :: Other/Nonlisted Topic",
"Topic :: Software Development :: Build Tools",
"Topic :: Software Development :: Libraries",
"Topic :: Software Development :: Libraries :: Python Modules",
"Topic :: Software Development :: Pre-processors",
"Topic :: Software Development :: User Interfaces",
"Topic :: System :: Installation/Setup",
"Topic :: System :: Logging",
"Topic :: System :: Monitoring",
"Topic :: System :: Shells",
"Topic :: Terminals",
"Topic :: Utilities"]
dependencies = ['colorama; platform_system == "Windows"']
[project.optional-dependencies]
dev = ["pytest>=6", "pytest-cov", "pytest-timeout", "pytest-asyncio>=0.24", "nbval"]
discord = ["requests"]
slack = ["slack-sdk"]
telegram = ["requests"]
notebook = ["ipywidgets>=6"]
[project.scripts]
tqdm = "tqdm.cli:main"
[tool.flake8]
max_line_length = 99
exclude = [".git", "__pycache__", "build", "dist", ".eggs", ".asv", ".tox", ".ipynb_checkpoints"]
[tool.yapf]
spaces_before_comment = [15, 20]
arithmetic_precedence_indication = true
allow_split_before_dict_value = false
coalesce_brackets = true
column_limit = 99
each_dict_entry_on_separate_line = false
space_between_ending_comma_and_closing_bracket = false
split_before_named_assigns = false
split_before_closing_bracket = false
blank_line_before_nested_class_or_def = false
[tool.isort]
line_length = 99
multi_line_output = 4
known_first_party = ["tqdm", "tests"]
[tool.pytest.ini_options]
minversion = "6.0"
timeout = 30
log_level = "INFO"
markers = ["asyncio", "slow"]
python_files = ["tests_*.py", "tests_*.ipynb"]
testpaths = ["tests"]
addopts = "-v --tb=short -rxs -W=error --durations=0 --durations-min=0.1 --asyncio-mode=strict"
asyncio_default_fixture_loop_scope = "function"
[tool.coverage.run]
branch = true
include = ["tqdm/*"]
omit = [
"tqdm/contrib/bells.py",
"tqdm/contrib/slack.py",
"tqdm/contrib/discord.py",
"tqdm/contrib/telegram.py",
"tqdm/contrib/utils_worker.py"]
relative_files = true
disable_warnings = ["include-ignored"]
[tool.coverage.report]
show_missing = true

159
setup.cfg Normal file
View file

@ -0,0 +1,159 @@
[metadata]
name = tqdm
url = https://tqdm.github.io
project_urls =
Changelog=https://tqdm.github.io/releases
Source=https://github.com/tqdm/tqdm
Wiki=https://github.com/tqdm/tqdm/wiki
maintainer = tqdm developers
maintainer_email = python.tqdm@gmail.com
license = MPLv2.0, MIT Licences
license_file = LICENCE
description = Fast, Extensible Progress Meter
long_description = file: README.rst
long_description_content_type = text/x-rst
keywords = progressbar, progressmeter, progress, bar, meter, rate, eta, console, terminal, time
platforms = any
provides = tqdm
classifiers =
Development Status :: 5 - Production/Stable
Environment :: Console
Environment :: MacOS X
Environment :: Other Environment
Environment :: Win32 (MS Windows)
Environment :: X11 Applications
Framework :: IPython
Framework :: Jupyter
Intended Audience :: Developers
Intended Audience :: Education
Intended Audience :: End Users/Desktop
Intended Audience :: Other Audience
Intended Audience :: System Administrators
License :: OSI Approved :: MIT License
License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)
Operating System :: MacOS
Operating System :: MacOS :: MacOS X
Operating System :: Microsoft
Operating System :: Microsoft :: MS-DOS
Operating System :: Microsoft :: Windows
Operating System :: POSIX
Operating System :: POSIX :: BSD
Operating System :: POSIX :: BSD :: FreeBSD
Operating System :: POSIX :: Linux
Operating System :: POSIX :: SunOS/Solaris
Operating System :: Unix
Programming Language :: Python
Programming Language :: Python :: 2
Programming Language :: Python :: 2.7
Programming Language :: Python :: 3
Programming Language :: Python :: 3.5
Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.10
Programming Language :: Python :: Implementation
Programming Language :: Python :: Implementation :: IronPython
Programming Language :: Python :: Implementation :: PyPy
Programming Language :: Unix Shell
Topic :: Desktop Environment
Topic :: Education :: Computer Aided Instruction (CAI)
Topic :: Education :: Testing
Topic :: Office/Business
Topic :: Other/Nonlisted Topic
Topic :: Software Development :: Build Tools
Topic :: Software Development :: Libraries
Topic :: Software Development :: Libraries :: Python Modules
Topic :: Software Development :: Pre-processors
Topic :: Software Development :: User Interfaces
Topic :: System :: Installation/Setup
Topic :: System :: Logging
Topic :: System :: Monitoring
Topic :: System :: Shells
Topic :: Terminals
Topic :: Utilities
[options]
setup_requires = setuptools>=42; setuptools_scm[toml]>=3.4
python_requires = >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*
install_requires =
colorama; platform_system == 'Windows'
importlib_resources; python_version < "3.7"
tests_require = tox
include_package_data = True
packages = find:
[options.extras_require]
dev = py-make>=0.1.0; twine; wheel
slack = slack-sdk
telegram = requests
notebook = ipywidgets>=6
[options.entry_points]
console_scripts =
tqdm=tqdm.cli:main
[options.packages.find]
exclude = benchmarks, tests
[bdist_wheel]
universal = 1
[flake8]
max_line_length = 99
exclude = .asv,.eggs,.tox,.ipynb_checkpoints,build,dist,.git,__pycache__
[pydocstyle]
add_ignore = D400,D415
[yapf]
coalesce_brackets = True
column_limit = 99
each_dict_entry_on_separate_line = False
i18n_comment = NOQA
space_between_ending_comma_and_closing_bracket = False
split_before_named_assigns = False
split_before_closing_bracket = False
[isort]
line_length = 99
multi_line_output = 4
known_first_party = tqdm,tests
[tool:pytest]
timeout = 30
log_level = INFO
markers =
asyncio
slow
python_files = tests_*.py tests_*.ipynb
testpaths = tests
addopts = -v --tb=short -rxs -W=error --durations=0 --durations-min=0.1 --asyncio-mode=strict
[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/*
omit =
tqdm/contrib/bells.py
tqdm/contrib/slack.py
tqdm/contrib/discord.py
tqdm/contrib/telegram.py
tqdm/contrib/utils_worker.py
relative_files = True
disable_warnings = include-ignored
[coverage:report]
show_missing = True
[egg_info]
tag_build =
tag_date = 0

16
setup.py Executable file
View file

@ -0,0 +1,16 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
from os import path
from setuptools import setup
src_dir = path.abspath(path.dirname(__file__))
if sys.argv[1].lower().strip() == 'make': # exec Makefile commands
import pymake
fpath = path.join(src_dir, 'Makefile')
pymake.main(['-f', fpath] + sys.argv[2:])
# Stop to avoid setup.py raising non-standard command error
sys.exit(0)
setup(use_scm_version=True)

View file

@ -18,10 +18,24 @@ def pretest_posttest():
n = len(tqdm._instances) n = len(tqdm._instances)
if n: if n:
tqdm._instances.clear() tqdm._instances.clear()
raise EnvironmentError(f"{n} `tqdm` instances still in existence PRE-test") raise EnvironmentError(
"{0} `tqdm` instances still in existence PRE-test".format(n))
yield yield
if getattr(tqdm, "_instances", False): if getattr(tqdm, "_instances", False):
n = len(tqdm._instances) n = len(tqdm._instances)
if n: if n:
tqdm._instances.clear() tqdm._instances.clear()
raise EnvironmentError(f"{n} `tqdm` instances still in existence POST-test") raise EnvironmentError(
"{0} `tqdm` instances still in existence POST-test".format(n))
if sys.version_info[0] > 2:
@fixture
def capsysbin(capsysbinary):
"""alias for capsysbinary (py3)"""
return capsysbinary
else:
@fixture
def capsysbin(capsys):
"""alias for capsys (py2)"""
return capsys

128
tests/py37_asyncio.py Normal file
View file

@ -0,0 +1,128 @@
import asyncio
from functools import partial
from sys import platform
from time import time
from tqdm.asyncio import tarange, tqdm_asyncio
from .tests_tqdm import StringIO, closing, mark
tqdm = partial(tqdm_asyncio, miniters=0, mininterval=0)
trange = partial(tarange, miniters=0, mininterval=0)
as_completed = partial(tqdm_asyncio.as_completed, miniters=0, mininterval=0)
gather = partial(tqdm_asyncio.gather, miniters=0, mininterval=0)
def count(start=0, step=1):
i = start
while True:
new_start = yield i
if new_start is None:
i += step
else:
i = new_start
async def acount(*args, **kwargs):
for i in count(*args, **kwargs):
yield i
@mark.asyncio
async def test_break():
"""Test asyncio break"""
pbar = tqdm(count())
async for _ in pbar:
break
pbar.close()
@mark.asyncio
async def test_generators(capsys):
"""Test asyncio generators"""
with tqdm(count(), desc="counter") as pbar:
async for i in pbar:
if i >= 8:
break
_, err = capsys.readouterr()
assert '9it' in err
with tqdm(acount(), desc="async_counter") as pbar:
async for i in pbar:
if i >= 8:
break
_, err = capsys.readouterr()
assert '9it' in err
@mark.asyncio
async def test_range():
"""Test asyncio range"""
with closing(StringIO()) as our_file:
async for _ in tqdm(range(9), desc="range", file=our_file):
pass
assert '9/9' in our_file.getvalue()
our_file.seek(0)
our_file.truncate()
async for _ in trange(9, desc="trange", file=our_file):
pass
assert '9/9' in our_file.getvalue()
@mark.asyncio
async def test_nested():
"""Test asyncio nested"""
with closing(StringIO()) as our_file:
async for _ in tqdm(trange(9, desc="inner", file=our_file),
desc="outer", file=our_file):
pass
assert 'inner: 100%' in our_file.getvalue()
assert 'outer: 100%' in our_file.getvalue()
@mark.asyncio
async def test_coroutines():
"""Test asyncio coroutine.send"""
with closing(StringIO()) as our_file:
with tqdm(count(), file=our_file) as pbar:
async for i in pbar:
if i == 9:
pbar.send(-10)
elif i < 0:
assert i == -9
break
assert '10it' in our_file.getvalue()
@mark.slow
@mark.asyncio
@mark.parametrize("tol", [0.2 if platform.startswith("darwin") else 0.1])
async def test_as_completed(capsys, tol):
"""Test asyncio as_completed"""
for retry in range(3):
t = time()
skew = time() - t
for i in as_completed([asyncio.sleep(0.01 * i) for i in range(30, 0, -1)]):
await i
t = time() - t - 2 * skew
try:
assert 0.3 * (1 - tol) < t < 0.3 * (1 + tol), t
_, err = capsys.readouterr()
assert '30/30' in err
except AssertionError:
if retry == 2:
raise
async def double(i):
return i * 2
@mark.asyncio
async def test_gather(capsys):
"""Test asyncio gather"""
res = await gather(*map(double, range(30)))
_, err = capsys.readouterr()
assert '30/30' in err
assert res == list(range(0, 30 * 2, 2))

View file

@ -1,133 +1,11 @@
"""Tests `tqdm.asyncio`.""" """Tests `tqdm.asyncio` on `python>=3.7`."""
import asyncio import sys
from functools import partial
from sys import platform
from time import time
from tqdm.asyncio import tarange, tqdm_asyncio if sys.version_info[:2] > (3, 6):
from .py37_asyncio import * # NOQA, pylint: disable=wildcard-import
from .tests_tqdm import StringIO, closing, mark
tqdm = partial(tqdm_asyncio, miniters=0, mininterval=0)
trange = partial(tarange, miniters=0, mininterval=0)
as_completed = partial(tqdm_asyncio.as_completed, miniters=0, mininterval=0)
gather = partial(tqdm_asyncio.gather, miniters=0, mininterval=0)
def count(start=0, step=1):
i = start
while True:
new_start = yield i
if new_start is None:
i += step
else: else:
i = new_start from .tests_tqdm import skip
async def acount(*args, **kwargs):
for i in count(*args, **kwargs):
yield i
@mark.asyncio
async def test_break():
"""Test asyncio break"""
pbar = tqdm(count())
async for _ in pbar:
break
pbar.close()
@mark.asyncio
async def test_generators(capsys):
"""Test asyncio generators"""
with tqdm(count(), desc="counter") as pbar:
async for i in pbar:
if i >= 8:
break
_, err = capsys.readouterr()
assert '9it' in err
acounter = acount()
try: try:
with tqdm(acounter, desc="async_counter") as pbar: skip("async not supported", allow_module_level=True)
async for i in pbar: except TypeError:
if i >= 8:
break
finally:
await acounter.aclose()
_, err = capsys.readouterr()
assert '9it' in err
@mark.asyncio
async def test_range():
"""Test asyncio range"""
with closing(StringIO()) as our_file:
async for _ in tqdm(range(9), desc="range", file=our_file):
pass pass
assert '9/9' in our_file.getvalue()
our_file.seek(0)
our_file.truncate()
async for _ in trange(9, desc="trange", file=our_file):
pass
assert '9/9' in our_file.getvalue()
@mark.asyncio
async def test_nested():
"""Test asyncio nested"""
with closing(StringIO()) as our_file:
async for _ in tqdm(trange(9, desc="inner", file=our_file),
desc="outer", file=our_file):
pass
assert 'inner: 100%' in our_file.getvalue()
assert 'outer: 100%' in our_file.getvalue()
@mark.asyncio
async def test_coroutines():
"""Test asyncio coroutine.send"""
with closing(StringIO()) as our_file:
with tqdm(count(), file=our_file) as pbar:
async for i in pbar:
if i == 9:
pbar.send(-10)
elif i < 0:
assert i == -9
break
assert '10it' in our_file.getvalue()
@mark.slow
@mark.asyncio
@mark.parametrize("tol", [0.2 if platform.startswith("darwin") else 0.1])
async def test_as_completed(capsys, tol):
"""Test asyncio as_completed"""
for retry in range(3):
t = time()
skew = time() - t
for i in as_completed([asyncio.sleep(0.01 * i) for i in range(30, 0, -1)]):
await i
t = time() - t - 2 * skew
try:
assert 0.3 * (1 - tol) < t < 0.3 * (1 + tol), t
_, err = capsys.readouterr()
assert '30/30' in err
except AssertionError:
if retry == 2:
raise
async def double(i):
return i * 2
@mark.asyncio
async def test_gather(capsys):
"""Test asyncio gather"""
res = await gather(*map(double, range(30)))
_, err = capsys.readouterr()
assert '30/30' in err
assert res == list(range(0, 30 * 2, 2))

View file

@ -1,6 +1,8 @@
""" """
Tests for `tqdm.contrib`. Tests for `tqdm.contrib`.
""" """
import sys
import pytest import pytest
from tqdm import tqdm from tqdm import tqdm
@ -45,6 +47,9 @@ def test_zip(tqdm_kwargs):
with closing(StringIO()) as our_file: with closing(StringIO()) as our_file:
a = range(9) a = range(9)
b = [i + 1 for i in a] b = [i + 1 for i in a]
if sys.version_info[:1] < (3,):
assert tzip(a, b, file=our_file, **tqdm_kwargs) == zip(a, b)
else:
gen = tzip(a, b, file=our_file, **tqdm_kwargs) gen = tzip(a, b, file=our_file, **tqdm_kwargs)
assert gen != list(zip(a, b)) assert gen != list(zip(a, b))
assert list(gen) == list(zip(a, b)) assert list(gen) == list(zip(a, b))
@ -56,6 +61,11 @@ def test_map(tqdm_kwargs):
with closing(StringIO()) as our_file: with closing(StringIO()) as our_file:
a = range(9) a = range(9)
b = [i + 1 for i in a] b = [i + 1 for i in a]
if sys.version_info[:1] < (3,):
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, **tqdm_kwargs) gen = tmap(lambda x: x + 1, a, file=our_file, **tqdm_kwargs)
assert gen != b assert gen != b
assert list(gen) == b assert list(gen) == b

View file

@ -1,5 +1,7 @@
# pylint: disable=missing-module-docstring, missing-class-docstring # pylint: disable=missing-module-docstring, missing-class-docstring
# pylint: disable=missing-function-docstring, no-self-use # pylint: disable=missing-function-docstring, no-self-use
from __future__ import absolute_import
import logging import logging
import logging.handlers import logging.handlers
import sys import sys

View file

@ -1,3 +1,5 @@
from __future__ import division
from time import sleep from time import sleep
from .tests_tqdm import importorskip, mark from .tests_tqdm import importorskip, mark

View file

@ -1,3 +1,5 @@
from __future__ import division
from .tests_tqdm import importorskip, mark from .tests_tqdm import importorskip, mark
pytestmark = mark.slow pytestmark = mark.slow
@ -39,8 +41,8 @@ def test_keras(capsys):
verbose=0)]) verbose=0)])
_, res = capsys.readouterr() _, res = capsys.readouterr()
assert "training: " in res assert "training: " in res
assert f"{epochs}/{epochs}" in res assert "{epochs}/{epochs}".format(epochs=epochs) in res
assert f"{batches}/{batches}" not in res assert "{batches}/{batches}".format(batches=batches) not in res
# full (epoch and batch) progress # full (epoch and batch) progress
model.fit( model.fit(
@ -58,8 +60,8 @@ def test_keras(capsys):
verbose=2)]) verbose=2)])
_, res = capsys.readouterr() _, res = capsys.readouterr()
assert "training: " in res assert "training: " in res
assert f"{epochs}/{epochs}" in res assert "{epochs}/{epochs}".format(epochs=epochs) in res
assert f"{batches}/{batches}" in res assert "{batches}/{batches}".format(batches=batches) in res
# auto-detect epochs and batches # auto-detect epochs and batches
model.fit( model.fit(
@ -71,8 +73,8 @@ def test_keras(capsys):
callbacks=[TqdmCallback(desc="training", verbose=2)]) callbacks=[TqdmCallback(desc="training", verbose=2)])
_, res = capsys.readouterr() _, res = capsys.readouterr()
assert "training: " in res assert "training: " in res
assert f"{epochs}/{epochs}" in res assert "{epochs}/{epochs}".format(epochs=epochs) in res
assert f"{batches}/{batches}" in res assert "{batches}/{batches}".format(batches=batches) in res
# continue training (start from epoch != 0) # continue training (start from epoch != 0)
initial_epoch = 3 initial_epoch = 3
@ -87,5 +89,5 @@ def test_keras(capsys):
miniters=1, mininterval=0, maxinterval=0)]) miniters=1, mininterval=0, maxinterval=0)])
_, res = capsys.readouterr() _, res = capsys.readouterr()
assert "training: " in res assert "training: " in res
assert f"{initial_epoch - 1}/{initial_epoch - 1}" not in res assert "{epochs}/{epochs}".format(epochs=initial_epoch - 1) not in res
assert f"{epochs}/{epochs}" in res assert "{epochs}/{epochs}".format(epochs=epochs) in res

View file

@ -8,17 +8,17 @@ from os import linesep
from tqdm.cli import TqdmKeyError, TqdmTypeError, main from tqdm.cli import TqdmKeyError, TqdmTypeError, main
from tqdm.utils import IS_WIN from tqdm.utils import IS_WIN
from .tests_tqdm import BytesIO, closing, mark, raises from .tests_tqdm import BytesIO, _range, closing, mark, raises
def restore_sys(func): def restore_sys(func):
"""Decorates `func(capsysbinary)` to save & restore `sys.(stdin|argv)`.""" """Decorates `func(capsysbin)` to save & restore `sys.(stdin|argv)`."""
@wraps(func) @wraps(func)
def inner(capsysbinary): def inner(capsysbin):
"""function requiring capsysbinary which may alter `sys.(stdin|argv)`""" """function requiring capsysbin which may alter `sys.(stdin|argv)`"""
_SYS = sys.stdin, sys.argv _SYS = sys.stdin, sys.argv
try: try:
res = func(capsysbinary) res = func(capsysbin)
finally: finally:
sys.stdin, sys.argv = _SYS sys.stdin, sys.argv = _SYS
return res return res
@ -58,7 +58,7 @@ def test_main_import():
N = 123 N = 123
_SYS = sys.stdin, sys.argv _SYS = sys.stdin, sys.argv
# test direct import # test direct import
sys.stdin = [str(i).encode() for i in range(N)] sys.stdin = [str(i).encode() for i in _range(N)]
sys.argv = ['', '--desc', 'Test CLI import', sys.argv = ['', '--desc', 'Test CLI import',
'--ascii', 'True', '--unit_scale', 'True'] '--ascii', 'True', '--unit_scale', 'True']
try: try:
@ -68,19 +68,19 @@ def test_main_import():
@restore_sys @restore_sys
def test_main_bytes(capsysbinary): def test_main_bytes(capsysbin):
"""Test CLI --bytes""" """Test CLI --bytes"""
N = 123 N = 123
# test --delim # test --delim
IN_DATA = '\0'.join(map(str, range(N))).encode() IN_DATA = '\0'.join(map(str, _range(N))).encode()
with closing(BytesIO()) as sys.stdin: with closing(BytesIO()) as sys.stdin:
sys.stdin.write(IN_DATA) sys.stdin.write(IN_DATA)
# sys.stdin.write(b'\xff') # TODO # sys.stdin.write(b'\xff') # TODO
sys.stdin.seek(0) sys.stdin.seek(0)
main(sys.stderr, ['--desc', 'Test CLI delim', '--ascii', 'True', main(sys.stderr, ['--desc', 'Test CLI delim', '--ascii', 'True',
'--delim', r'\0', '--buf_size', '64']) '--delim', r'\0', '--buf_size', '64'])
out, err = capsysbinary.readouterr() out, err = capsysbin.readouterr()
assert out == IN_DATA assert out == IN_DATA
assert str(N) + "it" in err.decode("U8") assert str(N) + "it" in err.decode("U8")
@ -90,26 +90,27 @@ def test_main_bytes(capsysbinary):
sys.stdin.write(IN_DATA) sys.stdin.write(IN_DATA)
sys.stdin.seek(0) sys.stdin.seek(0)
main(sys.stderr, ['--ascii', '--bytes=True', '--unit_scale', 'False']) main(sys.stderr, ['--ascii', '--bytes=True', '--unit_scale', 'False'])
out, err = capsysbinary.readouterr() out, err = capsysbin.readouterr()
assert out == IN_DATA assert out == IN_DATA
assert str(len(IN_DATA)) + "B" in err.decode("U8") assert str(len(IN_DATA)) + "B" in err.decode("U8")
def test_main_log(capsysbinary, caplog): @mark.skipif(sys.version_info[0] == 2, reason="no caplog on py2")
def test_main_log(capsysbin, caplog):
"""Test CLI --log""" """Test CLI --log"""
_SYS = sys.stdin, sys.argv _SYS = sys.stdin, sys.argv
N = 123 N = 123
sys.stdin = [(str(i) + '\n').encode() for i in range(N)] sys.stdin = [(str(i) + '\n').encode() for i in _range(N)]
IN_DATA = b''.join(sys.stdin) IN_DATA = b''.join(sys.stdin)
try: try:
with caplog.at_level(logging.INFO): with caplog.at_level(logging.INFO):
main(sys.stderr, ['--log', 'INFO']) main(sys.stderr, ['--log', 'INFO'])
out, err = capsysbinary.readouterr() out, err = capsysbin.readouterr()
assert norm(out) == IN_DATA and b"123/123" in err assert norm(out) == IN_DATA and b"123/123" in err
assert not caplog.record_tuples assert not caplog.record_tuples
with caplog.at_level(logging.DEBUG): with caplog.at_level(logging.DEBUG):
main(sys.stderr, ['--log', 'DEBUG']) main(sys.stderr, ['--log', 'DEBUG'])
out, err = capsysbinary.readouterr() out, err = capsysbin.readouterr()
assert norm(out) == IN_DATA and b"123/123" in err assert norm(out) == IN_DATA and b"123/123" in err
assert caplog.record_tuples assert caplog.record_tuples
finally: finally:
@ -117,39 +118,39 @@ def test_main_log(capsysbinary, caplog):
@restore_sys @restore_sys
def test_main(capsysbinary): def test_main(capsysbin):
"""Test misc CLI options""" """Test misc CLI options"""
N = 123 N = 123
sys.stdin = [(str(i) + '\n').encode() for i in range(N)] sys.stdin = [(str(i) + '\n').encode() for i in _range(N)]
IN_DATA = b''.join(sys.stdin) IN_DATA = b''.join(sys.stdin)
# test --tee # test --tee
main(sys.stderr, ['--mininterval', '0', '--miniters', '1']) main(sys.stderr, ['--mininterval', '0', '--miniters', '1'])
out, err = capsysbinary.readouterr() out, err = capsysbin.readouterr()
assert norm(out) == IN_DATA and b"123/123" in err assert norm(out) == IN_DATA and b"123/123" in err
assert N <= len(err.split(b"\r")) < N + 5 assert N <= len(err.split(b"\r")) < N + 5
len_err = len(err) len_err = len(err)
main(sys.stderr, ['--tee', '--mininterval', '0', '--miniters', '1']) main(sys.stderr, ['--tee', '--mininterval', '0', '--miniters', '1'])
out, err = capsysbinary.readouterr() out, err = capsysbin.readouterr()
assert norm(out) == IN_DATA and b"123/123" in err assert norm(out) == IN_DATA and b"123/123" in err
# spaces to clear intermediate lines could increase length # spaces to clear intermediate lines could increase length
assert len_err + len(norm(out)) <= len(err) assert len_err + len(norm(out)) <= len(err)
# test --null # test --null
main(sys.stderr, ['--null']) main(sys.stderr, ['--null'])
out, err = capsysbinary.readouterr() out, err = capsysbin.readouterr()
assert not out and b"123/123" in err assert not out and b"123/123" in err
# test integer --update # test integer --update
main(sys.stderr, ['--update']) main(sys.stderr, ['--update'])
out, err = capsysbinary.readouterr() out, err = capsysbin.readouterr()
assert norm(out) == IN_DATA assert norm(out) == IN_DATA
assert (str(N // 2 * N) + "it").encode() in err, "expected arithmetic sum formula" assert (str(N // 2 * N) + "it").encode() in err, "expected arithmetic sum formula"
# test integer --update_to # test integer --update_to
main(sys.stderr, ['--update-to']) main(sys.stderr, ['--update-to'])
out, err = capsysbinary.readouterr() out, err = capsysbin.readouterr()
assert norm(out) == IN_DATA assert norm(out) == IN_DATA
assert (str(N - 1) + "it").encode() in err assert (str(N - 1) + "it").encode() in err
assert (str(N) + "it").encode() not in err assert (str(N) + "it").encode() not in err
@ -160,23 +161,23 @@ def test_main(capsysbinary):
# test integer --update --delim # test integer --update --delim
sys.stdin.seek(0) sys.stdin.seek(0)
main(sys.stderr, ['--update', '--delim', 'D']) main(sys.stderr, ['--update', '--delim', 'D'])
out, err = capsysbinary.readouterr() out, err = capsysbin.readouterr()
assert out == IN_DATA.replace(b'\n', b'D') assert out == IN_DATA.replace(b'\n', b'D')
assert (str(N // 2 * N) + "it").encode() in err, "expected arithmetic sum" assert (str(N // 2 * N) + "it").encode() in err, "expected arithmetic sum"
# test integer --update_to --delim # test integer --update_to --delim
sys.stdin.seek(0) sys.stdin.seek(0)
main(sys.stderr, ['--update-to', '--delim', 'D']) main(sys.stderr, ['--update-to', '--delim', 'D'])
out, err = capsysbinary.readouterr() out, err = capsysbin.readouterr()
assert out == IN_DATA.replace(b'\n', b'D') assert out == IN_DATA.replace(b'\n', b'D')
assert (str(N - 1) + "it").encode() in err assert (str(N - 1) + "it").encode() in err
assert (str(N) + "it").encode() not in err assert (str(N) + "it").encode() not in err
# test float --update_to # test float --update_to
sys.stdin = [(str(i / 2.0) + '\n').encode() for i in range(N)] sys.stdin = [(str(i / 2.0) + '\n').encode() for i in _range(N)]
IN_DATA = b''.join(sys.stdin) IN_DATA = b''.join(sys.stdin)
main(sys.stderr, ['--update-to']) main(sys.stderr, ['--update-to'])
out, err = capsysbinary.readouterr() out, err = capsysbin.readouterr()
assert norm(out) == IN_DATA assert norm(out) == IN_DATA
assert (str((N - 1) / 2.0) + "it").encode() in err assert (str((N - 1) / 2.0) + "it").encode() in err
assert (str(N / 2.0) + "it").encode() not in err assert (str(N / 2.0) + "it").encode() not in err
@ -212,30 +213,30 @@ def test_comppath(tmp_path):
@restore_sys @restore_sys
def test_exceptions(capsysbinary): def test_exceptions(capsysbin):
"""Test CLI Exceptions""" """Test CLI Exceptions"""
N = 123 N = 123
sys.stdin = [str(i) + '\n' for i in range(N)] sys.stdin = [str(i) + '\n' for i in _range(N)]
IN_DATA = ''.join(sys.stdin).encode() IN_DATA = ''.join(sys.stdin).encode()
with raises(TqdmKeyError, match="bad_arg_u_ment"): with raises(TqdmKeyError, match="bad_arg_u_ment"):
main(sys.stderr, argv=['-ascii', '-unit_scale', '--bad_arg_u_ment', 'foo']) main(sys.stderr, argv=['-ascii', '-unit_scale', '--bad_arg_u_ment', 'foo'])
out, _ = capsysbinary.readouterr() out, _ = capsysbin.readouterr()
assert norm(out) == IN_DATA assert norm(out) == IN_DATA
with raises(TqdmTypeError, match="invalid_bool_value"): with raises(TqdmTypeError, match="invalid_bool_value"):
main(sys.stderr, argv=['-ascii', '-unit_scale', 'invalid_bool_value']) main(sys.stderr, argv=['-ascii', '-unit_scale', 'invalid_bool_value'])
out, _ = capsysbinary.readouterr() out, _ = capsysbin.readouterr()
assert norm(out) == IN_DATA assert norm(out) == IN_DATA
with raises(TqdmTypeError, match="invalid_int_value"): with raises(TqdmTypeError, match="invalid_int_value"):
main(sys.stderr, argv=['-ascii', '--total', 'invalid_int_value']) main(sys.stderr, argv=['-ascii', '--total', 'invalid_int_value'])
out, _ = capsysbinary.readouterr() out, _ = capsysbin.readouterr()
assert norm(out) == IN_DATA assert norm(out) == IN_DATA
with raises(TqdmKeyError, match="Can only have one of --"): with raises(TqdmKeyError, match="Can only have one of --"):
main(sys.stderr, argv=['--update', '--update_to']) main(sys.stderr, argv=['--update', '--update_to'])
out, _ = capsysbinary.readouterr() out, _ = capsysbin.readouterr()
assert norm(out) == IN_DATA assert norm(out) == IN_DATA
# test SystemExits # test SystemExits

View file

@ -4,7 +4,6 @@ from .tests_tqdm import StringIO, closing, importorskip, mark, skip
pytestmark = mark.slow pytestmark = mark.slow
np = importorskip('numpy')
random = importorskip('numpy.random') random = importorskip('numpy.random')
rand = random.rand rand = random.rand
randint = random.randint randint = random.randint
@ -40,8 +39,8 @@ def test_pandas_rolling_expanding():
our_file.seek(0) our_file.seek(0)
if our_file.getvalue().count(exres) < 2: if our_file.getvalue().count(exres) < 2:
our_file.seek(0) our_file.seek(0)
raise AssertionError( raise AssertionError("\nExpected:\n{0}\nIn:\n{1}\n".format(
f"\nExpected:\n{exres} at least twice.\nIn:\n{our_file.read()}\n") exres + " at least twice.", our_file.read()))
def test_pandas_series(): def test_pandas_series():
@ -63,11 +62,10 @@ def test_pandas_series():
our_file.seek(0) our_file.seek(0)
if our_file.getvalue().count(exres) < 2: if our_file.getvalue().count(exres) < 2:
our_file.seek(0) our_file.seek(0)
raise AssertionError( raise AssertionError("\nExpected:\n{0}\nIn:\n{1}\n".format(
f"\nExpected:\n{exres} at least twice.\nIn:\n{our_file.read()}\n") exres + " at least twice.", our_file.read()))
@mark.filterwarnings("ignore:DataFrame.applymap has been deprecated:FutureWarning")
def test_pandas_data_frame(): def test_pandas_data_frame():
"""Test pandas.DataFrame.progress_apply and .progress_applymap""" """Test pandas.DataFrame.progress_apply and .progress_applymap"""
with closing(StringIO()) as our_file: with closing(StringIO()) as our_file:
@ -82,12 +80,6 @@ def test_pandas_data_frame():
res2 = df.applymap(task_func) res2 = df.applymap(task_func)
assert res1.equals(res2) assert res1.equals(res2)
# map
if hasattr(df, 'map'): # pandas>=2.1.0
res1 = df.progress_map(task_func)
res2 = df.map(task_func)
assert res1.equals(res2)
# apply unhashable # apply unhashable
res1 = [] res1 = []
df.progress_apply(res1.extend) df.progress_apply(res1.extend)
@ -102,8 +94,8 @@ def test_pandas_data_frame():
our_file.seek(0) our_file.seek(0)
if our_file.read().count('100%') < 3: if our_file.read().count('100%') < 3:
our_file.seek(0) our_file.seek(0)
raise AssertionError( raise AssertionError("\nExpected:\n{0}\nIn:\n{1}\n".format(
f"\nExpected:\n100% at least three times\nIn:\n{our_file.read()}\n") '100% at least three times', our_file.read()))
# apply_map, apply axis=0, apply axis=1 # apply_map, apply axis=0, apply axis=1
expects = ['20000/20000', '200/200', '100/100'] expects = ['20000/20000', '200/200', '100/100']
@ -111,12 +103,10 @@ def test_pandas_data_frame():
our_file.seek(0) our_file.seek(0)
if our_file.getvalue().count(exres) < 1: if our_file.getvalue().count(exres) < 1:
our_file.seek(0) our_file.seek(0)
raise AssertionError( raise AssertionError("\nExpected:\n{0}\nIn:\n {1}\n".format(
f"\nExpected:\n{exres} at least once.\nIn:\n{our_file.read()}\n") exres + " at least once.", our_file.read()))
@mark.filterwarnings(
"ignore:DataFrameGroupBy.apply operated on the grouping columns:DeprecationWarning")
def test_pandas_groupby_apply(): def test_pandas_groupby_apply():
"""Test pandas.DataFrame.groupby(...).progress_apply""" """Test pandas.DataFrame.groupby(...).progress_apply"""
with closing(StringIO()) as our_file: with closing(StringIO()) as our_file:
@ -129,8 +119,8 @@ def test_pandas_groupby_apply():
dfs.groupby(['a']).progress_apply(lambda x: None) dfs.groupby(['a']).progress_apply(lambda x: None)
df2 = df = pd.DataFrame({'a': randint(1, 8, 10000), 'b': rand(10000)}) df2 = df = pd.DataFrame({'a': randint(1, 8, 10000), 'b': rand(10000)})
res1 = df2.groupby("a").apply(np.maximum.reduce) res1 = df2.groupby("a").apply(max)
res2 = df2.groupby("a").progress_apply(np.maximum.reduce) res2 = df2.groupby("a").progress_apply(max)
assert res1.equals(res2) assert res1.equals(res2)
our_file.seek(0) our_file.seek(0)
@ -140,7 +130,8 @@ def test_pandas_groupby_apply():
nexres = '100%|##########|' nexres = '100%|##########|'
if nexres in our_file.read(): if nexres in our_file.read():
our_file.seek(0) our_file.seek(0)
raise AssertionError(f"\nDid not expect:\n{nexres}\nIn:{our_file.read()}\n") raise AssertionError("\nDid not expect:\n{0}\nIn:{1}\n".format(
nexres, our_file.read()))
with closing(StringIO()) as our_file: with closing(StringIO()) as our_file:
tqdm.pandas(file=our_file, leave=True, ascii=True) tqdm.pandas(file=our_file, leave=True, ascii=True)
@ -149,28 +140,26 @@ def test_pandas_groupby_apply():
dfs.loc[0] = [2, 1, 1] dfs.loc[0] = [2, 1, 1]
dfs['d'] = 100 dfs['d'] = 100
expects = ['500/500', '1/1', '4/4', '4/4'] expects = ['500/500', '1/1', '4/4', '2/2']
dfs.groupby(dfs.index).progress_apply(lambda x: None) dfs.groupby(dfs.index).progress_apply(lambda x: None)
dfs.groupby('d').progress_apply(lambda x: None) dfs.groupby('d').progress_apply(lambda x: None)
dfs.T.groupby(dfs.columns).progress_apply(lambda x: None) dfs.groupby(dfs.columns, axis=1).progress_apply(lambda x: None)
dfs.T.groupby([2, 2, 1, 1]).progress_apply(lambda x: None) dfs.groupby([2, 2, 1, 1], axis=1).progress_apply(lambda x: None)
our_file.seek(0) our_file.seek(0)
if our_file.read().count('100%') < 4: if our_file.read().count('100%') < 4:
our_file.seek(0) our_file.seek(0)
raise AssertionError( raise AssertionError("\nExpected:\n{0}\nIn:\n{1}\n".format(
f"\nExpected:\n100% at least four times\nIn:\n{our_file.read()}\n") '100% at least four times', our_file.read()))
for exres in expects: for exres in expects:
our_file.seek(0) our_file.seek(0)
if our_file.getvalue().count(exres) < 1: if our_file.getvalue().count(exres) < 1:
our_file.seek(0) our_file.seek(0)
raise AssertionError( raise AssertionError("\nExpected:\n{0}\nIn:\n {1}\n".format(
f"\nExpected:\n{exres} at least once.\nIn:\n{our_file.read()}\n") exres + " at least once.", our_file.read()))
@mark.filterwarnings(
"ignore:DataFrameGroupBy.apply operated on the grouping columns:DeprecationWarning")
def test_pandas_leave(): def test_pandas_leave():
"""Test pandas with `leave=True`""" """Test pandas with `leave=True`"""
with closing(StringIO()) as our_file: with closing(StringIO()) as our_file:
@ -183,7 +172,8 @@ def test_pandas_leave():
exres = '100%|##########| 100/100' exres = '100%|##########| 100/100'
if exres not in our_file.read(): if exres not in our_file.read():
our_file.seek(0) our_file.seek(0)
raise AssertionError(f"\nExpected:\n{exres}\nIn:{our_file.read()}\n") raise AssertionError("\nExpected:\n{0}\nIn:{1}\n".format(
exres, our_file.read()))
def test_pandas_apply_args_deprecation(): def test_pandas_apply_args_deprecation():
@ -205,8 +195,6 @@ def test_pandas_apply_args_deprecation():
"keyword arguments instead")) "keyword arguments instead"))
@mark.filterwarnings(
"ignore:DataFrameGroupBy.apply operated on the grouping columns:DeprecationWarning")
def test_pandas_deprecation(): def test_pandas_deprecation():
"""Test bar object instance as argument deprecation""" """Test bar object instance as argument deprecation"""
try: try:

View file

@ -1,3 +1,5 @@
from __future__ import division, print_function
import sys import sys
from contextlib import contextmanager from contextlib import contextmanager
from functools import wraps from functools import wraps
@ -12,7 +14,7 @@ except ImportError:
from tqdm import tqdm, trange from tqdm import tqdm, trange
from .tests_tqdm import importorskip, mark, patch_lock, skip from .tests_tqdm import _range, importorskip, mark, patch_lock, skip
pytestmark = mark.slow pytestmark = mark.slow
@ -96,7 +98,10 @@ def simple_progress(iterable=None, total=None, file=sys.stdout, desc='',
def format_interval(t): def format_interval(t):
mins, s = divmod(int(t), 60) mins, s = divmod(int(t), 60)
h, m = divmod(mins, 60) h, m = divmod(mins, 60)
return f'{h:d}:{m:02d}:{s:02d}' if h else f'{m:02d}:{s:02d}' if h:
return '{0:d}:{1:02d}:{2:02d}'.format(h, m, s)
else:
return '{0:02d}:{1:02d}'.format(m, s)
def update_and_print(i=1): def update_and_print(i=1):
n[0] += i n[0] += i
@ -138,6 +143,7 @@ def simple_progress(iterable=None, total=None, file=sys.stdout, desc='',
update_and_print(0) update_and_print(0)
if iterable is not None: if iterable is not None:
return update_and_yield() return update_and_yield()
else:
return update_and_print return update_and_print
@ -145,8 +151,12 @@ def assert_performance(thresh, name_left, time_left, name_right, time_right):
"""raises if time_left > thresh * time_right""" """raises if time_left > thresh * time_right"""
if time_left > thresh * time_right: if time_left > thresh * time_right:
raise ValueError( raise ValueError(
f'{name_left}: {time_left:f}, {name_right}: {time_right:f}' ('{name[0]}: {time[0]:f}, '
f', ratio {time_left / time_right:f} > {thresh:f}') '{name[1]}: {time[1]:f}, '
'ratio {ratio:f} > {thresh:f}').format(
name=(name_left, name_right),
time=(time_left, time_right),
ratio=time_left / time_right, thresh=thresh))
@retry_on_except() @retry_on_except()
@ -163,7 +173,7 @@ def test_iter_basic_overhead():
a = 0 a = 0
with relative_timer() as time_bench: with relative_timer() as time_bench:
for i in range(total): for i in _range(total):
a += i a += i
sys.stdout.write(str(a)) sys.stdout.write(str(a))
@ -178,13 +188,13 @@ def test_manual_basic_overhead():
with tqdm(total=total * 10, leave=True) as t: with tqdm(total=total * 10, leave=True) as t:
a = 0 a = 0
with relative_timer() as time_tqdm: with relative_timer() as time_tqdm:
for i in range(total): for i in _range(total):
a += i a += i
t.update(10) t.update(10)
a = 0 a = 0
with relative_timer() as time_bench: with relative_timer() as time_bench:
for i in range(total): for i in _range(total):
a += i a += i
sys.stdout.write(str(a)) sys.stdout.write(str(a))
@ -239,7 +249,7 @@ def test_iter_overhead_hard():
a = 0 a = 0
with relative_timer() as time_bench: with relative_timer() as time_bench:
for i in range(total): for i in _range(total):
a += i a += i
sys.stdout.write(("%i" % a) * 40) sys.stdout.write(("%i" % a) * 40)
@ -255,13 +265,13 @@ def test_manual_overhead_hard():
mininterval=0, maxinterval=0) as t: mininterval=0, maxinterval=0) as t:
a = 0 a = 0
with relative_timer() as time_tqdm: with relative_timer() as time_tqdm:
for i in range(total): for i in _range(total):
a += i a += i
t.update(10) t.update(10)
a = 0 a = 0
with relative_timer() as time_bench: with relative_timer() as time_bench:
for i in range(total): for i in _range(total):
a += i a += i
sys.stdout.write(("%i" % a) * 40) sys.stdout.write(("%i" % a) * 40)
@ -282,7 +292,7 @@ def test_iter_overhead_simplebar_hard():
assert a == (total ** 2 - total) / 2.0 assert a == (total ** 2 - total) / 2.0
a = 0 a = 0
s = simple_progress(range(total), leave=True, s = simple_progress(_range(total), leave=True,
miniters=1, mininterval=0) miniters=1, mininterval=0)
with relative_timer() as time_bench: with relative_timer() as time_bench:
for i in s: for i in s:
@ -300,7 +310,7 @@ def test_manual_overhead_simplebar_hard():
mininterval=0, maxinterval=0) as t: mininterval=0, maxinterval=0) as t:
a = 0 a = 0
with relative_timer() as time_tqdm: with relative_timer() as time_tqdm:
for i in range(total): for i in _range(total):
a += i a += i
t.update(10) t.update(10)
@ -308,7 +318,7 @@ def test_manual_overhead_simplebar_hard():
miniters=1, mininterval=0) miniters=1, mininterval=0)
a = 0 a = 0
with relative_timer() as time_bench: with relative_timer() as time_bench:
for i in range(total): for i in _range(total):
a += i a += i
simplebar_update(10) simplebar_update(10)

View file

@ -1,7 +1,10 @@
"""Test `tqdm.rich`.""" """Test `tqdm.rich`."""
from .tests_tqdm import importorskip import sys
from .tests_tqdm import importorskip, mark
@mark.skipif(sys.version_info[:3] < (3, 6, 1), reason="`rich` needs py>=3.6.1")
def test_rich_import(): def test_rich_import():
"""Test `tqdm.rich` import""" """Test `tqdm.rich` import"""
importorskip('tqdm.rich') importorskip('tqdm.rich')

View file

@ -1,9 +1,13 @@
from __future__ import division
import sys
from functools import wraps from functools import wraps
from threading import Event from threading import Event
from time import sleep, time from time import sleep, time
from tqdm import TMonitor, tqdm, trange from tqdm import TMonitor, tqdm, trange
from .tests_perf import retry_on_except
from .tests_tqdm import StringIO, closing, importorskip, patch_lock, skip from .tests_tqdm import StringIO, closing, importorskip, patch_lock, skip
@ -33,13 +37,18 @@ class Time(object):
sleep(0.000001) # sleep to allow interrupt (instead of pass) sleep(0.000001) # sleep to allow interrupt (instead of pass)
class FakeEvent(Event): def FakeEvent():
"""patched `threading.Event` where `wait()` uses `Time.fake_sleep()`""" """patched `threading.Event` where `wait()` uses `Time.fake_sleep()`"""
def wait(self, timeout=None): event = Event() # not a class in py2 so can't inherit
def wait(timeout=None):
"""uses Time.fake_sleep""" """uses Time.fake_sleep"""
if timeout is not None: if timeout is not None:
Time.fake_sleep(timeout) Time.fake_sleep(timeout)
return self.is_set() return event.is_set()
event.wait = wait
return event
def patch_sleep(func): def patch_sleep(func):
@ -197,11 +206,19 @@ def test_imap():
assert res[-1] == 100 assert res[-1] == 100
# py2: locks won't propagate to incr_bar so may cause `AttributeError`
@retry_on_except(n=3 if sys.version_info < (3,) else 1, check_cpu_time=False)
@patch_lock(thread=True) @patch_lock(thread=True)
def test_threadpool(): def test_threadpool():
"""Test concurrent.futures.ThreadPoolExecutor""" """Test concurrent.futures.ThreadPoolExecutor"""
ThreadPoolExecutor = importorskip('concurrent.futures').ThreadPoolExecutor ThreadPoolExecutor = importorskip('concurrent.futures').ThreadPoolExecutor
with ThreadPoolExecutor(8) as pool: with ThreadPoolExecutor(8) as pool:
try:
res = list(tqdm(pool.map(incr_bar, range(100)), disable=True)) res = list(tqdm(pool.map(incr_bar, range(100)), disable=True))
except AttributeError:
if sys.version_info < (3,):
skip("not supported on py2")
else:
raise
assert sum(res) == sum(range(1, 101)) assert sum(res) == sum(range(1, 101))

View file

@ -1,6 +1,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Advice: use repr(our_file.read()) to print the full output of tqdm # Advice: use repr(our_file.read()) to print the full output of tqdm
# (else '\r' will replace the previous lines and you'll see only the latest. # (else '\r' will replace the previous lines and you'll see only the latest.
from __future__ import print_function
import csv import csv
import os import os
import re import re
@ -35,6 +37,16 @@ if getattr(StringIO, '__exit__', False) and getattr(StringIO, '__enter__', False
else: else:
from contextlib import closing from contextlib import closing
try:
_range = xrange
except NameError:
_range = range
try:
_unicode = unicode
except NameError:
_unicode = str
nt_and_no_colorama = False nt_and_no_colorama = False
if os.name == 'nt': if os.name == 'nt':
try: try:
@ -107,7 +119,7 @@ def cpu_timify(t, timer=None):
class UnicodeIO(IOBase): class UnicodeIO(IOBase):
"""Unicode version of StringIO""" """Unicode version of StringIO"""
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super(UnicodeIO, self).__init__(*args, **kwargs)
self.encoding = 'U8' # io.StringIO supports unicode, but no encoding self.encoding = 'U8' # io.StringIO supports unicode, but no encoding
self.text = '' self.text = ''
self.cursor = 0 self.cursor = 0
@ -189,8 +201,6 @@ def test_format_num():
assert float(format_num(1337)) == 1337 assert float(format_num(1337)) == 1337
assert format_num(int(1e6)) == '1e+6' assert format_num(int(1e6)) == '1e+6'
assert format_num(1239876) == '1' '239' '876' assert format_num(1239876) == '1' '239' '876'
assert format_num(0.00001234) == '1.23e-5'
assert format_num(-0.1234) == '-0.123'
def test_format_meter(): def test_format_meter():
@ -261,6 +271,7 @@ def test_format_meter():
20, 100, 12, ncols=14, rate=8.1, 20, 100, 12, ncols=14, rate=8.1,
bar_format=r'{l_bar}{bar}|{n_fmt}/{total_fmt}') == " 20%|" + unich(0x258d) + " |20/100" bar_format=r'{l_bar}{bar}|{n_fmt}/{total_fmt}') == " 20%|" + unich(0x258d) + " |20/100"
# Check wide characters # Check wide characters
if sys.version_info >= (3,):
assert format_meter(0, 1000, 13, ncols=68, prefix=': ') == ( assert format_meter(0, 1000, 13, ncols=68, prefix=': ') == (
": 0%| | 0/1000 [00:13<?, ?it/s]") ": 0%| | 0/1000 [00:13<?, ?it/s]")
assert format_meter(0, 1000, 13, ncols=68, prefix='ニッポン [ニッポン]: ') == ( assert format_meter(0, 1000, 13, ncols=68, prefix='ニッポン [ニッポン]: ') == (
@ -317,11 +328,11 @@ def test_si_format():
def test_bar_formatspec(): def test_bar_formatspec():
"""Test Bar.__format__ spec""" """Test Bar.__format__ spec"""
assert f"{Bar(0.3):5a}" == "#5 " assert "{0:5a}".format(Bar(0.3)) == "#5 "
assert f"{Bar(0.5, charset=' .oO0'):2}" == "0 " assert "{0:2}".format(Bar(0.5, charset=" .oO0")) == "0 "
assert f"{Bar(0.5, charset=' .oO0'):2a}" == "# " assert "{0:2a}".format(Bar(0.5, charset=" .oO0")) == "# "
assert f"{Bar(0.5, 10):-6a}" == '## ' assert "{0:-6a}".format(Bar(0.5, 10)) == '## '
assert f"{Bar(0.5, 10):2b}" == ' ' assert "{0:2b}".format(Bar(0.5, 10)) == ' '
def test_all_defaults(): def test_all_defaults():
@ -342,7 +353,7 @@ def test_all_defaults():
class WriteTypeChecker(BytesIO): class WriteTypeChecker(BytesIO):
"""File-like to assert the expected type is written""" """File-like to assert the expected type is written"""
def __init__(self, expected_type): def __init__(self, expected_type):
super().__init__() super(WriteTypeChecker, self).__init__()
self.expected_type = expected_type self.expected_type = expected_type
def write(self, s): def write(self, s):
@ -390,7 +401,7 @@ def test_iterate_over_csv_rows():
# Create a test csv pseudo file # Create a test csv pseudo file
with closing(StringIO()) as test_csv_file: with closing(StringIO()) as test_csv_file:
writer = csv.writer(test_csv_file) writer = csv.writer(test_csv_file)
for _ in range(3): for _ in _range(3):
writer.writerow(['test'] * 3) writer.writerow(['test'] * 3)
test_csv_file.seek(0) test_csv_file.seek(0)
@ -404,7 +415,7 @@ def test_iterate_over_csv_rows():
def test_file_output(): def test_file_output():
"""Test output to arbitrary file-like objects""" """Test output to arbitrary file-like objects"""
with closing(StringIO()) as our_file: with closing(StringIO()) as our_file:
for i in tqdm(range(3), file=our_file): for i in tqdm(_range(3), file=our_file):
if i == 1: if i == 1:
our_file.seek(0) our_file.seek(0)
assert '0/3' in our_file.read() assert '0/3' in our_file.read()
@ -413,14 +424,14 @@ def test_file_output():
def test_leave_option(): def test_leave_option():
"""Test `leave=True` always prints info about the last iteration""" """Test `leave=True` always prints info about the last iteration"""
with closing(StringIO()) as our_file: with closing(StringIO()) as our_file:
for _ in tqdm(range(3), file=our_file, leave=True): for _ in tqdm(_range(3), file=our_file, leave=True):
pass pass
res = our_file.getvalue() res = our_file.getvalue()
assert '| 3/3 ' in res assert '| 3/3 ' in res
assert '\n' == res[-1] # not '\r' assert '\n' == res[-1] # not '\r'
with closing(StringIO()) as our_file2: with closing(StringIO()) as our_file2:
for _ in tqdm(range(3), file=our_file2, leave=False): for _ in tqdm(_range(3), file=our_file2, leave=False):
pass pass
assert '| 3/3 ' not in our_file2.getvalue() assert '| 3/3 ' not in our_file2.getvalue()
@ -441,7 +452,7 @@ def test_trange():
def test_min_interval(): def test_min_interval():
"""Test mininterval""" """Test mininterval"""
with closing(StringIO()) as our_file: with closing(StringIO()) as our_file:
for _ in tqdm(range(3), file=our_file, mininterval=1e-10): for _ in tqdm(_range(3), file=our_file, mininterval=1e-10):
pass pass
assert " 0%| | 0/3 [00:00<" in our_file.getvalue() assert " 0%| | 0/3 [00:00<" in our_file.getvalue()
@ -473,7 +484,7 @@ def test_max_interval():
t.update(bigstep) t.update(bigstep)
t2.update(bigstep) t2.update(bigstep)
# The next iterations should not trigger maxinterval (step 10) # The next iterations should not trigger maxinterval (step 10)
for _ in range(4): for _ in _range(4):
t.update(smallstep) t.update(smallstep)
t2.update(smallstep) t2.update(smallstep)
timer.sleep(1e-5) timer.sleep(1e-5)
@ -493,7 +504,7 @@ def test_max_interval():
# Increase 10 iterations at once # Increase 10 iterations at once
t.update(bigstep) t.update(bigstep)
# The next iterations should trigger maxinterval (step 5) # The next iterations should trigger maxinterval (step 5)
for _ in range(4): for _ in _range(4):
t.update(smallstep) t.update(smallstep)
timer.sleep(1e-2) timer.sleep(1e-2)
@ -502,7 +513,7 @@ def test_max_interval():
# Test iteration based tqdm with maxinterval effect # Test iteration based tqdm with maxinterval effect
timer = DiscreteTimer() timer = DiscreteTimer()
with closing(StringIO()) as our_file: with closing(StringIO()) as our_file:
with tqdm(range(total), file=our_file, miniters=None, with tqdm(_range(total), file=our_file, miniters=None,
mininterval=1e-5, smoothing=1, maxinterval=1e-4) as t2: mininterval=1e-5, smoothing=1, maxinterval=1e-4) as t2:
cpu_timify(t2, timer) cpu_timify(t2, timer)
@ -549,9 +560,9 @@ def test_max_interval():
mininterval = 0.1 mininterval = 0.1
maxinterval = 10 maxinterval = 10
with closing(StringIO()) as our_file: with closing(StringIO()) as our_file:
t1 = tqdm(range(total), file=our_file, miniters=None, smoothing=1, t1 = tqdm(_range(total), file=our_file, miniters=None, smoothing=1,
mininterval=mininterval, maxinterval=maxinterval) mininterval=mininterval, maxinterval=maxinterval)
t2 = tqdm(range(total), file=our_file, miniters=None, smoothing=1, t2 = tqdm(_range(total), file=our_file, miniters=None, smoothing=1,
mininterval=0, maxinterval=maxinterval) mininterval=0, maxinterval=maxinterval)
cpu_timify(t1, timer1) cpu_timify(t1, timer1)
@ -594,7 +605,7 @@ def test_delay():
def test_min_iters(): def test_min_iters():
"""Test miniters""" """Test miniters"""
with closing(StringIO()) as our_file: with closing(StringIO()) as our_file:
for _ in tqdm(range(3), file=our_file, leave=True, mininterval=0, miniters=2): for _ in tqdm(_range(3), file=our_file, leave=True, mininterval=0, miniters=2):
pass pass
out = our_file.getvalue() out = our_file.getvalue()
@ -604,7 +615,7 @@ def test_min_iters():
assert '| 3/3 ' in out assert '| 3/3 ' in out
with closing(StringIO()) as our_file: with closing(StringIO()) as our_file:
for _ in tqdm(range(3), file=our_file, leave=True, mininterval=0, miniters=1): for _ in tqdm(_range(3), file=our_file, leave=True, mininterval=0, miniters=1):
pass pass
out = our_file.getvalue() out = our_file.getvalue()
@ -658,7 +669,7 @@ def test_dynamic_min_iters():
# Check iterable based tqdm # Check iterable based tqdm
with closing(StringIO()) as our_file: with closing(StringIO()) as our_file:
t = tqdm(range(10), file=our_file, miniters=None, mininterval=None, t = tqdm(_range(10), file=our_file, miniters=None, mininterval=None,
smoothing=0.5) smoothing=0.5)
for _ in t: for _ in t:
pass pass
@ -666,7 +677,7 @@ def test_dynamic_min_iters():
# No smoothing # No smoothing
with closing(StringIO()) as our_file: with closing(StringIO()) as our_file:
t = tqdm(range(10), file=our_file, miniters=None, mininterval=None, t = tqdm(_range(10), file=our_file, miniters=None, mininterval=None,
smoothing=0) smoothing=0)
for _ in t: for _ in t:
pass pass
@ -674,7 +685,7 @@ def test_dynamic_min_iters():
# No dynamic_miniters (miniters is fixed manually) # No dynamic_miniters (miniters is fixed manually)
with closing(StringIO()) as our_file: with closing(StringIO()) as our_file:
t = tqdm(range(10), file=our_file, miniters=1, mininterval=None) t = tqdm(_range(10), file=our_file, miniters=1, mininterval=None)
for _ in t: for _ in t:
pass pass
assert not t.dynamic_miniters assert not t.dynamic_miniters
@ -683,12 +694,12 @@ def test_dynamic_min_iters():
def test_big_min_interval(): def test_big_min_interval():
"""Test large mininterval""" """Test large mininterval"""
with closing(StringIO()) as our_file: with closing(StringIO()) as our_file:
for _ in tqdm(range(2), file=our_file, mininterval=1E10): for _ in tqdm(_range(2), file=our_file, mininterval=1E10):
pass pass
assert '50%' not in our_file.getvalue() assert '50%' not in our_file.getvalue()
with closing(StringIO()) as our_file: with closing(StringIO()) as our_file:
with tqdm(range(2), file=our_file, mininterval=1E10) as t: with tqdm(_range(2), file=our_file, mininterval=1E10) as t:
t.update() t.update()
t.update() t.update()
assert '50%' not in our_file.getvalue() assert '50%' not in our_file.getvalue()
@ -707,10 +718,10 @@ def test_smoothed_dynamic_min_iters():
timer.sleep(1) timer.sleep(1)
t.update(10) t.update(10)
# The next iterations should be partially skipped # The next iterations should be partially skipped
for _ in range(2): for _ in _range(2):
timer.sleep(1) timer.sleep(1)
t.update(4) t.update(4)
for _ in range(20): for _ in _range(20):
timer.sleep(1) timer.sleep(1)
t.update() t.update()
@ -739,7 +750,7 @@ def test_smoothed_dynamic_min_iters_with_min_interval():
t.update(10) t.update(10)
timer.sleep(1e-2) timer.sleep(1e-2)
for _ in range(4): for _ in _range(4):
t.update() t.update()
timer.sleep(1e-2) timer.sleep(1e-2)
out = our_file.getvalue() out = our_file.getvalue()
@ -747,7 +758,7 @@ def test_smoothed_dynamic_min_iters_with_min_interval():
with closing(StringIO()) as our_file: with closing(StringIO()) as our_file:
# Test iteration-based tqdm # Test iteration-based tqdm
with tqdm(range(total), file=our_file, miniters=None, with tqdm(_range(total), file=our_file, miniters=None,
mininterval=0.01, smoothing=1, maxinterval=0) as t2: mininterval=0.01, smoothing=1, maxinterval=0) as t2:
cpu_timify(t2, timer) cpu_timify(t2, timer)
@ -806,7 +817,7 @@ def _rlock_creation_target():
def test_disable(): def test_disable():
"""Test disable""" """Test disable"""
with closing(StringIO()) as our_file: with closing(StringIO()) as our_file:
for _ in tqdm(range(3), file=our_file, disable=True): for _ in tqdm(_range(3), file=our_file, disable=True):
pass pass
assert our_file.getvalue() == '' assert our_file.getvalue() == ''
@ -820,7 +831,7 @@ def test_disable():
def test_infinite_total(): def test_infinite_total():
"""Test treatment of infinite total""" """Test treatment of infinite total"""
with closing(StringIO()) as our_file: with closing(StringIO()) as our_file:
for _ in tqdm(range(3), file=our_file, total=float("inf")): for _ in tqdm(_range(3), file=our_file, total=float("inf")):
pass pass
@ -841,7 +852,7 @@ def test_nototal():
def test_unit(): def test_unit():
"""Test SI unit prefix""" """Test SI unit prefix"""
with closing(StringIO()) as our_file: with closing(StringIO()) as our_file:
for _ in tqdm(range(3), file=our_file, miniters=1, unit="bytes"): for _ in tqdm(_range(3), file=our_file, miniters=1, unit="bytes"):
pass pass
assert 'bytes/s' in our_file.getvalue() assert 'bytes/s' in our_file.getvalue()
@ -855,7 +866,7 @@ def test_ascii():
# Test ascii bar # Test ascii bar
with closing(StringIO()) as our_file: with closing(StringIO()) as our_file:
for _ in tqdm(range(3), total=15, file=our_file, miniters=1, for _ in tqdm(_range(3), total=15, file=our_file, miniters=1,
mininterval=0, ascii=True): mininterval=0, ascii=True):
pass pass
res = our_file.getvalue().strip("\r").split("\r") res = our_file.getvalue().strip("\r").split("\r")
@ -866,7 +877,7 @@ def test_ascii():
# Test unicode bar # Test unicode bar
with closing(UnicodeIO()) as our_file: with closing(UnicodeIO()) as our_file:
with tqdm(total=15, file=our_file, ascii=False, mininterval=0) as t: with tqdm(total=15, file=our_file, ascii=False, mininterval=0) as t:
for _ in range(3): for _ in _range(3):
t.update() t.update()
res = our_file.getvalue().strip("\r").split("\r") res = our_file.getvalue().strip("\r").split("\r")
assert u"7%|\u258b" in res[1] assert u"7%|\u258b" in res[1]
@ -876,7 +887,7 @@ def test_ascii():
# Test custom bar # Test custom bar
for bars in [" .oO0", " #"]: for bars in [" .oO0", " #"]:
with closing(StringIO()) as our_file: with closing(StringIO()) as our_file:
for _ in tqdm(range(len(bars) - 1), file=our_file, miniters=1, for _ in tqdm(_range(len(bars) - 1), file=our_file, miniters=1,
mininterval=0, ascii=bars, ncols=27): mininterval=0, ascii=bars, ncols=27):
pass pass
res = our_file.getvalue().strip("\r").split("\r") res = our_file.getvalue().strip("\r").split("\r")
@ -938,7 +949,8 @@ def test_close():
res = our_file.getvalue() res = our_file.getvalue()
assert res[-1] == '\n' assert res[-1] == '\n'
if not res.startswith(exres): if not res.startswith(exres):
raise AssertionError(f"\n<<< Expected:\n{exres}, ...it/s]\n>>> Got:\n{res}\n===") raise AssertionError("\n<<< Expected:\n{0}\n>>> Got:\n{1}\n===".format(
exres + ', ...it/s]\n', our_file.getvalue()))
# Closing after the output stream has closed # Closing after the output stream has closed
with closing(StringIO()) as our_file: with closing(StringIO()) as our_file:
@ -964,7 +976,7 @@ def test_smoothing():
# -- Test disabling smoothing # -- Test disabling smoothing
with closing(StringIO()) as our_file: with closing(StringIO()) as our_file:
with tqdm(range(3), file=our_file, smoothing=None, leave=True) as t: with tqdm(_range(3), file=our_file, smoothing=None, leave=True) as t:
cpu_timify(t, timer) cpu_timify(t, timer)
for _ in t: for _ in t:
@ -975,11 +987,11 @@ def test_smoothing():
# 1st case: no smoothing (only use average) # 1st case: no smoothing (only use average)
with closing(StringIO()) as our_file2: with closing(StringIO()) as our_file2:
with closing(StringIO()) as our_file: with closing(StringIO()) as our_file:
t = tqdm(range(3), file=our_file2, smoothing=None, leave=True, t = tqdm(_range(3), file=our_file2, smoothing=None, leave=True,
miniters=1, mininterval=0) miniters=1, mininterval=0)
cpu_timify(t, timer) cpu_timify(t, timer)
with tqdm(range(3), file=our_file, smoothing=None, leave=True, with tqdm(_range(3), file=our_file, smoothing=None, leave=True,
miniters=1, mininterval=0) as t2: miniters=1, mininterval=0) as t2:
cpu_timify(t2, timer) cpu_timify(t2, timer)
@ -1005,11 +1017,11 @@ def test_smoothing():
# 2nd case: use max smoothing (= instant rate) # 2nd case: use max smoothing (= instant rate)
with closing(StringIO()) as our_file2: with closing(StringIO()) as our_file2:
with closing(StringIO()) as our_file: with closing(StringIO()) as our_file:
t = tqdm(range(3), file=our_file2, smoothing=1, leave=True, t = tqdm(_range(3), file=our_file2, smoothing=1, leave=True,
miniters=1, mininterval=0) miniters=1, mininterval=0)
cpu_timify(t, timer) cpu_timify(t, timer)
with tqdm(range(3), file=our_file, smoothing=1, leave=True, with tqdm(_range(3), file=our_file, smoothing=1, leave=True,
miniters=1, mininterval=0) as t2: miniters=1, mininterval=0) as t2:
cpu_timify(t2, timer) cpu_timify(t2, timer)
@ -1028,11 +1040,11 @@ def test_smoothing():
# 3rd case: use medium smoothing # 3rd case: use medium smoothing
with closing(StringIO()) as our_file2: with closing(StringIO()) as our_file2:
with closing(StringIO()) as our_file: with closing(StringIO()) as our_file:
t = tqdm(range(3), file=our_file2, smoothing=0.5, leave=True, t = tqdm(_range(3), file=our_file2, smoothing=0.5, leave=True,
miniters=1, mininterval=0) miniters=1, mininterval=0)
cpu_timify(t, timer) cpu_timify(t, timer)
t2 = tqdm(range(3), file=our_file, smoothing=0.5, leave=True, t2 = tqdm(_range(3), file=our_file, smoothing=0.5, leave=True,
miniters=1, mininterval=0) miniters=1, mininterval=0)
cpu_timify(t2, timer) cpu_timify(t2, timer)
@ -1086,7 +1098,7 @@ def test_bar_format():
with closing(StringIO()) as our_file: with closing(StringIO()) as our_file:
bar_format = r'hello world' bar_format = r'hello world'
with tqdm(ascii=False, bar_format=bar_format, file=our_file) as t: with tqdm(ascii=False, bar_format=bar_format, file=our_file) as t:
assert isinstance(t.bar_format, str) assert isinstance(t.bar_format, _unicode)
def test_custom_format(): def test_custom_format():
@ -1095,7 +1107,7 @@ def test_custom_format():
"""Provides a `total_time` format parameter""" """Provides a `total_time` format parameter"""
@property @property
def format_dict(self): def format_dict(self):
d = super().format_dict d = super(TqdmExtraFormat, self).format_dict
total_time = d["elapsed"] * (d["total"] or 0) / max(d["n"], 1) total_time = d["elapsed"] * (d["total"] or 0) / max(d["n"], 1)
d.update(total_time=self.format_interval(total_time) + " in total") d.update(total_time=self.format_interval(total_time) + " in total")
return d return d
@ -1115,7 +1127,7 @@ def test_eta(capsys):
bar_format='{l_bar}{eta:%Y-%m-%d}'): bar_format='{l_bar}{eta:%Y-%m-%d}'):
pass pass
_, err = capsys.readouterr() _, err = capsys.readouterr()
assert f"\r100%|{dt.now():%Y-%m-%d}\n" in err assert "\r100%|{eta:%Y-%m-%d}\n".format(eta=dt.now()) in err
def test_unpause(): def test_unpause():
@ -1245,7 +1257,7 @@ def test_position():
t1 = tqdm(desc='pos0 bar', position=0, **kwargs) t1 = tqdm(desc='pos0 bar', position=0, **kwargs)
t2 = tqdm(desc='pos1 bar', position=1, **kwargs) t2 = tqdm(desc='pos1 bar', position=1, **kwargs)
t3 = tqdm(desc='pos2 bar', position=2, **kwargs) t3 = tqdm(desc='pos2 bar', position=2, **kwargs)
for _ in range(2): for _ in _range(2):
t1.update() t1.update()
t3.update() t3.update()
t2.update() t2.update()
@ -1348,7 +1360,7 @@ def test_deprecated_gui():
# t.close() # t.close()
# len(tqdm._instances) += 1 # undo the close() decrement # len(tqdm._instances) += 1 # undo the close() decrement
t = tqdm(range(3), gui=True, file=our_file, miniters=1, mininterval=0) t = tqdm(_range(3), gui=True, file=our_file, miniters=1, mininterval=0)
try: try:
for _ in t: for _ in t:
pass pass
@ -1723,7 +1735,7 @@ def test_external_write():
def test_unit_scale(): def test_unit_scale():
"""Test numeric `unit_scale`""" """Test numeric `unit_scale`"""
with closing(StringIO()) as our_file: with closing(StringIO()) as our_file:
for _ in tqdm(range(9), unit_scale=9, file=our_file, for _ in tqdm(_range(9), unit_scale=9, file=our_file,
miniters=1, mininterval=0): miniters=1, mininterval=0):
pass pass
out = our_file.getvalue() out = our_file.getvalue()
@ -1925,7 +1937,7 @@ def test_screen_shape():
def test_initial(): def test_initial():
"""Test `initial`""" """Test `initial`"""
with closing(StringIO()) as our_file: with closing(StringIO()) as our_file:
for _ in tqdm(range(9), initial=10, total=19, file=our_file, for _ in tqdm(_range(9), initial=10, total=19, file=our_file,
miniters=1, mininterval=0): miniters=1, mininterval=0):
pass pass
out = our_file.getvalue() out = our_file.getvalue()
@ -1936,7 +1948,7 @@ def test_initial():
def test_colour(): def test_colour():
"""Test `colour`""" """Test `colour`"""
with closing(StringIO()) as our_file: with closing(StringIO()) as our_file:
for _ in tqdm(range(9), file=our_file, colour="#beefed"): for _ in tqdm(_range(9), file=our_file, colour="#beefed"):
pass pass
out = our_file.getvalue() out = our_file.getvalue()
assert '\x1b[38;2;%d;%d;%dm' % (0xbe, 0xef, 0xed) in out assert '\x1b[38;2;%d;%d;%dm' % (0xbe, 0xef, 0xed) in out
@ -1949,7 +1961,7 @@ def test_colour():
assert "Unknown colour" in str(w[-1].message) assert "Unknown colour" in str(w[-1].message)
with closing(StringIO()) as our_file2: with closing(StringIO()) as our_file2:
for _ in tqdm(range(9), file=our_file2, colour="blue"): for _ in tqdm(_range(9), file=our_file2, colour="blue"):
pass pass
out = our_file2.getvalue() out = our_file2.getvalue()
assert '\x1b[34m' in out assert '\x1b[34m' in out
@ -1965,7 +1977,7 @@ def test_closed():
def test_reversed(capsys): def test_reversed(capsys):
"""Test reversed()""" """Test reversed()"""
for _ in reversed(tqdm(range(9))): for _ in reversed(tqdm(_range(9))):
pass pass
out, err = capsys.readouterr() out, err = capsys.readouterr()
assert not out assert not out
@ -1977,7 +1989,7 @@ def test_contains(capsys):
"""Test __contains__ doesn't iterate""" """Test __contains__ doesn't iterate"""
with tqdm(list(range(9))) as t: with tqdm(list(range(9))) as t:
assert 9 not in t assert 9 not in t
assert all(i in t for i in range(9)) assert all(i in t for i in _range(9))
out, err = capsys.readouterr() out, err = capsys.readouterr()
assert not out assert not out
assert ' 0%' in err assert ' 0%' in err

View file

@ -1,51 +0,0 @@
from ast import literal_eval
from collections import defaultdict
from typing import Union # py<3.10
from tqdm.utils import envwrap
def test_envwrap(monkeypatch):
"""Test @envwrap (basic)"""
monkeypatch.setenv('FUNC_A', "42")
monkeypatch.setenv('FUNC_TyPe_HiNt', "1337")
monkeypatch.setenv('FUNC_Unused', "x")
@envwrap("FUNC_")
def func(a=1, b=2, type_hint: int = None):
return a, b, type_hint
assert (42, 2, 1337) == func()
assert (99, 2, 1337) == func(a=99)
def test_envwrap_types(monkeypatch):
"""Test @envwrap(types)"""
monkeypatch.setenv('FUNC_notype', "3.14159")
@envwrap("FUNC_", types=defaultdict(lambda: literal_eval))
def func(notype=None):
return notype
assert 3.14159 == func()
monkeypatch.setenv('FUNC_number', "1")
monkeypatch.setenv('FUNC_string', "1")
@envwrap("FUNC_", types={'number': int})
def nofallback(number=None, string=None):
return number, string
assert 1, "1" == nofallback()
def test_envwrap_annotations(monkeypatch):
"""Test @envwrap with typehints"""
monkeypatch.setenv('FUNC_number', "1.1")
monkeypatch.setenv('FUNC_string', "1.1")
@envwrap("FUNC_")
def annotated(number: Union[int, float] = None, string: int = None):
return number, string
assert 1.1, "1.1" == annotated()

48
tox.ini
View file

@ -4,17 +4,20 @@
# and then run "tox" from this directory. # and then run "tox" from this directory.
[tox] [tox]
envlist=py{37,38,39,310,311,312,py3}{,-tf}{,-keras}, perf, check # deprecation warning: py{27,py2,34,35,36}
envlist=py{27,34,35,36,37,38,39,310,py2,py3}{,-tf}{,-keras}, perf, setup.py
isolated_build=True isolated_build=True
[gh-actions] [gh-actions]
python= python=
2.7: py27
3.5: py35
3.6: py36
3.7: py37 3.7: py37
3.8: py38 3.8: py38
3.9: py39 3.9: py39
3.10: py310 3.10: py310
3.11: py311 pypy-2.7: pypy2
3.12: py312
pypy-3.7: pypy3 pypy-3.7: pypy3
[gh-actions:env] [gh-actions:env]
PLATFORM= PLATFORM=
@ -23,11 +26,12 @@ PLATFORM=
[core] [core]
deps= deps=
pytest pytest
py3{4,5,6}: pytest<7
pytest-cov pytest-cov
pytest-timeout pytest-timeout
pytest-asyncio py3{7,8,9,10}: pytest-asyncio
ipywidgets py3{6,7,8,9,10}: ipywidgets
git+https://github.com/casperdcl/nbval.git@master#egg=nbval py3{7,8,9,10}: git+https://github.com/casperdcl/nbval.git@master#egg=nbval
coverage coverage
coveralls coveralls
codecov codecov
@ -37,7 +41,7 @@ commands=
- codacy report -l Python -r coverage.xml --partial - codacy report -l Python -r coverage.xml --partial
[testenv] [testenv]
passenv=TOXENV,CI,GITHUB_*,CODECOV_*,COVERALLS_*,CODACY_*,HOME passenv=TOXENV CI GITHUB_* CODECOV_* COVERALLS_* CODACY_* HOME
deps= deps=
{[core]deps} {[core]deps}
cython cython
@ -45,17 +49,26 @@ deps=
matplotlib matplotlib
numpy numpy
pandas pandas
rich
tf: tensorflow!=2.5.0 tf: tensorflow!=2.5.0
keras: keras !py27-keras: keras
py27-keras: keras<2.5
py35-keras: keras<2.7
py27-tf: protobuf<3.18
py3{6,7,8,9,10}: rich
commands= commands=
pytest --cov=tqdm --cov-report= -W=ignore tests_notebook.ipynb --nbval --nbval-current-env --nbval-sanitize-with=.meta/nbval.ini py3{4,5,6}: pytest --cov=tqdm --cov-report=xml --cov-report=term -k "not perf" -o addopts= -v --tb=short -rxs -W=error --durations=0 --durations-min=0.1
pytest --cov=tqdm --cov-report=xml --cov-report=term --cov-append -k "not perf" py3{7,8,9,10}: pytest --cov=tqdm --cov-report= tests_notebook.ipynb --nbval --nbval-current-env -W=ignore --nbval-sanitize-with=setup.cfg
py3{7,8,9,10}: pytest --cov=tqdm --cov-report=xml --cov-report=term --cov-append -k "not perf"
{[core]commands} {[core]commands}
allowlist_externals=codacy allowlist_externals=codacy
[testenv:py{27,py2}{,-tf}{,-keras}]
commands=
pytest --cov=tqdm --cov-report=xml --cov-report=term -k "not perf" -o addopts= -v --tb=short -rxs -W=error --durations=10
{[core]commands}
# no cython/numpy/pandas # no cython/numpy/pandas
[testenv:pypy3] [testenv:py{34,py2,py3}]
deps={[core]deps} deps={[core]deps}
[testenv:perf] [testenv:perf]
@ -65,12 +78,11 @@ deps=
pytest-asyncio pytest-asyncio
commands=pytest -k perf commands=pytest -k perf
[testenv:check] [testenv:setup.py]
deps= deps=
build docutils
twine pygments
py-make>=0.1.0 py-make>=0.1.0
commands= commands=
{envpython} -m build {envpython} setup.py check --restructuredtext --metadata --strict
{envpython} -m twine check dist/* {envpython} setup.py make none
{envpython} -m pymake -h

1581
tqdm.egg-info/PKG-INFO Normal file

File diff suppressed because it is too large Load diff

88
tqdm.egg-info/SOURCES.txt Normal file
View file

@ -0,0 +1,88 @@
.pre-commit-config.yaml
.zenodo.json
CODE_OF_CONDUCT.md
CONTRIBUTING.md
DEMO.ipynb
LICENCE
Makefile
README.rst
environment.yml
logo.png
pyproject.toml
setup.cfg
setup.py
tests_notebook.ipynb
tox.ini
examples/7zx.py
examples/async_coroutines.py
examples/coroutine_pipe.py
examples/include_no_requirements.py
examples/pandas_progress_apply.py
examples/paper.bib
examples/paper.md
examples/parallel_bars.py
examples/redirect_print.py
examples/simple_examples.py
examples/tqdm_requests.py
examples/tqdm_wget.py
examples/wrapping_generators.py
tests/__init__.py
tests/conftest.py
tests/py37_asyncio.py
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
tests/tests_keras.py
tests/tests_main.py
tests/tests_notebook.py
tests/tests_pandas.py
tests/tests_perf.py
tests/tests_rich.py
tests/tests_synchronisation.py
tests/tests_tk.py
tests/tests_tqdm.py
tests/tests_version.py
tqdm/__init__.py
tqdm/__main__.py
tqdm/_dist_ver.py
tqdm/_main.py
tqdm/_monitor.py
tqdm/_tqdm.py
tqdm/_tqdm_gui.py
tqdm/_tqdm_notebook.py
tqdm/_tqdm_pandas.py
tqdm/_utils.py
tqdm/asyncio.py
tqdm/auto.py
tqdm/autonotebook.py
tqdm/cli.py
tqdm/completion.sh
tqdm/dask.py
tqdm/gui.py
tqdm/keras.py
tqdm/notebook.py
tqdm/rich.py
tqdm/std.py
tqdm/tk.py
tqdm/tqdm.1
tqdm/utils.py
tqdm/version.py
tqdm.egg-info/PKG-INFO
tqdm.egg-info/SOURCES.txt
tqdm.egg-info/dependency_links.txt
tqdm.egg-info/entry_points.txt
tqdm.egg-info/requires.txt
tqdm.egg-info/top_level.txt
tqdm/contrib/__init__.py
tqdm/contrib/bells.py
tqdm/contrib/concurrent.py
tqdm/contrib/discord.py
tqdm/contrib/itertools.py
tqdm/contrib/logging.py
tqdm/contrib/slack.py
tqdm/contrib/telegram.py
tqdm/contrib/utils_worker.py

View file

@ -0,0 +1 @@

View file

@ -0,0 +1,2 @@
[console_scripts]
tqdm = tqdm.cli:main

View file

@ -0,0 +1,20 @@
[:platform_system == "Windows"]
colorama
[:python_version < "3.7"]
importlib_resources
[dev]
py-make>=0.1.0
twine
wheel
[notebook]
ipywidgets>=6
[slack]
slack-sdk
[telegram]
requests

View file

@ -0,0 +1 @@
tqdm

View file

@ -29,7 +29,10 @@ def tqdm_notebook(*args, **kwargs): # pragma: no cover
def tnrange(*args, **kwargs): # pragma: no cover def tnrange(*args, **kwargs): # pragma: no cover
"""Shortcut for `tqdm.notebook.tqdm(range(*args), **kwargs)`.""" """
A shortcut for `tqdm.notebook.tqdm(xrange(*args), **kwargs)`.
On Python3+, `range` is used instead of `xrange`.
"""
from warnings import warn from warnings import warn
from .notebook import trange as _tnrange from .notebook import trange as _tnrange

1
tqdm/_dist_ver.py Normal file
View file

@ -0,0 +1 @@
__version__ = '4.64.1'

View file

@ -2,9 +2,10 @@ from warnings import warn
from .std import TqdmDeprecationWarning from .std import TqdmDeprecationWarning
from .utils import ( # NOQA, pylint: disable=unused-import from .utils import ( # NOQA, pylint: disable=unused-import
CUR_OS, IS_NIX, IS_WIN, RE_ANSI, Comparable, FormatReplace, SimpleTextIOWrapper, CUR_OS, IS_NIX, IS_WIN, RE_ANSI, Comparable, FormatReplace, SimpleTextIOWrapper, _basestring,
_environ_cols_wrapper, _is_ascii, _is_utf, _screen_shape_linux, _screen_shape_tput, _environ_cols_wrapper, _is_ascii, _is_utf, _range, _screen_shape_linux, _screen_shape_tput,
_screen_shape_windows, _screen_shape_wrapper, _supports_unicode, _term_move_up, colorama) _screen_shape_windows, _screen_shape_wrapper, _supports_unicode, _term_move_up, _unich,
_unicode, colorama)
warn("This function will be removed in tqdm==5.0.0\n" warn("This function will be removed in tqdm==5.0.0\n"
"Please use `tqdm.utils.*` instead of `tqdm._utils.*`", "Please use `tqdm.utils.*` instead of `tqdm._utils.*`",

View file

@ -18,10 +18,10 @@ __all__ = ['tqdm_asyncio', 'tarange', 'tqdm', 'trange']
class tqdm_asyncio(std_tqdm): class tqdm_asyncio(std_tqdm):
""" """
Asynchronous-friendly version of tqdm. Asynchronous-friendly version of tqdm (Python 3.6+).
""" """
def __init__(self, iterable=None, *args, **kwargs): def __init__(self, iterable=None, *args, **kwargs):
super().__init__(iterable, *args, **kwargs) super(tqdm_asyncio, self).__init__(iterable, *args, **kwargs)
self.iterable_awaitable = False self.iterable_awaitable = False
if iterable is not None: if iterable is not None:
if hasattr(iterable, "__anext__"): if hasattr(iterable, "__anext__"):

View file

@ -4,7 +4,7 @@ Enables multiple commonly used features.
Method resolution order: Method resolution order:
- `tqdm.autonotebook` without import warnings - `tqdm.autonotebook` without import warnings
- `tqdm.asyncio` - `tqdm.asyncio` on Python3.6+
- `tqdm.std` base class - `tqdm.std` base class
Usage: Usage:
@ -12,6 +12,7 @@ Usage:
>>> for i in trange(10): >>> for i in trange(10):
... ... ... ...
""" """
import sys
import warnings import warnings
from .std import TqdmExperimentalWarning from .std import TqdmExperimentalWarning
@ -19,7 +20,12 @@ from .std import TqdmExperimentalWarning
with warnings.catch_warnings(): with warnings.catch_warnings():
warnings.simplefilter("ignore", category=TqdmExperimentalWarning) warnings.simplefilter("ignore", category=TqdmExperimentalWarning)
from .autonotebook import tqdm as notebook_tqdm from .autonotebook import tqdm as notebook_tqdm
from .autonotebook import trange as notebook_trange
if sys.version_info[:2] < (3, 6):
tqdm = notebook_tqdm
trange = notebook_trange
else: # Python3.6+
from .asyncio import tqdm as asyncio_tqdm from .asyncio import tqdm as asyncio_tqdm
from .std import tqdm as std_tqdm from .std import tqdm as std_tqdm
@ -29,12 +35,10 @@ if notebook_tqdm != std_tqdm:
else: else:
tqdm = asyncio_tqdm tqdm = asyncio_tqdm
def trange(*args, **kwargs): def trange(*args, **kwargs):
""" """
A shortcut for `tqdm.auto.tqdm(range(*args), **kwargs)`. A shortcut for `tqdm.auto.tqdm(range(*args), **kwargs)`.
""" """
return tqdm(range(*args), **kwargs) return tqdm(range(*args), **kwargs)
__all__ = ["tqdm", "trange"] __all__ = ["tqdm", "trange"]

View file

@ -5,7 +5,6 @@ import logging
import re import re
import sys import sys
from ast import literal_eval as numeric from ast import literal_eval as numeric
from textwrap import indent
from .std import TqdmKeyError, TqdmTypeError, tqdm from .std import TqdmKeyError, TqdmTypeError, tqdm
from .version import __version__ from .version import __version__
@ -22,34 +21,23 @@ def cast(val, typ):
return cast(val, t) return cast(val, t)
except TqdmTypeError: except TqdmTypeError:
pass pass
raise TqdmTypeError(f"{val} : {typ}") raise TqdmTypeError(val + ' : ' + typ)
# sys.stderr.write('\ndebug | `val:type`: `' + val + ':' + typ + '`.\n') # sys.stderr.write('\ndebug | `val:type`: `' + val + ':' + typ + '`.\n')
if typ == 'bool': if typ == 'bool':
if (val == 'True') or (val == ''): if (val == 'True') or (val == ''):
return True return True
if val == 'False': elif val == 'False':
return False return False
else:
raise TqdmTypeError(val + ' : ' + typ) raise TqdmTypeError(val + ' : ' + typ)
try:
return eval(typ + '("' + val + '")')
except Exception:
if typ == 'chr': if typ == 'chr':
if len(val) == 1: return chr(ord(eval('"' + val + '"'))).encode()
return val.encode() else:
if re.match(r"^\\\w+$", val): raise TqdmTypeError(val + ' : ' + typ)
return eval(f'"{val}"').encode()
raise TqdmTypeError(f"{val} : {typ}")
if typ == 'str':
return val
if typ == 'int':
try:
return int(val)
except ValueError as exc:
raise TqdmTypeError(f"{val} : {typ}") from exc
if typ == 'float':
try:
return float(val)
except ValueError as exc:
raise TqdmTypeError(f"{val} : {typ}") from exc
raise TqdmTypeError(f"{val} : {typ}")
def posix_pipe(fin, fout, delim=b'\\n', buf_size=256, def posix_pipe(fin, fout, delim=b'\\n', buf_size=256,
@ -110,7 +98,7 @@ def posix_pipe(fin, fout, delim=b'\\n', buf_size=256,
# ((opt, type), ... ) # ((opt, type), ... )
RE_OPTS = re.compile(r'\n {4}(\S+)\s{2,}:\s*([^,]+)') RE_OPTS = re.compile(r'\n {8}(\S+)\s{2,}:\s*([^,]+)')
# better split method assuming no positional args # better split method assuming no positional args
RE_SHLEX = re.compile(r'\s*(?<!\S)--?([^\s=]+)(\s+|=|$)') RE_SHLEX = re.compile(r'\s*(?<!\S)--?([^\s=]+)(\s+|=|$)')
@ -178,9 +166,7 @@ def main(fp=sys.stderr, argv=None):
logging.basicConfig(level=getattr(logging, logLevel), logging.basicConfig(level=getattr(logging, logLevel),
format="%(levelname)s:%(module)s:%(lineno)d:%(message)s") format="%(levelname)s:%(module)s:%(lineno)d:%(message)s")
# py<3.13 doesn't dedent docstrings d = tqdm.__init__.__doc__ + CLI_EXTRA_DOC
d = (tqdm.__doc__ if sys.version_info < (3, 13)
else indent(tqdm.__doc__, " ")) + CLI_EXTRA_DOC
opt_types = dict(RE_OPTS.findall(d)) opt_types = dict(RE_OPTS.findall(d))
# opt_types['delim'] = 'chr' # opt_types['delim'] = 'chr'
@ -213,7 +199,8 @@ Options:
sys.stdout.write(d + '\n') sys.stdout.write(d + '\n')
sys.exit(0) sys.exit(0)
elif argv and argv[0][:2] != '--': elif argv and argv[0][:2] != '--':
sys.stderr.write(f"Error:Unknown argument:{argv[0]}\n{help_short}") sys.stderr.write(
"Error:Unknown argument:{0}\n{1}".format(argv[0], help_short))
argv = RE_SHLEX.split(' '.join(["tqdm"] + argv)) argv = RE_SHLEX.split(' '.join(["tqdm"] + argv))
opts = dict(zip(argv[1::3], argv[3::3])) opts = dict(zip(argv[1::3], argv[3::3]))
@ -258,21 +245,25 @@ Options:
stdout = getattr(stdout, 'buffer', stdout) stdout = getattr(stdout, 'buffer', stdout)
stdin = getattr(sys.stdin, 'buffer', sys.stdin) stdin = getattr(sys.stdin, 'buffer', sys.stdin)
if manpath or comppath: if manpath or comppath:
try: # py<3.9 from os import path
from shutil import copyfile
try: # py<3.7
import importlib_resources as resources import importlib_resources as resources
except ImportError: except ImportError:
from importlib import resources from importlib import resources
from pathlib import Path
def cp(name, dst): def cp(name, dst):
"""copy resource `name` to `dst`""" """copy resource `name` to `dst`"""
fi = resources.files('tqdm') / name if hasattr(resources, 'files'):
dst.write_bytes(fi.read_bytes()) copyfile(str(resources.files('tqdm') / name), dst)
else: # py<3.9
with resources.path('tqdm', name) as src:
copyfile(str(src), dst)
log.info("written:%s", dst) log.info("written:%s", dst)
if manpath is not None: if manpath is not None:
cp('tqdm.1', Path(manpath) / 'tqdm.1') cp('tqdm.1', path.join(manpath, 'tqdm.1'))
if comppath is not None: if comppath is not None:
cp('completion.sh', Path(comppath) / 'tqdm_completion.sh') cp('completion.sh', path.join(comppath, 'tqdm_completion.sh'))
sys.exit(0) sys.exit(0)
if tee: if tee:
stdout_write = stdout.write stdout_write = stdout.write

View file

@ -3,10 +3,11 @@ Thin wrappers around common functions.
Subpackages contain potentially unstable extensions. Subpackages contain potentially unstable extensions.
""" """
from warnings import warn import sys
from functools import wraps
from ..auto import tqdm as tqdm_auto from ..auto import tqdm as tqdm_auto
from ..std import TqdmDeprecationWarning, tqdm from ..std import tqdm
from ..utils import ObjectWrapper from ..utils import ObjectWrapper
__author__ = {"github.com/": ["casperdcl"]} __author__ = {"github.com/": ["casperdcl"]}
@ -17,7 +18,7 @@ class DummyTqdmFile(ObjectWrapper):
"""Dummy file-like that will write to tqdm""" """Dummy file-like that will write to tqdm"""
def __init__(self, wrapped): def __init__(self, wrapped):
super().__init__(wrapped) super(DummyTqdmFile, self).__init__(wrapped)
self._buf = [] self._buf = []
def write(self, x, nolock=False): def write(self, x, nolock=False):
@ -41,9 +42,12 @@ class DummyTqdmFile(ObjectWrapper):
def builtin_iterable(func): def builtin_iterable(func):
"""Returns `func`""" """Wraps `func()` output in a `list()` in py2"""
warn("This function has no effect, and will be removed in tqdm==5.0.0", if sys.version_info[:1] < (3,):
TqdmDeprecationWarning, stacklevel=2) @wraps(func)
def inner(*args, **kwargs):
return list(func(*args, **kwargs))
return inner
return func return func
@ -66,6 +70,7 @@ def tenumerate(iterable, start=0, total=None, tqdm_class=tqdm_auto, **tqdm_kwarg
return enumerate(tqdm_class(iterable, total=total, **tqdm_kwargs), start) return enumerate(tqdm_class(iterable, total=total, **tqdm_kwargs), start)
@builtin_iterable
def tzip(iter1, *iter2plus, **tqdm_kwargs): def tzip(iter1, *iter2plus, **tqdm_kwargs):
""" """
Equivalent of builtin `zip`. Equivalent of builtin `zip`.
@ -80,6 +85,7 @@ def tzip(iter1, *iter2plus, **tqdm_kwargs):
yield i yield i
@builtin_iterable
def tmap(function, *sequences, **tqdm_kwargs): def tmap(function, *sequences, **tqdm_kwargs):
""" """
Equivalent of builtin `map`. Equivalent of builtin `map`.

View file

@ -1,13 +1,32 @@
""" """
Thin wrappers around `concurrent.futures`. Thin wrappers around `concurrent.futures`.
""" """
from __future__ import absolute_import
from contextlib import contextmanager from contextlib import contextmanager
from operator import length_hint
from os import cpu_count
from ..auto import tqdm as tqdm_auto from ..auto import tqdm as tqdm_auto
from ..std import TqdmWarning from ..std import TqdmWarning
try:
from operator import length_hint
except ImportError:
def length_hint(it, default=0):
"""Returns `len(it)`, falling back to `default`"""
try:
return len(it)
except TypeError:
return default
try:
from os import cpu_count
except ImportError:
try:
from multiprocessing import cpu_count
except ImportError:
def cpu_count():
return 4
import sys
__author__ = {"github.com/": ["casperdcl"]} __author__ = {"github.com/": ["casperdcl"]}
__all__ = ['thread_map', 'process_map'] __all__ = ['thread_map', 'process_map']
@ -45,10 +64,16 @@ def _executor_map(PoolExecutor, fn, *iterables, **tqdm_kwargs):
chunksize = kwargs.pop("chunksize", 1) chunksize = kwargs.pop("chunksize", 1)
lock_name = kwargs.pop("lock_name", "") lock_name = kwargs.pop("lock_name", "")
with ensure_lock(tqdm_class, lock_name=lock_name) as lk: with ensure_lock(tqdm_class, lock_name=lock_name) as lk:
pool_kwargs = {'max_workers': max_workers}
sys_version = sys.version_info[:2]
if sys_version >= (3, 7):
# share lock in case workers are already using `tqdm` # share lock in case workers are already using `tqdm`
with PoolExecutor(max_workers=max_workers, initializer=tqdm_class.set_lock, pool_kwargs.update(initializer=tqdm_class.set_lock, initargs=(lk,))
initargs=(lk,)) as ex: map_args = {}
return list(tqdm_class(ex.map(fn, *iterables, chunksize=chunksize), **kwargs)) if not (3, 0) < sys_version < (3, 5):
map_args.update(chunksize=chunksize)
with PoolExecutor(**pool_kwargs) as ex:
return list(tqdm_class(ex.map(fn, *iterables, **map_args), **kwargs))
def thread_map(fn, *iterables, **tqdm_kwargs): def thread_map(fn, *iterables, **tqdm_kwargs):

View file

@ -6,85 +6,54 @@ Usage:
>>> for i in trange(10, token='{token}', channel_id='{channel_id}'): >>> for i in trange(10, token='{token}', channel_id='{channel_id}'):
... ... ... ...
![screenshot](https://tqdm.github.io/img/screenshot-discord.png) ![screenshot](https://img.tqdm.ml/screenshot-discord.png)
""" """
from os import getenv from __future__ import absolute_import
from warnings import warn
from requests import Session import logging
from requests.utils import default_user_agent from os import getenv
try:
from disco.client import Client, ClientConfig
except ImportError:
raise ImportError("Please `pip install disco-py`")
from ..auto import tqdm as tqdm_auto from ..auto import tqdm as tqdm_auto
from ..std import TqdmWarning from ..utils import _range
from ..version import __version__
from .utils_worker import MonoWorker from .utils_worker import MonoWorker
__author__ = {"github.com/": ["casperdcl", "guigoruiz1"]} __author__ = {"github.com/": ["casperdcl"]}
__all__ = ['DiscordIO', 'tqdm_discord', 'tdrange', 'tqdm', 'trange'] __all__ = ['DiscordIO', 'tqdm_discord', 'tdrange', 'tqdm', 'trange']
class DiscordIO(MonoWorker): class DiscordIO(MonoWorker):
"""Non-blocking file-like IO using a Discord Bot.""" """Non-blocking file-like IO using a Discord Bot."""
API = "https://discord.com/api/v10"
UA = f"tqdm (https://tqdm.github.io, {__version__}) {default_user_agent()}"
def __init__(self, token, channel_id): def __init__(self, token, channel_id):
"""Creates a new message in the given `channel_id`.""" """Creates a new message in the given `channel_id`."""
super().__init__() super(DiscordIO, self).__init__()
self.token = token config = ClientConfig()
self.channel_id = channel_id config.token = token
self.session = Session() client = Client(config)
self.text = self.__class__.__name__ self.text = self.__class__.__name__
self.message_id
@property
def message_id(self):
if hasattr(self, '_message_id'):
return self._message_id
try: try:
res = self.session.post( self.message = client.api.channels_messages_create(channel_id, self.text)
f'{self.API}/channels/{self.channel_id}/messages',
headers={'Authorization': f'Bot {self.token}', 'User-Agent': self.UA},
json={'content': f"`{self.text}`"}).json()
except Exception as e: except Exception as e:
tqdm_auto.write(str(e)) tqdm_auto.write(str(e))
else: self.message = None
if res.get('error_code') == 429:
warn("Creation rate limit: try increasing `mininterval`.",
TqdmWarning, stacklevel=2)
else:
self._message_id = res['id']
return self._message_id
def write(self, s): def write(self, s):
"""Replaces internal `message_id`'s text with `s`.""" """Replaces internal `message`'s text with `s`."""
if not s: if not s:
s = "..." s = "..."
s = s.replace('\r', '').strip() s = s.replace('\r', '').strip()
if s == self.text: if s == self.text:
return # avoid duplicate message Bot error return # skip duplicate message
message_id = self.message_id message = self.message
if message_id is None: if message is None:
return return
self.text = s self.text = s
try: try:
future = self.submit( future = self.submit(message.edit, '`' + s + '`')
self.session.patch,
f'{self.API}/channels/{self.channel_id}/messages/{message_id}',
headers={'Authorization': f'Bot {self.token}', 'User-Agent': self.UA},
json={'content': f"`{self.text}`"})
except Exception as e:
tqdm_auto.write(str(e))
else:
return future
def delete(self):
"""Deletes internal `message_id`."""
try:
future = self.submit(
self.session.delete,
f'{self.API}/channels/{self.channel_id}/messages/{self.message_id}',
headers={'Authorization': f'Bot {self.token}', 'User-Agent': self.UA})
except Exception as e: except Exception as e:
tqdm_auto.write(str(e)) tqdm_auto.write(str(e))
else: else:
@ -109,22 +78,26 @@ class tqdm_discord(tqdm_auto):
""" """
Parameters Parameters
---------- ----------
token : str, required. Discord bot token token : str, required. Discord token
[default: ${TQDM_DISCORD_TOKEN}]. [default: ${TQDM_DISCORD_TOKEN}].
channel_id : int, required. Discord channel ID channel_id : int, required. Discord channel ID
[default: ${TQDM_DISCORD_CHANNEL_ID}]. [default: ${TQDM_DISCORD_CHANNEL_ID}].
mininterval : float, optional.
Minimum of [default: 1.5] to avoid rate limit.
See `tqdm.auto.tqdm.__init__` for other parameters. See `tqdm.auto.tqdm.__init__` for other parameters.
""" """
if not kwargs.get('disable'): if not kwargs.get('disable'):
kwargs = kwargs.copy() kwargs = kwargs.copy()
logging.getLogger("HTTPClient").setLevel(logging.WARNING)
self.dio = DiscordIO( self.dio = DiscordIO(
kwargs.pop('token', getenv('TQDM_DISCORD_TOKEN')), kwargs.pop('token', getenv("TQDM_DISCORD_TOKEN")),
kwargs.pop('channel_id', getenv('TQDM_DISCORD_CHANNEL_ID'))) kwargs.pop('channel_id', getenv("TQDM_DISCORD_CHANNEL_ID")))
super().__init__(*args, **kwargs) kwargs['mininterval'] = max(1.5, kwargs.get('mininterval', 1.5))
super(tqdm_discord, self).__init__(*args, **kwargs)
def display(self, **kwargs): def display(self, **kwargs):
super().display(**kwargs) super(tqdm_discord, self).display(**kwargs)
fmt = self.format_dict fmt = self.format_dict
if fmt.get('bar_format', None): if fmt.get('bar_format', None):
fmt['bar_format'] = fmt['bar_format'].replace( fmt['bar_format'] = fmt['bar_format'].replace(
@ -134,21 +107,17 @@ class tqdm_discord(tqdm_auto):
self.dio.write(self.format_meter(**fmt)) self.dio.write(self.format_meter(**fmt))
def clear(self, *args, **kwargs): def clear(self, *args, **kwargs):
super().clear(*args, **kwargs) super(tqdm_discord, self).clear(*args, **kwargs)
if not self.disable: if not self.disable:
self.dio.write("") self.dio.write("")
def close(self):
if self.disable:
return
super().close()
if not (self.leave or (self.leave is None and self.pos == 0)):
self.dio.delete()
def tdrange(*args, **kwargs): def tdrange(*args, **kwargs):
"""Shortcut for `tqdm.contrib.discord.tqdm(range(*args), **kwargs)`.""" """
return tqdm_discord(range(*args), **kwargs) A shortcut for `tqdm.contrib.discord.tqdm(xrange(*args), **kwargs)`.
On Python3+, `range` is used instead of `xrange`.
"""
return tqdm_discord(_range(*args), **kwargs)
# Aliases # Aliases

View file

@ -1,6 +1,8 @@
""" """
Thin wrappers around `itertools`. Thin wrappers around `itertools`.
""" """
from __future__ import absolute_import
import itertools import itertools
from ..auto import tqdm as tqdm_auto from ..auto import tqdm as tqdm_auto

View file

@ -1,12 +1,14 @@
""" """
Helper functionality for interoperability with stdlib `logging`. Helper functionality for interoperability with stdlib `logging`.
""" """
from __future__ import absolute_import
import logging import logging
import sys import sys
from contextlib import contextmanager from contextlib import contextmanager
try: try:
from typing import Iterator, List, Optional, Type # noqa: F401 from typing import Iterator, List, Optional, Type # pylint: disable=unused-import
except ImportError: except ImportError:
pass pass
@ -18,7 +20,7 @@ class _TqdmLoggingHandler(logging.StreamHandler):
self, self,
tqdm_class=std_tqdm # type: Type[std_tqdm] tqdm_class=std_tqdm # type: Type[std_tqdm]
): ):
super().__init__() super(_TqdmLoggingHandler, self).__init__()
self.tqdm_class = tqdm_class self.tqdm_class = tqdm_class
def emit(self, record): def emit(self, record):

View file

@ -6,8 +6,10 @@ Usage:
>>> for i in trange(10, token='{token}', channel='{channel}'): >>> for i in trange(10, token='{token}', channel='{channel}'):
... ... ... ...
![screenshot](https://tqdm.github.io/img/screenshot-slack.png) ![screenshot](https://img.tqdm.ml/screenshot-slack.png)
""" """
from __future__ import absolute_import
import logging import logging
from os import getenv from os import getenv
@ -17,6 +19,7 @@ except ImportError:
raise ImportError("Please `pip install slack-sdk`") raise ImportError("Please `pip install slack-sdk`")
from ..auto import tqdm as tqdm_auto from ..auto import tqdm as tqdm_auto
from ..utils import _range
from .utils_worker import MonoWorker from .utils_worker import MonoWorker
__author__ = {"github.com/": ["0x2b3bfa0", "casperdcl"]} __author__ = {"github.com/": ["0x2b3bfa0", "casperdcl"]}
@ -27,7 +30,7 @@ class SlackIO(MonoWorker):
"""Non-blocking file-like IO using a Slack app.""" """Non-blocking file-like IO using a Slack app."""
def __init__(self, token, channel): def __init__(self, token, channel):
"""Creates a new message in the given `channel`.""" """Creates a new message in the given `channel`."""
super().__init__() super(SlackIO, self).__init__()
self.client = WebClient(token=token) self.client = WebClient(token=token)
self.text = self.__class__.__name__ self.text = self.__class__.__name__
try: try:
@ -88,10 +91,10 @@ class tqdm_slack(tqdm_auto):
kwargs.pop('token', getenv("TQDM_SLACK_TOKEN")), kwargs.pop('token', getenv("TQDM_SLACK_TOKEN")),
kwargs.pop('channel', getenv("TQDM_SLACK_CHANNEL"))) kwargs.pop('channel', getenv("TQDM_SLACK_CHANNEL")))
kwargs['mininterval'] = max(1.5, kwargs.get('mininterval', 1.5)) kwargs['mininterval'] = max(1.5, kwargs.get('mininterval', 1.5))
super().__init__(*args, **kwargs) super(tqdm_slack, self).__init__(*args, **kwargs)
def display(self, **kwargs): def display(self, **kwargs):
super().display(**kwargs) super(tqdm_slack, self).display(**kwargs)
fmt = self.format_dict fmt = self.format_dict
if fmt.get('bar_format', None): if fmt.get('bar_format', None):
fmt['bar_format'] = fmt['bar_format'].replace( fmt['bar_format'] = fmt['bar_format'].replace(
@ -105,14 +108,17 @@ class tqdm_slack(tqdm_auto):
self.sio.write(self.format_meter(**fmt)) self.sio.write(self.format_meter(**fmt))
def clear(self, *args, **kwargs): def clear(self, *args, **kwargs):
super().clear(*args, **kwargs) super(tqdm_slack, self).clear(*args, **kwargs)
if not self.disable: if not self.disable:
self.sio.write("") self.sio.write("")
def tsrange(*args, **kwargs): def tsrange(*args, **kwargs):
"""Shortcut for `tqdm.contrib.slack.tqdm(range(*args), **kwargs)`.""" """
return tqdm_slack(range(*args), **kwargs) A shortcut for `tqdm.contrib.slack.tqdm(xrange(*args), **kwargs)`.
On Python3+, `range` is used instead of `xrange`.
"""
return tqdm_slack(_range(*args), **kwargs)
# Aliases # Aliases

View file

@ -6,8 +6,10 @@ Usage:
>>> for i in trange(10, token='{token}', chat_id='{chat_id}'): >>> for i in trange(10, token='{token}', chat_id='{chat_id}'):
... ... ... ...
![screenshot](https://tqdm.github.io/img/screenshot-telegram.gif) ![screenshot](https://img.tqdm.ml/screenshot-telegram.gif)
""" """
from __future__ import absolute_import
from os import getenv from os import getenv
from warnings import warn from warnings import warn
@ -15,6 +17,7 @@ from requests import Session
from ..auto import tqdm as tqdm_auto from ..auto import tqdm as tqdm_auto
from ..std import TqdmWarning from ..std import TqdmWarning
from ..utils import _range
from .utils_worker import MonoWorker from .utils_worker import MonoWorker
__author__ = {"github.com/": ["casperdcl"]} __author__ = {"github.com/": ["casperdcl"]}
@ -27,7 +30,7 @@ class TelegramIO(MonoWorker):
def __init__(self, token, chat_id): def __init__(self, token, chat_id):
"""Creates a new message in the given `chat_id`.""" """Creates a new message in the given `chat_id`."""
super().__init__() super(TelegramIO, self).__init__()
self.token = token self.token = token
self.chat_id = chat_id self.chat_id = chat_id
self.session = Session() self.session = Session()
@ -118,10 +121,10 @@ class tqdm_telegram(tqdm_auto):
self.tgio = TelegramIO( self.tgio = TelegramIO(
kwargs.pop('token', getenv('TQDM_TELEGRAM_TOKEN')), kwargs.pop('token', getenv('TQDM_TELEGRAM_TOKEN')),
kwargs.pop('chat_id', getenv('TQDM_TELEGRAM_CHAT_ID'))) kwargs.pop('chat_id', getenv('TQDM_TELEGRAM_CHAT_ID')))
super().__init__(*args, **kwargs) super(tqdm_telegram, self).__init__(*args, **kwargs)
def display(self, **kwargs): def display(self, **kwargs):
super().display(**kwargs) super(tqdm_telegram, self).display(**kwargs)
fmt = self.format_dict fmt = self.format_dict
if fmt.get('bar_format', None): if fmt.get('bar_format', None):
fmt['bar_format'] = fmt['bar_format'].replace( fmt['bar_format'] = fmt['bar_format'].replace(
@ -131,21 +134,24 @@ class tqdm_telegram(tqdm_auto):
self.tgio.write(self.format_meter(**fmt)) self.tgio.write(self.format_meter(**fmt))
def clear(self, *args, **kwargs): def clear(self, *args, **kwargs):
super().clear(*args, **kwargs) super(tqdm_telegram, self).clear(*args, **kwargs)
if not self.disable: if not self.disable:
self.tgio.write("") self.tgio.write("")
def close(self): def close(self):
if self.disable: if self.disable:
return return
super().close() super(tqdm_telegram, self).close()
if not (self.leave or (self.leave is None and self.pos == 0)): if not (self.leave or (self.leave is None and self.pos == 0)):
self.tgio.delete() self.tgio.delete()
def ttgrange(*args, **kwargs): def ttgrange(*args, **kwargs):
"""Shortcut for `tqdm.contrib.telegram.tqdm(range(*args), **kwargs)`.""" """
return tqdm_telegram(range(*args), **kwargs) A shortcut for `tqdm.contrib.telegram.tqdm(xrange(*args), **kwargs)`.
On Python3+, `range` is used instead of `xrange`.
"""
return tqdm_telegram(_range(*args), **kwargs)
# Aliases # Aliases

View file

@ -1,6 +1,8 @@
""" """
IO/concurrency helpers for `tqdm.contrib`. IO/concurrency helpers for `tqdm.contrib`.
""" """
from __future__ import absolute_import
from collections import deque from collections import deque
from concurrent.futures import ThreadPoolExecutor from concurrent.futures import ThreadPoolExecutor

View file

@ -1,3 +1,5 @@
from __future__ import absolute_import
from functools import partial from functools import partial
from dask.callbacks import Callback from dask.callbacks import Callback
@ -20,7 +22,7 @@ class TqdmCallback(Callback):
tqdm_kwargs : optional tqdm_kwargs : optional
Any other arguments used for all bars. Any other arguments used for all bars.
""" """
super().__init__(start=start, pretask=pretask) super(TqdmCallback, self).__init__(start=start, pretask=pretask)
if tqdm_kwargs: if tqdm_kwargs:
tqdm_class = partial(tqdm_class, **tqdm_kwargs) tqdm_class = partial(tqdm_class, **tqdm_kwargs)
self.tqdm_class = tqdm_class self.tqdm_class = tqdm_class

View file

@ -8,14 +8,16 @@ Usage:
""" """
# future division is important to divide integers and get as # future division is important to divide integers and get as
# a result precise floating numbers (instead of truncated int) # a result precise floating numbers (instead of truncated int)
from __future__ import absolute_import, division
import re import re
from warnings import warn from warnings import warn
# to inherit from the tqdm class # to inherit from the tqdm class
from .std import TqdmExperimentalWarning from .std import TqdmExperimentalWarning
from .std import tqdm as std_tqdm from .std import tqdm as std_tqdm
# import compatibility functions and utilities # import compatibility functions and utilities
from .utils import _range
__author__ = {"github.com/": ["casperdcl", "lrq3000"]} __author__ = {"github.com/": ["casperdcl", "lrq3000"]}
__all__ = ['tqdm_gui', 'tgrange', 'tqdm', 'trange'] __all__ = ['tqdm_gui', 'tgrange', 'tqdm', 'trange']
@ -32,7 +34,7 @@ class tqdm_gui(std_tqdm): # pragma: no cover
kwargs = kwargs.copy() kwargs = kwargs.copy()
kwargs['gui'] = True kwargs['gui'] = True
colour = kwargs.pop('colour', 'g') colour = kwargs.pop('colour', 'g')
super().__init__(*args, **kwargs) super(tqdm_gui, self).__init__(*args, **kwargs)
if self.disable: if self.disable:
return return
@ -122,7 +124,6 @@ class tqdm_gui(std_tqdm): # pragma: no cover
ax = self.ax ax = self.ax
line1 = self.line1 line1 = self.line1
line2 = self.line2 line2 = self.line2
hspan = getattr(self, 'hspan', None)
# instantaneous rate # instantaneous rate
y = delta_it / delta_t y = delta_it / delta_t
# overall rate # overall rate
@ -149,10 +150,18 @@ class tqdm_gui(std_tqdm): # pragma: no cover
if total: if total:
line1.set_data(xdata, ydata) line1.set_data(xdata, ydata)
line2.set_data(xdata, zdata) line2.set_data(xdata, zdata)
if hspan: try:
hspan.set_xy((0, ymin)) poly_lims = self.hspan.get_xy()
hspan.set_height(ymax - ymin) except AttributeError:
hspan.set_width(n / total) self.hspan = self.plt.axhspan(0, 0.001, xmin=0, xmax=0, color='g')
poly_lims = self.hspan.get_xy()
poly_lims[0, 1] = ymin
poly_lims[1, 1] = ymax
poly_lims[2] = [n / total, ymax]
poly_lims[3] = [poly_lims[2, 0], ymin]
if len(poly_lims) > 4:
poly_lims[4, 1] = ymin
self.hspan.set_xy(poly_lims)
else: else:
t_ago = [cur_t - i for i in xdata] t_ago = [cur_t - i for i in xdata]
line1.set_data(t_ago, ydata) line1.set_data(t_ago, ydata)
@ -164,14 +173,17 @@ class tqdm_gui(std_tqdm): # pragma: no cover
"{bar}", "<bar/>") "{bar}", "<bar/>")
msg = self.format_meter(**d) msg = self.format_meter(**d)
if '<bar/>' in msg: if '<bar/>' in msg:
msg = "".join(re.split(r'\|?<bar/>\|?', msg, maxsplit=1)) msg = "".join(re.split(r'\|?<bar/>\|?', msg, 1))
ax.set_title(msg, fontname="DejaVu Sans Mono", fontsize=11) ax.set_title(msg, fontname="DejaVu Sans Mono", fontsize=11)
self.plt.pause(1e-9) self.plt.pause(1e-9)
def tgrange(*args, **kwargs): def tgrange(*args, **kwargs):
"""Shortcut for `tqdm.gui.tqdm(range(*args), **kwargs)`.""" """
return tqdm_gui(range(*args), **kwargs) A shortcut for `tqdm.gui.tqdm(xrange(*args), **kwargs)`.
On Python3+, `range` is used instead of `xrange`.
"""
return tqdm_gui(_range(*args), **kwargs)
# Aliases # Aliases

View file

@ -1,3 +1,5 @@
from __future__ import absolute_import, division
from copy import copy from copy import copy
from functools import partial from functools import partial
@ -94,7 +96,7 @@ class TqdmCallback(keras.callbacks.Callback):
raise KeyError('Unknown verbosity') raise KeyError('Unknown verbosity')
def on_train_end(self, *_, **__): def on_train_end(self, *_, **__):
if hasattr(self, 'batch_bar'): if self.verbose:
self.batch_bar.close() self.batch_bar.close()
self.epoch_bar.close() self.epoch_bar.close()

View file

@ -7,14 +7,18 @@ Usage:
>>> for i in trange(10): >>> for i in trange(10):
... ... ... ...
""" """
# future division is important to divide integers and get as
# a result precise floating numbers (instead of truncated int)
from __future__ import absolute_import, division
# import compatibility functions and utilities # import compatibility functions and utilities
import re import re
import sys import sys
from html import escape
from weakref import proxy from weakref import proxy
# to inherit from the tqdm class # to inherit from the tqdm class
from .std import tqdm as std_tqdm from .std import tqdm as std_tqdm
from .utils import _range
if True: # pragma: no cover if True: # pragma: no cover
# import IPython/Jupyter base widget and display utilities # import IPython/Jupyter base widget and display utilities
@ -59,6 +63,12 @@ if True: # pragma: no cover
except ImportError: except ImportError:
pass pass
# HTML encoding
try: # Py3
from html import escape
except ImportError: # Py2
from cgi import escape
__author__ = {"github.com/": ["lrq3000", "casperdcl", "alexanderkuk"]} __author__ = {"github.com/": ["lrq3000", "casperdcl", "alexanderkuk"]}
__all__ = ['tqdm_notebook', 'tnrange', 'tqdm', 'trange'] __all__ = ['tqdm_notebook', 'tnrange', 'tqdm', 'trange']
WARN_NOIPYW = ("IProgress not found. Please update jupyter and ipywidgets." WARN_NOIPYW = ("IProgress not found. Please update jupyter and ipywidgets."
@ -80,7 +90,7 @@ class TqdmHBox(HBox):
def __repr__(self, pretty=False): def __repr__(self, pretty=False):
pbar = getattr(self, 'pbar', None) pbar = getattr(self, 'pbar', None)
if pbar is None: if pbar is None:
return super().__repr__() return super(TqdmHBox, self).__repr__()
return pbar.format_meter(**self._json_(pretty)) return pbar.format_meter(**self._json_(pretty))
def _repr_pretty_(self, pp, *_, **__): def _repr_pretty_(self, pp, *_, **__):
@ -157,10 +167,9 @@ class tqdm_notebook(std_tqdm):
pbar.value = self.n pbar.value = self.n
if msg: if msg:
msg = msg.replace(' ', u'\u2007') # fix html space padding
# html escape special characters (like '&') # html escape special characters (like '&')
if '<bar/>' in msg: if '<bar/>' in msg:
left, right = map(escape, re.split(r'\|?<bar/>\|?', msg, maxsplit=1)) left, right = map(escape, re.split(r'\|?<bar/>\|?', msg, 1))
else: else:
left, right = '', escape(msg) left, right = '', escape(msg)
@ -220,7 +229,7 @@ class tqdm_notebook(std_tqdm):
kwargs['disable'] = bool(kwargs.get('disable', False)) kwargs['disable'] = bool(kwargs.get('disable', False))
colour = kwargs.pop('colour', None) colour = kwargs.pop('colour', None)
display_here = kwargs.pop('display', True) display_here = kwargs.pop('display', True)
super().__init__(*args, **kwargs) super(tqdm_notebook, self).__init__(*args, **kwargs)
if self.disable or not kwargs['gui']: if self.disable or not kwargs['gui']:
self.disp = lambda *_, **__: None self.disp = lambda *_, **__: None
return return
@ -246,7 +255,7 @@ class tqdm_notebook(std_tqdm):
def __iter__(self): def __iter__(self):
try: try:
it = super().__iter__() it = super(tqdm_notebook, self).__iter__()
for obj in it: for obj in it:
# return super(tqdm...) will not catch exception # return super(tqdm...) will not catch exception
yield obj yield obj
@ -259,7 +268,7 @@ class tqdm_notebook(std_tqdm):
def update(self, n=1): def update(self, n=1):
try: try:
return super().update(n=n) return super(tqdm_notebook, self).update(n=n)
# NB: except ... [ as ...] breaks IPython async KeyboardInterrupt # NB: except ... [ as ...] breaks IPython async KeyboardInterrupt
except: # NOQA except: # NOQA
# cannot catch KeyboardInterrupt when using manual tqdm # cannot catch KeyboardInterrupt when using manual tqdm
@ -272,7 +281,7 @@ class tqdm_notebook(std_tqdm):
def close(self): def close(self):
if self.disable: if self.disable:
return return
super().close() super(tqdm_notebook, self).close()
# Try to detect if there was an error or KeyboardInterrupt # Try to detect if there was an error or KeyboardInterrupt
# in manual mode: if n < total, things probably got wrong # in manual mode: if n < total, things probably got wrong
if self.total and self.n < self.total: if self.total and self.n < self.total:
@ -297,19 +306,22 @@ class tqdm_notebook(std_tqdm):
total : int or float, optional. Total to use for the new bar. total : int or float, optional. Total to use for the new bar.
""" """
if self.disable: if self.disable:
return super().reset(total=total) return super(tqdm_notebook, self).reset(total=total)
_, pbar, _ = self.container.children _, pbar, _ = self.container.children
pbar.bar_style = '' pbar.bar_style = ''
if total is not None: if total is not None:
pbar.max = total pbar.max = total
if not self.total and self.ncols is None: # no longer unknown total if not self.total and self.ncols is None: # no longer unknown total
pbar.layout.width = None # reset width pbar.layout.width = None # reset width
return super().reset(total=total) return super(tqdm_notebook, self).reset(total=total)
def tnrange(*args, **kwargs): def tnrange(*args, **kwargs):
"""Shortcut for `tqdm.notebook.tqdm(range(*args), **kwargs)`.""" """
return tqdm_notebook(range(*args), **kwargs) A shortcut for `tqdm.notebook.tqdm(xrange(*args), **kwargs)`.
On Python3+, `range` is used instead of `xrange`.
"""
return tqdm_notebook(_range(*args), **kwargs)
# Aliases # Aliases

View file

@ -6,6 +6,8 @@ Usage:
>>> for i in trange(10): >>> for i in trange(10):
... ... ... ...
""" """
from __future__ import absolute_import
from warnings import warn from warnings import warn
from rich.progress import ( from rich.progress import (
@ -13,6 +15,7 @@ from rich.progress import (
from .std import TqdmExperimentalWarning from .std import TqdmExperimentalWarning
from .std import tqdm as std_tqdm from .std import tqdm as std_tqdm
from .utils import _range
__author__ = {"github.com/": ["casperdcl"]} __author__ = {"github.com/": ["casperdcl"]}
__all__ = ['tqdm_rich', 'trrange', 'tqdm', 'trange'] __all__ = ['tqdm_rich', 'trrange', 'tqdm', 'trange']
@ -90,7 +93,7 @@ class tqdm_rich(std_tqdm): # pragma: no cover
kwargs['disable'] = bool(kwargs.get('disable', False)) kwargs['disable'] = bool(kwargs.get('disable', False))
progress = kwargs.pop('progress', None) progress = kwargs.pop('progress', None)
options = kwargs.pop('options', {}).copy() options = kwargs.pop('options', {}).copy()
super().__init__(*args, **kwargs) super(tqdm_rich, self).__init__(*args, **kwargs)
if self.disable: if self.disable:
return return
@ -116,8 +119,7 @@ class tqdm_rich(std_tqdm): # pragma: no cover
def close(self): def close(self):
if self.disable: if self.disable:
return return
self.display() # print 100%, vis #1306 super(tqdm_rich, self).close()
super().close()
self._prog.__exit__(None, None, None) self._prog.__exit__(None, None, None)
def clear(self, *_, **__): def clear(self, *_, **__):
@ -138,12 +140,15 @@ class tqdm_rich(std_tqdm): # pragma: no cover
""" """
if hasattr(self, '_prog'): if hasattr(self, '_prog'):
self._prog.reset(total=total) self._prog.reset(total=total)
super().reset(total=total) super(tqdm_rich, self).reset(total=total)
def trrange(*args, **kwargs): def trrange(*args, **kwargs):
"""Shortcut for `tqdm.rich.tqdm(range(*args), **kwargs)`.""" """
return tqdm_rich(range(*args), **kwargs) A shortcut for `tqdm.rich.tqdm(xrange(*args), **kwargs)`.
On Python3+, `range` is used instead of `xrange`.
"""
return tqdm_rich(_range(*args), **kwargs)
# Aliases # Aliases

View file

@ -7,10 +7,12 @@ Usage:
>>> for i in trange(10): >>> for i in trange(10):
... ... ... ...
""" """
from __future__ import absolute_import, division
import sys import sys
from collections import OrderedDict, defaultdict from collections import OrderedDict, defaultdict
from contextlib import contextmanager from contextlib import contextmanager
from datetime import datetime, timedelta, timezone from datetime import datetime, timedelta
from numbers import Number from numbers import Number
from time import time from time import time
from warnings import warn from warnings import warn
@ -19,8 +21,8 @@ from weakref import WeakSet
from ._monitor import TMonitor from ._monitor import TMonitor
from .utils import ( from .utils import (
CallbackIOWrapper, Comparable, DisableOnWriteError, FormatReplace, SimpleTextIOWrapper, CallbackIOWrapper, Comparable, DisableOnWriteError, FormatReplace, SimpleTextIOWrapper,
_is_ascii, _screen_shape_wrapper, _supports_unicode, _term_move_up, disp_len, disp_trim, _basestring, _is_ascii, _range, _screen_shape_wrapper, _supports_unicode, _term_move_up,
envwrap) _unich, _unicode, disp_len, disp_trim)
__author__ = "https://github.com/tqdm/tqdm#contributions" __author__ = "https://github.com/tqdm/tqdm#contributions"
__all__ = ['tqdm', 'trange', __all__ = ['tqdm', 'trange',
@ -46,7 +48,7 @@ class TqdmWarning(Warning):
if fp_write is not None: if fp_write is not None:
fp_write("\n" + self.__class__.__name__ + ": " + str(msg).rstrip() + '\n') fp_write("\n" + self.__class__.__name__ + ": " + str(msg).rstrip() + '\n')
else: else:
super().__init__(msg, *a, **k) super(TqdmWarning, self).__init__(msg, *a, **k)
class TqdmExperimentalWarning(TqdmWarning, FutureWarning): class TqdmExperimentalWarning(TqdmWarning, FutureWarning):
@ -142,7 +144,7 @@ class Bar(object):
+ `b`: blank (`charset=" "` override) + `b`: blank (`charset=" "` override)
""" """
ASCII = " 123456789#" ASCII = " 123456789#"
UTF = u" " + u''.join(map(chr, range(0x258F, 0x2587, -1))) UTF = u" " + u''.join(map(_unich, range(0x258F, 0x2587, -1)))
BLANK = " " BLANK = " "
COLOUR_RESET = '\x1b[0m' COLOUR_RESET = '\x1b[0m'
COLOUR_RGB = '\x1b[38;2;%d;%d;%dm' COLOUR_RGB = '\x1b[38;2;%d;%d;%dm'
@ -247,120 +249,6 @@ class tqdm(Comparable):
Decorate an iterable object, returning an iterator which acts exactly Decorate an iterable object, returning an iterator which acts exactly
like the original iterable, but prints a dynamically updating like the original iterable, but prints a dynamically updating
progressbar every time a value is requested. progressbar every time a value is requested.
Parameters
----------
iterable : iterable, optional
Iterable to decorate with a progressbar.
Leave blank to manually manage the updates.
desc : str, optional
Prefix for the progressbar.
total : int or float, optional
The number of expected iterations. If unspecified,
len(iterable) is used if possible. If float("inf") or as a last
resort, only basic progress statistics are displayed
(no ETA, no progressbar).
If `gui` is True and this parameter needs subsequent updating,
specify an initial arbitrary large positive number,
e.g. 9e9.
leave : bool, optional
If [default: True], keeps all traces of the progressbar
upon termination of iteration.
If `None`, will leave only if `position` is `0`.
file : `io.TextIOWrapper` or `io.StringIO`, optional
Specifies where to output the progress messages
(default: sys.stderr). Uses `file.write(str)` and `file.flush()`
methods. For encoding, see `write_bytes`.
ncols : int, optional
The width of the entire output message. If specified,
dynamically resizes the progressbar to stay within this bound.
If unspecified, attempts to use environment width. The
fallback is a meter width of 10 and no limit for the counter and
statistics. If 0, will not print any meter (only stats).
mininterval : float, optional
Minimum progress display update interval [default: 0.1] seconds.
maxinterval : float, optional
Maximum progress display update interval [default: 10] seconds.
Automatically adjusts `miniters` to correspond to `mininterval`
after long display update lag. Only works if `dynamic_miniters`
or monitor thread is enabled.
miniters : int or float, optional
Minimum progress display update interval, in iterations.
If 0 and `dynamic_miniters`, will automatically adjust to equal
`mininterval` (more CPU efficient, good for tight loops).
If > 0, will skip display of specified number of iterations.
Tweak this and `mininterval` to get very efficient loops.
If your progress is erratic with both fast and slow iterations
(network, skipping items, etc) you should set miniters=1.
ascii : bool or str, optional
If unspecified or False, use unicode (smooth blocks) to fill
the meter. The fallback is to use ASCII characters " 123456789#".
disable : bool, optional
Whether to disable the entire progressbar wrapper
[default: False]. If set to None, disable on non-TTY.
unit : str, optional
String that will be used to define the unit of each iteration
[default: it].
unit_scale : bool or int or float, optional
If 1 or True, the number of iterations will be reduced/scaled
automatically and a metric prefix following the
International System of Units standard will be added
(kilo, mega, etc.) [default: False]. If any other non-zero
number, will scale `total` and `n`.
dynamic_ncols : bool, optional
If set, constantly alters `ncols` and `nrows` to the
environment (allowing for window resizes) [default: False].
smoothing : float, optional
Exponential moving average smoothing factor for speed estimates
(ignored in GUI mode). Ranges from 0 (average speed) to 1
(current/instantaneous speed) [default: 0.3].
bar_format : str, optional
Specify a custom bar string formatting. May impact performance.
[default: '{l_bar}{bar}{r_bar}'], where
l_bar='{desc}: {percentage:3.0f}%|' and
r_bar='| {n_fmt}/{total_fmt} [{elapsed}<{remaining}, '
'{rate_fmt}{postfix}]'
Possible vars: l_bar, bar, r_bar, n, n_fmt, total, total_fmt,
percentage, elapsed, elapsed_s, ncols, nrows, desc, unit,
rate, rate_fmt, rate_noinv, rate_noinv_fmt,
rate_inv, rate_inv_fmt, postfix, unit_divisor,
remaining, remaining_s, eta.
Note that a trailing ": " is automatically removed after {desc}
if the latter is empty.
initial : int or float, optional
The initial counter value. Useful when restarting a progress
bar [default: 0]. If using float, consider specifying `{n:.3f}`
or similar in `bar_format`, or specifying `unit_scale`.
position : int, optional
Specify the line offset to print this bar (starting from 0)
Automatic if unspecified.
Useful to manage multiple bars at once (eg, from threads).
postfix : dict or *, optional
Specify additional stats to display at the end of the bar.
Calls `set_postfix(**postfix)` if possible (dict).
unit_divisor : float, optional
[default: 1000], ignored unless `unit_scale` is True.
write_bytes : bool, optional
Whether to write bytes. If (default: False) will write unicode.
lock_args : tuple, optional
Passed to `refresh` for intermediate output
(initialisation, iterating, and updating).
nrows : int, optional
The screen height. If specified, hides nested bars outside this
bound. If unspecified, attempts to use environment height.
The fallback is 20.
colour : str, optional
Bar colour (e.g. 'green', '#00ff00').
delay : float, optional
Don't display until [default: 0] seconds have elapsed.
gui : bool, optional
WARNING: internal parameter - do not use.
Use tqdm.gui.tqdm(...) instead. If set, will attempt to use
matplotlib animations for a graphical output [default: False].
Returns
-------
out : decorated iterator.
""" """
monitor_interval = 10 # set to 0 to disable the thread monitor_interval = 10 # set to 0 to disable the thread
@ -391,11 +279,11 @@ class tqdm(Comparable):
if abs(num) < 999.5: if abs(num) < 999.5:
if abs(num) < 99.95: if abs(num) < 99.95:
if abs(num) < 9.995: if abs(num) < 9.995:
return f'{num:1.2f}{unit}{suffix}' return '{0:1.2f}'.format(num) + unit + suffix
return f'{num:2.1f}{unit}{suffix}' return '{0:2.1f}'.format(num) + unit + suffix
return f'{num:3.0f}{unit}{suffix}' return '{0:3.0f}'.format(num) + unit + suffix
num /= divisor num /= divisor
return f'{num:3.1f}Y{suffix}' return '{0:3.1f}Y'.format(num) + suffix
@staticmethod @staticmethod
def format_interval(t): def format_interval(t):
@ -414,7 +302,10 @@ class tqdm(Comparable):
""" """
mins, s = divmod(int(t), 60) mins, s = divmod(int(t), 60)
h, m = divmod(mins, 60) h, m = divmod(mins, 60)
return f'{h:d}:{m:02d}:{s:02d}' if h else f'{m:02d}:{s:02d}' if h:
return '{0:d}:{1:02d}:{2:02d}'.format(h, m, s)
else:
return '{0:02d}:{1:02d}'.format(m, s)
@staticmethod @staticmethod
def format_num(n): def format_num(n):
@ -431,7 +322,7 @@ class tqdm(Comparable):
out : str out : str
Formatted number. Formatted number.
""" """
f = f'{n:.3g}'.replace('e+0', 'e+').replace('e-0', 'e-') f = '{0:.3g}'.format(n).replace('+0', '+').replace('-0', '-')
n = str(n) n = str(n)
return f if len(f) < len(n) else n return f if len(f) < len(n) else n
@ -449,7 +340,7 @@ class tqdm(Comparable):
getattr(sys.stdout, 'flush', lambda: None)() getattr(sys.stdout, 'flush', lambda: None)()
def fp_write(s): def fp_write(s):
fp.write(str(s)) fp.write(_unicode(s))
fp_flush() fp_flush()
last_len = [0] last_len = [0]
@ -551,10 +442,10 @@ class tqdm(Comparable):
rate = (n - initial) / elapsed rate = (n - initial) / elapsed
inv_rate = 1 / rate if rate else None inv_rate = 1 / rate if rate else None
format_sizeof = tqdm.format_sizeof format_sizeof = tqdm.format_sizeof
rate_noinv_fmt = ((format_sizeof(rate) if unit_scale else f'{rate:5.2f}') rate_noinv_fmt = ((format_sizeof(rate) if unit_scale else
if rate else '?') + unit + '/s' '{0:5.2f}'.format(rate)) if rate else '?') + unit + '/s'
rate_inv_fmt = ( rate_inv_fmt = (
(format_sizeof(inv_rate) if unit_scale else f'{inv_rate:5.2f}') (format_sizeof(inv_rate) if unit_scale else '{0:5.2f}'.format(inv_rate))
if inv_rate else '?') + 's/' + unit if inv_rate else '?') + 's/' + unit
rate_fmt = rate_inv_fmt if inv_rate and inv_rate > 1 else rate_noinv_fmt rate_fmt = rate_inv_fmt if inv_rate and inv_rate > 1 else rate_noinv_fmt
@ -574,7 +465,7 @@ class tqdm(Comparable):
remaining_str = tqdm.format_interval(remaining) if rate else '?' remaining_str = tqdm.format_interval(remaining) if rate else '?'
try: try:
eta_dt = (datetime.now() + timedelta(seconds=remaining) eta_dt = (datetime.now() + timedelta(seconds=remaining)
if rate and total else datetime.fromtimestamp(0, timezone.utc)) if rate and total else datetime.utcfromtimestamp(0))
except OverflowError: except OverflowError:
eta_dt = datetime.max eta_dt = datetime.max
@ -586,25 +477,26 @@ class tqdm(Comparable):
else: else:
l_bar = '' l_bar = ''
r_bar = f'| {n_fmt}/{total_fmt} [{elapsed_str}<{remaining_str}, {rate_fmt}{postfix}]' r_bar = '| {0}/{1} [{2}<{3}, {4}{5}]'.format(
n_fmt, total_fmt, elapsed_str, remaining_str, rate_fmt, postfix)
# Custom bar formatting # Custom bar formatting
# Populate a dict with all available progress indicators # Populate a dict with all available progress indicators
format_dict = { format_dict = dict(
# slight extension of self.format_dict # slight extension of self.format_dict
'n': n, 'n_fmt': n_fmt, 'total': total, 'total_fmt': total_fmt, n=n, n_fmt=n_fmt, total=total, total_fmt=total_fmt,
'elapsed': elapsed_str, 'elapsed_s': elapsed, elapsed=elapsed_str, elapsed_s=elapsed,
'ncols': ncols, 'desc': prefix or '', 'unit': unit, ncols=ncols, desc=prefix or '', unit=unit,
'rate': inv_rate if inv_rate and inv_rate > 1 else rate, rate=inv_rate if inv_rate and inv_rate > 1 else rate,
'rate_fmt': rate_fmt, 'rate_noinv': rate, rate_fmt=rate_fmt, rate_noinv=rate,
'rate_noinv_fmt': rate_noinv_fmt, 'rate_inv': inv_rate, rate_noinv_fmt=rate_noinv_fmt, rate_inv=inv_rate,
'rate_inv_fmt': rate_inv_fmt, rate_inv_fmt=rate_inv_fmt,
'postfix': postfix, 'unit_divisor': unit_divisor, postfix=postfix, unit_divisor=unit_divisor,
'colour': colour, colour=colour,
# plus more useful definitions # plus more useful definitions
'remaining': remaining_str, 'remaining_s': remaining, remaining=remaining_str, remaining_s=remaining,
'l_bar': l_bar, 'r_bar': r_bar, 'eta': eta_dt, l_bar=l_bar, r_bar=r_bar, eta=eta_dt,
**extra_kwargs} **extra_kwargs)
# total is known: we can predict some stats # total is known: we can predict some stats
if total: if total:
@ -612,7 +504,7 @@ class tqdm(Comparable):
frac = n / total frac = n / total
percentage = frac * 100 percentage = frac * 100
l_bar += f'{percentage:3.0f}%|' l_bar += '{0:3.0f}%|'.format(percentage)
if ncols == 0: if ncols == 0:
return l_bar[:-1] + r_bar[1:] return l_bar[:-1] + r_bar[1:]
@ -621,16 +513,21 @@ class tqdm(Comparable):
if bar_format: if bar_format:
format_dict.update(percentage=percentage) format_dict.update(percentage=percentage)
# auto-remove colon for empty `{desc}` # auto-remove colon for empty `desc`
if not prefix: if not prefix:
bar_format = bar_format.replace("{desc}: ", '') bar_format = bar_format.replace("{desc}: ", '')
else: else:
bar_format = "{l_bar}{bar}{r_bar}" bar_format = "{l_bar}{bar}{r_bar}"
full_bar = FormatReplace() full_bar = FormatReplace()
try:
nobar = bar_format.format(bar=full_bar, **format_dict)
except UnicodeEncodeError:
bar_format = _unicode(bar_format)
nobar = bar_format.format(bar=full_bar, **format_dict) nobar = bar_format.format(bar=full_bar, **format_dict)
if not full_bar.format_called: if not full_bar.format_called:
return nobar # no `{bar}`; nothing else to do # no {bar}, we can just format and return
return nobar
# Formatting progress bar space available for bar's display # Formatting progress bar space available for bar's display
full_bar = Bar(frac, full_bar = Bar(frac,
@ -638,7 +535,7 @@ class tqdm(Comparable):
charset=Bar.ASCII if ascii is True else ascii or Bar.UTF, charset=Bar.ASCII if ascii is True else ascii or Bar.UTF,
colour=colour) colour=colour)
if not _is_ascii(full_bar.charset) and _is_ascii(bar_format): if not _is_ascii(full_bar.charset) and _is_ascii(bar_format):
bar_format = str(bar_format) bar_format = _unicode(bar_format)
res = bar_format.format(bar=full_bar, **format_dict) res = bar_format.format(bar=full_bar, **format_dict)
return disp_trim(res, ncols) if ncols else res return disp_trim(res, ncols) if ncols else res
@ -657,8 +554,8 @@ class tqdm(Comparable):
return disp_trim(res, ncols) if ncols else res return disp_trim(res, ncols) if ncols else res
else: else:
# no total: no progressbar, ETA, just progress stats # no total: no progressbar, ETA, just progress stats
return (f'{(prefix + ": ") if prefix else ""}' return '{0}{1}{2} [{3}, {4}{5}]'.format(
f'{n_fmt}{unit} [{elapsed_str}, {rate_fmt}{postfix}]') (prefix + ": ") if prefix else '', n_fmt, unit, elapsed_str, rate_fmt, postfix)
def __new__(cls, *_, **__): def __new__(cls, *_, **__):
instance = object.__new__(cls) instance = object.__new__(cls)
@ -930,8 +827,6 @@ class tqdm(Comparable):
DataFrame.progress_apply = inner_generator() DataFrame.progress_apply = inner_generator()
DataFrameGroupBy.progress_apply = inner_generator() DataFrameGroupBy.progress_apply = inner_generator()
DataFrame.progress_applymap = inner_generator('applymap') DataFrame.progress_applymap = inner_generator('applymap')
DataFrame.progress_map = inner_generator('map')
DataFrameGroupBy.progress_map = inner_generator('map')
if Panel is not None: if Panel is not None:
Panel.progress_apply = inner_generator() Panel.progress_apply = inner_generator()
@ -948,17 +843,133 @@ class tqdm(Comparable):
elif _Rolling_and_Expanding is not None: elif _Rolling_and_Expanding is not None:
_Rolling_and_Expanding.progress_apply = inner_generator() _Rolling_and_Expanding.progress_apply = inner_generator()
# override defaults via env vars
@envwrap("TQDM_", is_method=True, types={'total': float, 'ncols': int, 'miniters': float,
'position': int, 'nrows': int})
def __init__(self, iterable=None, desc=None, total=None, leave=True, file=None, def __init__(self, iterable=None, desc=None, total=None, leave=True, file=None,
ncols=None, mininterval=0.1, maxinterval=10.0, miniters=None, ncols=None, mininterval=0.1, maxinterval=10.0, miniters=None,
ascii=None, disable=False, unit='it', unit_scale=False, ascii=None, disable=False, unit='it', unit_scale=False,
dynamic_ncols=False, smoothing=0.3, bar_format=None, initial=0, dynamic_ncols=False, smoothing=0.3, bar_format=None, initial=0,
position=None, postfix=None, unit_divisor=1000, write_bytes=False, position=None, postfix=None, unit_divisor=1000, write_bytes=None,
lock_args=None, nrows=None, colour=None, delay=0.0, gui=False, lock_args=None, nrows=None, colour=None, delay=0, gui=False,
**kwargs): **kwargs):
"""see tqdm.tqdm for arguments""" """
Parameters
----------
iterable : iterable, optional
Iterable to decorate with a progressbar.
Leave blank to manually manage the updates.
desc : str, optional
Prefix for the progressbar.
total : int or float, optional
The number of expected iterations. If unspecified,
len(iterable) is used if possible. If float("inf") or as a last
resort, only basic progress statistics are displayed
(no ETA, no progressbar).
If `gui` is True and this parameter needs subsequent updating,
specify an initial arbitrary large positive number,
e.g. 9e9.
leave : bool, optional
If [default: True], keeps all traces of the progressbar
upon termination of iteration.
If `None`, will leave only if `position` is `0`.
file : `io.TextIOWrapper` or `io.StringIO`, optional
Specifies where to output the progress messages
(default: sys.stderr). Uses `file.write(str)` and `file.flush()`
methods. For encoding, see `write_bytes`.
ncols : int, optional
The width of the entire output message. If specified,
dynamically resizes the progressbar to stay within this bound.
If unspecified, attempts to use environment width. The
fallback is a meter width of 10 and no limit for the counter and
statistics. If 0, will not print any meter (only stats).
mininterval : float, optional
Minimum progress display update interval [default: 0.1] seconds.
maxinterval : float, optional
Maximum progress display update interval [default: 10] seconds.
Automatically adjusts `miniters` to correspond to `mininterval`
after long display update lag. Only works if `dynamic_miniters`
or monitor thread is enabled.
miniters : int or float, optional
Minimum progress display update interval, in iterations.
If 0 and `dynamic_miniters`, will automatically adjust to equal
`mininterval` (more CPU efficient, good for tight loops).
If > 0, will skip display of specified number of iterations.
Tweak this and `mininterval` to get very efficient loops.
If your progress is erratic with both fast and slow iterations
(network, skipping items, etc) you should set miniters=1.
ascii : bool or str, optional
If unspecified or False, use unicode (smooth blocks) to fill
the meter. The fallback is to use ASCII characters " 123456789#".
disable : bool, optional
Whether to disable the entire progressbar wrapper
[default: False]. If set to None, disable on non-TTY.
unit : str, optional
String that will be used to define the unit of each iteration
[default: it].
unit_scale : bool or int or float, optional
If 1 or True, the number of iterations will be reduced/scaled
automatically and a metric prefix following the
International System of Units standard will be added
(kilo, mega, etc.) [default: False]. If any other non-zero
number, will scale `total` and `n`.
dynamic_ncols : bool, optional
If set, constantly alters `ncols` and `nrows` to the
environment (allowing for window resizes) [default: False].
smoothing : float, optional
Exponential moving average smoothing factor for speed estimates
(ignored in GUI mode). Ranges from 0 (average speed) to 1
(current/instantaneous speed) [default: 0.3].
bar_format : str, optional
Specify a custom bar string formatting. May impact performance.
[default: '{l_bar}{bar}{r_bar}'], where
l_bar='{desc}: {percentage:3.0f}%|' and
r_bar='| {n_fmt}/{total_fmt} [{elapsed}<{remaining}, '
'{rate_fmt}{postfix}]'
Possible vars: l_bar, bar, r_bar, n, n_fmt, total, total_fmt,
percentage, elapsed, elapsed_s, ncols, nrows, desc, unit,
rate, rate_fmt, rate_noinv, rate_noinv_fmt,
rate_inv, rate_inv_fmt, postfix, unit_divisor,
remaining, remaining_s, eta.
Note that a trailing ": " is automatically removed after {desc}
if the latter is empty.
initial : int or float, optional
The initial counter value. Useful when restarting a progress
bar [default: 0]. If using float, consider specifying `{n:.3f}`
or similar in `bar_format`, or specifying `unit_scale`.
position : int, optional
Specify the line offset to print this bar (starting from 0)
Automatic if unspecified.
Useful to manage multiple bars at once (eg, from threads).
postfix : dict or *, optional
Specify additional stats to display at the end of the bar.
Calls `set_postfix(**postfix)` if possible (dict).
unit_divisor : float, optional
[default: 1000], ignored unless `unit_scale` is True.
write_bytes : bool, optional
If (default: None) and `file` is unspecified,
bytes will be written in Python 2. If `True` will also write
bytes. In all other cases will default to unicode.
lock_args : tuple, optional
Passed to `refresh` for intermediate output
(initialisation, iterating, and updating).
nrows : int, optional
The screen height. If specified, hides nested bars outside this
bound. If unspecified, attempts to use environment height.
The fallback is 20.
colour : str, optional
Bar colour (e.g. 'green', '#00ff00').
delay : float, optional
Don't display until [default: 0] seconds have elapsed.
gui : bool, optional
WARNING: internal parameter - do not use.
Use tqdm.gui.tqdm(...) instead. If set, will attempt to use
matplotlib animations for a graphical output [default: False].
Returns
-------
out : decorated iterator.
"""
if write_bytes is None:
write_bytes = file is None and sys.version_info < (3,)
if file is None: if file is None:
file = sys.stderr file = sys.stderr
@ -1040,7 +1051,7 @@ class tqdm(Comparable):
if bar_format and ascii is not True and not _is_ascii(ascii): if bar_format and ascii is not True and not _is_ascii(ascii):
# Convert bar format into unicode since terminal uses unicode # Convert bar format into unicode since terminal uses unicode
bar_format = str(bar_format) bar_format = _unicode(bar_format)
if smoothing is None: if smoothing is None:
smoothing = 0 smoothing = 0
@ -1109,6 +1120,9 @@ class tqdm(Comparable):
raise TypeError('bool() undefined when iterable == total == None') raise TypeError('bool() undefined when iterable == total == None')
return bool(self.iterable) return bool(self.iterable)
def __nonzero__(self):
return self.__bool__()
def __len__(self): def __len__(self):
return ( return (
self.total if self.iterable is None self.total if self.iterable is None
@ -1284,7 +1298,7 @@ class tqdm(Comparable):
# annoyingly, _supports_unicode isn't good enough # annoyingly, _supports_unicode isn't good enough
def fp_write(s): def fp_write(s):
self.fp.write(str(s)) self.fp.write(_unicode(s))
try: try:
fp_write('') fp_write('')
@ -1421,7 +1435,7 @@ class tqdm(Comparable):
if isinstance(postfix[key], Number): if isinstance(postfix[key], Number):
postfix[key] = self.format_num(postfix[key]) postfix[key] = self.format_num(postfix[key])
# Else for any other type, try to get the string conversion # Else for any other type, try to get the string conversion
elif not isinstance(postfix[key], str): elif not isinstance(postfix[key], _basestring):
postfix[key] = str(postfix[key]) postfix[key] = str(postfix[key])
# Else if it's a string, don't need to preprocess anything # Else if it's a string, don't need to preprocess anything
# Stitch together to get the final postfix # Stitch together to get the final postfix
@ -1440,7 +1454,7 @@ class tqdm(Comparable):
def moveto(self, n): def moveto(self, n):
# TODO: private method # TODO: private method
self.fp.write('\n' * n + _term_move_up() * -n) self.fp.write(_unicode('\n' * n + _term_move_up() * -n))
getattr(self.fp, 'flush', lambda: None)() getattr(self.fp, 'flush', lambda: None)()
@property @property
@ -1520,5 +1534,8 @@ class tqdm(Comparable):
def trange(*args, **kwargs): def trange(*args, **kwargs):
"""Shortcut for tqdm(range(*args), **kwargs).""" """
return tqdm(range(*args), **kwargs) A shortcut for tqdm(xrange(*args), **kwargs).
On Python3+ range is used instead of xrange.
"""
return tqdm(_range(*args), **kwargs)

View file

@ -6,14 +6,22 @@ Usage:
>>> for i in trange(10): >>> for i in trange(10):
... ... ... ...
""" """
from __future__ import absolute_import, division
import re import re
import sys import sys
from warnings import warn
try:
import tkinter import tkinter
import tkinter.ttk as ttk import tkinter.ttk as ttk
from warnings import warn except ImportError:
import Tkinter as tkinter
import ttk as ttk
from .std import TqdmExperimentalWarning, TqdmWarning from .std import TqdmExperimentalWarning, TqdmWarning
from .std import tqdm as std_tqdm from .std import tqdm as std_tqdm
from .utils import _range
__author__ = {"github.com/": ["richardsheridan", "casperdcl"]} __author__ = {"github.com/": ["richardsheridan", "casperdcl"]}
__all__ = ['tqdm_tk', 'ttkrange', 'tqdm', 'trange'] __all__ = ['tqdm_tk', 'ttkrange', 'tqdm', 'trange']
@ -53,7 +61,7 @@ class tqdm_tk(std_tqdm): # pragma: no cover
grab = kwargs.pop('grab', False) grab = kwargs.pop('grab', False)
tk_parent = kwargs.pop('tk_parent', None) tk_parent = kwargs.pop('tk_parent', None)
self._cancel_callback = kwargs.pop('cancel_callback', None) self._cancel_callback = kwargs.pop('cancel_callback', None)
super().__init__(*args, **kwargs) super(tqdm_tk, self).__init__(*args, **kwargs)
if self.disable: if self.disable:
return return
@ -135,7 +143,7 @@ class tqdm_tk(std_tqdm): # pragma: no cover
"{bar}", "<bar/>") "{bar}", "<bar/>")
msg = self.format_meter(**d) msg = self.format_meter(**d)
if '<bar/>' in msg: if '<bar/>' in msg:
msg = "".join(re.split(r'\|?<bar/>\|?', msg, maxsplit=1)) msg = "".join(re.split(r'\|?<bar/>\|?', msg, 1))
self._tk_text_var.set(msg) self._tk_text_var.set(msg)
if not self._tk_dispatching: if not self._tk_dispatching:
self._tk_window.update() self._tk_window.update()
@ -172,7 +180,7 @@ class tqdm_tk(std_tqdm): # pragma: no cover
self._tk_pbar.configure(maximum=100, mode="indeterminate") self._tk_pbar.configure(maximum=100, mode="indeterminate")
else: else:
self._tk_pbar.configure(maximum=total, mode="determinate") self._tk_pbar.configure(maximum=total, mode="determinate")
super().reset(total=total) super(tqdm_tk, self).reset(total=total)
@staticmethod @staticmethod
def _tk_dispatching_helper(): def _tk_dispatching_helper():
@ -187,8 +195,11 @@ class tqdm_tk(std_tqdm): # pragma: no cover
def ttkrange(*args, **kwargs): def ttkrange(*args, **kwargs):
"""Shortcut for `tqdm.tk.tqdm(range(*args), **kwargs)`.""" """
return tqdm_tk(range(*args), **kwargs) A shortcut for `tqdm.tk.tqdm(xrange(*args), **kwargs)`.
On Python3+, `range` is used instead of `xrange`.
"""
return tqdm_tk(_range(*args), **kwargs)
# Aliases # Aliases

View file

@ -204,8 +204,10 @@ float, optional.
.TP .TP
.B \-\-write\-bytes .B \-\-write\-bytes
bool, optional. bool, optional.
Whether to write bytes. If (default: None) and \f[C]file\f[] is unspecified, bytes will be
If (default: False) will write unicode. written in Python 2.
If \f[C]True\f[] will also write bytes.
In all other cases will default to unicode.
.RS .RS
.RE .RE
.TP .TP

View file

@ -4,17 +4,34 @@ General helpers required for `tqdm.std`.
import os import os
import re import re
import sys import sys
from functools import partial, partialmethod, wraps from functools import wraps
from inspect import signature
# TODO consider using wcswidth third-party package for 0-width characters
from unicodedata import east_asian_width
from warnings import warn from warnings import warn
from weakref import proxy from weakref import proxy
_range, _unich, _unicode, _basestring = range, chr, str, str # py2/3 compat
try:
_range = xrange
except NameError:
_range = range
try:
_unich = unichr
except NameError:
_unich = chr
try:
_unicode = unicode
except NameError:
_unicode = str
try:
_basestring = basestring
except NameError:
_basestring = str
CUR_OS = sys.platform CUR_OS = sys.platform
IS_WIN = any(CUR_OS.startswith(i) for i in ['win32', 'cygwin']) IS_WIN = any(CUR_OS.startswith(i) for i in ['win32', 'cygwin'])
IS_NIX = any(CUR_OS.startswith(i) for i in ['aix', 'linux', 'darwin', 'freebsd']) IS_NIX = any(CUR_OS.startswith(i) for i in ['aix', 'linux', 'darwin'])
RE_ANSI = re.compile(r"\x1b\[[;\d]*[A-Za-z]") RE_ANSI = re.compile(r"\x1b\[[;\d]*[A-Za-z]")
try: try:
@ -31,78 +48,10 @@ else:
colorama.init() colorama.init()
def envwrap(prefix, types=None, is_method=False):
"""
Override parameter defaults via `os.environ[prefix + param_name]`.
Maps UPPER_CASE env vars map to lower_case param names.
camelCase isn't supported (because Windows ignores case).
Precedence (highest first):
- call (`foo(a=3)`)
- environ (`FOO_A=2`)
- signature (`def foo(a=1)`)
Parameters
----------
prefix : str
Env var prefix, e.g. "FOO_"
types : dict, optional
Fallback mappings `{'param_name': type, ...}` if types cannot be
inferred from function signature.
Consider using `types=collections.defaultdict(lambda: ast.literal_eval)`.
is_method : bool, optional
Whether to use `functools.partialmethod`. If (default: False) use `functools.partial`.
Examples
--------
```
$ cat foo.py
from tqdm.utils import envwrap
@envwrap("FOO_")
def test(a=1, b=2, c=3):
print(f"received: a={a}, b={b}, c={c}")
$ FOO_A=42 FOO_C=1337 python -c 'import foo; foo.test(c=99)'
received: a=42, b=2, c=99
```
"""
if types is None:
types = {}
i = len(prefix)
env_overrides = {k[i:].lower(): v for k, v in os.environ.items() if k.startswith(prefix)}
part = partialmethod if is_method else partial
def wrap(func):
params = signature(func).parameters
# ignore unknown env vars
overrides = {k: v for k, v in env_overrides.items() if k in params}
# infer overrides' `type`s
for k in overrides:
param = params[k]
if param.annotation is not param.empty: # typehints
for typ in getattr(param.annotation, '__args__', (param.annotation,)):
try:
overrides[k] = typ(overrides[k])
except Exception:
pass
else:
break
elif param.default is not None: # type of default value
overrides[k] = type(param.default)(overrides[k])
else:
try: # `types` fallback
overrides[k] = types[k](overrides[k])
except KeyError: # keep unconverted (`str`)
pass
return part(func, **overrides)
return wrap
class FormatReplace(object): class FormatReplace(object):
""" """
>>> a = FormatReplace('something') >>> a = FormatReplace('something')
>>> f"{a:5d}" >>> "{:5d}".format(a)
'something' 'something'
""" # NOQA: P102 """ # NOQA: P102
def __init__(self, replace=''): def __init__(self, replace=''):
@ -167,7 +116,7 @@ class SimpleTextIOWrapper(ObjectWrapper):
""" """
# pylint: disable=too-few-public-methods # pylint: disable=too-few-public-methods
def __init__(self, wrapped, encoding): def __init__(self, wrapped, encoding):
super().__init__(wrapped) super(SimpleTextIOWrapper, self).__init__(wrapped)
self.wrapper_setattr('encoding', encoding) self.wrapper_setattr('encoding', encoding)
def write(self, s): def write(self, s):
@ -211,7 +160,7 @@ class DisableOnWriteError(ObjectWrapper):
return inner return inner
def __init__(self, wrapped, tqdm_instance): def __init__(self, wrapped, tqdm_instance):
super().__init__(wrapped) super(DisableOnWriteError, self).__init__(wrapped)
if hasattr(wrapped, 'write'): if hasattr(wrapped, 'write'):
self.wrapper_setattr( self.wrapper_setattr(
'write', self.disable_on_exception(tqdm_instance, wrapped.write)) 'write', self.disable_on_exception(tqdm_instance, wrapped.write))
@ -229,7 +178,7 @@ class CallbackIOWrapper(ObjectWrapper):
Wrap a given `file`-like object's `read()` or `write()` to report Wrap a given `file`-like object's `read()` or `write()` to report
lengths to the given `callback` lengths to the given `callback`
""" """
super().__init__(stream) super(CallbackIOWrapper, self).__init__(stream)
func = getattr(stream, method) func = getattr(stream, method)
if method == "write": if method == "write":
@wraps(func) @wraps(func)
@ -371,8 +320,14 @@ def _term_move_up(): # pragma: no cover
return '' if (os.name == 'nt') and (colorama is None) else '\x1b[A' return '' if (os.name == 'nt') and (colorama is None) else '\x1b[A'
try:
# TODO consider using wcswidth third-party package for 0-width characters
from unicodedata import east_asian_width
except ImportError:
_text_width = len
else:
def _text_width(s): def _text_width(s):
return sum(2 if east_asian_width(ch) in 'FW' else 1 for ch in str(s)) return sum(2 if east_asian_width(ch) in 'FW' else 1 for ch in _unicode(s))
def disp_len(data): def disp_len(data):