diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 1e4432333..eddd4ba9e 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -19,8 +19,6 @@ jobs: with: fetch-depth: 0 - uses: actions/setup-python@v2 - with: - python-version: '3.x' - run: pip install -U tox - run: tox env: @@ -34,8 +32,6 @@ jobs: with: fetch-depth: 0 - uses: actions/setup-python@v2 - with: - python-version: '3.x' - name: Install run: | pip install -U wheel @@ -70,8 +66,6 @@ jobs: with: fetch-depth: 0 - uses: actions/setup-python@v2 - with: - python-version: '3.x' - name: Install run: | pip install -U wheel diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index cbd958de4..68f27fa5b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,8 +10,6 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - with: - fetch-depth: 0 - uses: actions/setup-python@v2 - name: set PYSHA run: echo "PYSHA=$(python -VV | sha256sum | cut -d' ' -f1)" >> $GITHUB_ENV @@ -22,7 +20,7 @@ jobs: - name: dependencies run: pip install -U pre-commit - uses: reviewdog/action-setup@v1 - - if: github.event_name != 'schedule' + - if: github.event_name == 'push' || github.event_name == 'pull_request' name: comment run: | if [[ $EVENT == pull_request ]]; then @@ -42,7 +40,7 @@ jobs: strategy: matrix: os: [ubuntu] - python: [2.7, 3.5, 3.6, 3.7, 3.8, 3.9] + python: [2.7, 3.5, 3.6, 3.7, 3.8, 3.9, '3.10'] include: - os: macos python: 2.7 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f6597d93d..95b4a82f5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,7 +2,7 @@ default_language_version: python: python3 repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.0.1 + rev: v4.1.0 hooks: - id: check-added-large-files - id: check-case-conflict @@ -52,7 +52,7 @@ repos: - flake8-string-format - flake8-type-annotations - repo: https://github.com/PyCQA/isort - rev: 5.9.3 + rev: 5.10.1 hooks: - id: isort - repo: https://github.com/kynan/nbstripout diff --git a/Makefile b/Makefile index f0d1c0e61..796f8b07f 100644 --- a/Makefile +++ b/Makefile @@ -62,11 +62,11 @@ testsetup: python setup.py make none testnb: - pytest tests_notebook.ipynb --nbval --current-env -W=ignore --sanitize-with=setup.cfg --cov=tqdm.notebook --cov-report=term + pytest tests_notebook.ipynb --nbval --nbval-current-env -W=ignore --nbval-sanitize-with=setup.cfg --cov=tqdm.notebook --cov-report=term testcoverage: @make coverclean - pytest tests_notebook.ipynb --cov=tqdm --cov-report= --nbval --current-env --sanitize-with=setup.cfg -W=ignore + 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 testperf: diff --git a/setup.cfg b/setup.cfg index 8637b38d0..e43ab21a7 100644 --- a/setup.cfg +++ b/setup.cfg @@ -52,6 +52,7 @@ classifiers= 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 @@ -77,6 +78,7 @@ 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: @@ -122,7 +124,7 @@ markers= slow python_files=tests_*.py tests_*.ipynb testpaths=tests -addopts=-v --tb=short -rxs -W=error --durations=0 --durations-min=0.1 +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 @@ -139,5 +141,6 @@ omit= tqdm/contrib/telegram.py tqdm/contrib/utils_worker.py relative_files=True +disable_warnings=include-ignored [coverage:report] show_missing=True diff --git a/tests/tests_main.py b/tests/tests_main.py index c02c896bb..0523cc795 100644 --- a/tests/tests_main.py +++ b/tests/tests_main.py @@ -45,6 +45,7 @@ def test_pipes(): # actual test: assert norm(ls_out) == norm(out) assert b"it/s" in err + assert b"Error" not in err if sys.version_info[:2] >= (3, 8): diff --git a/tests/tests_tqdm.py b/tests/tests_tqdm.py index 99f570774..bba457aa2 100644 --- a/tests/tests_tqdm.py +++ b/tests/tests_tqdm.py @@ -1973,3 +1973,24 @@ def test_closed(): for i in trange(9, file=our_file, miniters=1, mininterval=0): if i == 5: our_file.close() + + +def test_reversed(capsys): + """Test reversed()""" + for _ in reversed(tqdm(_range(9))): + pass + out, err = capsys.readouterr() + assert not out + assert ' 0%' in err + assert '100%' in err + + +def test_contains(capsys): + """Test __contains__ doesn't iterate""" + with tqdm(list(range(9))) as t: + assert 9 not in t + assert all(i in t for i in _range(9)) + out, err = capsys.readouterr() + assert not out + assert ' 0%' in err + assert '100%' not in err diff --git a/tox.ini b/tox.ini index 506336d71..d77a5a880 100644 --- a/tox.ini +++ b/tox.ini @@ -26,11 +26,12 @@ PLATFORM= [core] deps= pytest + py3{4,5,6}: pytest<7 pytest-cov pytest-timeout py3{7,8,9,10}: pytest-asyncio py3{6,7,8,9,10}: ipywidgets - py3{6,7,8,9,10}: git+https://github.com/casperdcl/nbval.git@named_cells#egg=nbval + py3{7,8,9,10}: git+https://github.com/casperdcl/nbval.git@master#egg=nbval coverage coveralls codecov @@ -48,14 +49,16 @@ deps= matplotlib numpy pandas + tf: tensorflow!=2.5.0 + !py27-keras: keras py27-keras: keras<2.5 + py35-keras: keras<2.7 py27-tf: protobuf<3.18 - !py27-keras: keras py3{6,7,8,9,10}: rich - tf: tensorflow!=2.5.0 commands= - py3{6,7,8,9,10}: pytest --cov=tqdm --cov-report= tests_notebook.ipynb --nbval --current-env -W=ignore --sanitize-with=setup.cfg - pytest --cov=tqdm --cov-report=xml --cov-report=term --cov-append -k "not perf" + 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 + 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} allowlist_externals=codacy @@ -72,6 +75,7 @@ deps={[core]deps} deps= pytest pytest-timeout + pytest-asyncio commands=pytest -k perf [testenv:setup.py] diff --git a/tqdm/autonotebook.py b/tqdm/autonotebook.py index b032061bf..1f2258146 100644 --- a/tqdm/autonotebook.py +++ b/tqdm/autonotebook.py @@ -8,6 +8,7 @@ """ import os import sys +from warnings import warn try: get_ipython = sys.modules['IPython'].get_ipython @@ -15,11 +16,14 @@ raise ImportError("console") if 'VSCODE_PID' in os.environ: # pragma: no cover raise ImportError("vscode") + from .notebook import WARN_NOIPYW, IProgress + if IProgress is None: + from .std import TqdmWarning + warn(WARN_NOIPYW, TqdmWarning, stacklevel=2) + raise ImportError('ipywidgets') except Exception: from .std import tqdm, trange else: # pragma: no cover - from warnings import warn - from .notebook import tqdm, trange from .std import TqdmExperimentalWarning warn("Using `tqdm.autonotebook.tqdm` in notebook mode." diff --git a/tqdm/cli.py b/tqdm/cli.py index b5a16142b..693b95379 100644 --- a/tqdm/cli.py +++ b/tqdm/cli.py @@ -185,9 +185,8 @@ def main(fp=sys.stderr, argv=None): otd[0].replace('_', '-'), otd[0], *otd[1:]) for otd in opt_types_desc if otd[0] not in UNSUPPORTED_OPTS) - d = """Usage: - tqdm [--help | options] - + help_short = "Usage:\n tqdm [--help | options]\n" + d = help_short + """ Options: -h, --help Print this help and exit. -v, --version Print version and exit. @@ -200,6 +199,9 @@ def main(fp=sys.stderr, argv=None): elif any(v in argv for v in ('-h', '--help')): sys.stdout.write(d + '\n') sys.exit(0) + elif argv and argv[0][:2] != '--': + sys.stderr.write( + "Error:Unknown argument:{0}\n{1}".format(argv[0], help_short)) argv = RE_SHLEX.split(' '.join(["tqdm"] + argv)) opts = dict(zip(argv[1::3], argv[3::3])) @@ -223,7 +225,7 @@ def main(fp=sys.stderr, argv=None): if sum((delim_per_char, update, update_to)) > 1: raise TqdmKeyError("Can only have one of --bytes --update --update_to") except Exception: - fp.write('\nError:\nUsage:\n tqdm [--help | options]\n') + fp.write("\nError:\n" + help_short) for i in sys.stdin: sys.stdout.write(i) raise @@ -245,19 +247,23 @@ def write(_): if manpath or comppath: from os import path from shutil import copyfile - - from pkg_resources import Requirement, resource_filename - - def cp(src, dst): - """copies from src path to dst""" - copyfile(src, dst) - log.info("written:" + dst) + try: # py<3.7 + import importlib_resources as resources + except ImportError: + from importlib import resources + + def cp(name, dst): + """copy resource `name` to `dst`""" + if hasattr(resources, 'files'): + 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) if manpath is not None: - cp(resource_filename(Requirement.parse('tqdm'), 'tqdm/tqdm.1'), - path.join(manpath, 'tqdm.1')) + cp('tqdm.1', path.join(manpath, 'tqdm.1')) if comppath is not None: - cp(resource_filename(Requirement.parse('tqdm'), 'tqdm/completion.sh'), - path.join(comppath, 'tqdm_completion.sh')) + cp('completion.sh', path.join(comppath, 'tqdm_completion.sh')) sys.exit(0) if tee: stdout_write = stdout.write diff --git a/tqdm/notebook.py b/tqdm/notebook.py index 1f488d25f..ea44c9d00 100644 --- a/tqdm/notebook.py +++ b/tqdm/notebook.py @@ -71,6 +71,9 @@ __author__ = {"github.com/": ["lrq3000", "casperdcl", "alexanderkuk"]} __all__ = ['tqdm_notebook', 'tnrange', 'tqdm', 'trange'] +WARN_NOIPYW = ("IProgress not found. Please update jupyter and ipywidgets." + " See https://ipywidgets.readthedocs.io/en/stable" + "/user_install.html") class TqdmHBox(HBox): @@ -112,10 +115,7 @@ def status_printer(_, total=None, desc=None, ncols=None): # Prepare IPython progress bar if IProgress is None: # #187 #451 #558 #872 - raise ImportError( - "IProgress not found. Please update jupyter and ipywidgets." - " See https://ipywidgets.readthedocs.io/en/stable" - "/user_install.html") + raise ImportError(WARN_NOIPYW) if total: pbar = IProgress(min=0, max=total) else: # No total? Show info style bar with no progress tqdm status diff --git a/tqdm/std.py b/tqdm/std.py index e81c83680..a71a65d55 100644 --- a/tqdm/std.py +++ b/tqdm/std.py @@ -1131,6 +1131,21 @@ def __len__(self): else self.iterable.__length_hint__() if hasattr(self.iterable, "__length_hint__") else getattr(self, "total", None)) + def __reversed__(self): + try: + orig = self.iterable + except AttributeError: + raise TypeError("'tqdm' object is not reversible") + else: + self.iterable = reversed(self.iterable) + return self.__iter__() + finally: + self.iterable = orig + + def __contains__(self, item): + contains = getattr(self.iterable, '__contains__', None) + return contains(item) if contains is not None else item in self.__iter__() + def __enter__(self): return self