diff --git a/.circleci/ci-oldest-reqs.txt b/.circleci/ci-oldest-reqs.txt
index 3e79fb8eb..5a2caf640 100644
--- a/.circleci/ci-oldest-reqs.txt
+++ b/.circleci/ci-oldest-reqs.txt
@@ -1,5 +1,5 @@
cloudpickle==1.1.1
deprecation==2.0.0
jinja2==2.10
-tqdm==4.48.1
+tqdm==4.60.0
jsonschema==3.0.0
diff --git a/changelog.txt b/changelog.txt
index bf93c0971..9627fbdb7 100644
--- a/changelog.txt
+++ b/changelog.txt
@@ -5,6 +5,22 @@ Changelog
The **signac-flow** package follows `semantic versioning `_.
The numbers in brackets denote the related GitHub issue and/or pull request.
+Version 0.19
+============
+
+[0.19.0] -- 2022-xx-xx
+----------------------
+
+Changed
++++++++
+
+- Drop support for `tqdm `__ versions older than `4.60.0` (#614).
+
+Fixed
++++++
+
+- Progress bars shown in notebooks fall back to text-based output if ipywidgets is not available (#602, #614).
+
Version 0.18
============
diff --git a/flow/project.py b/flow/project.py
index 228fc6ce8..f84b330b9 100644
--- a/flow/project.py
+++ b/flow/project.py
@@ -36,7 +36,6 @@
import signac
from jinja2 import TemplateNotFound as Jinja2TemplateNotFound
from signac.contrib.filterparse import parse_filter_arg
-from tqdm.auto import tqdm
from .aggregates import (
_AggregatesCursor,
@@ -73,6 +72,7 @@
add_cwd_to_environment_pythonpath,
roundrobin,
switch_to_directory,
+ tqdm,
)
from .util.translate import abbreviate, shorten
diff --git a/flow/render_status.py b/flow/render_status.py
index 32324bd02..657808d0f 100644
--- a/flow/render_status.py
+++ b/flow/render_status.py
@@ -2,10 +2,9 @@
# All rights reserved.
# This software is licensed under the BSD 3-Clause License.
"""Status rendering logic."""
-from tqdm.auto import tqdm
-
from .scheduling.base import JobStatus
from .util import mistune
+from .util.misc import tqdm
def _render_status(
diff --git a/flow/util/misc.py b/flow/util/misc.py
index 0190d4c83..12b295e7f 100644
--- a/flow/util/misc.py
+++ b/flow/util/misc.py
@@ -14,6 +14,16 @@
from tqdm.contrib import tmap
from tqdm.contrib.concurrent import process_map, thread_map
+try:
+ # If ipywidgets is installed, use "auto" tqdm to improve notebook support.
+ # Otherwise, use only text-based progress bars. This workaround can be
+ # removed after https://github.com/tqdm/tqdm/pull/1218.
+ import ipywidgets # noqa: F401
+except ImportError:
+ from tqdm import tqdm
+else:
+ from tqdm.auto import tqdm
+
def _positive_int(value):
"""Parse a command line argument as a positive integer.
@@ -348,7 +358,10 @@ def _get_parallel_executor(parallelization="none"):
"""
if parallelization == "thread":
- parallel_executor = thread_map
+
+ def parallel_executor(func, iterable, **kwargs):
+ return thread_map(func, iterable, tqdm_class=tqdm, **kwargs)
+
elif parallelization == "process":
def parallel_executor(func, iterable, **kwargs):
@@ -365,6 +378,7 @@ def parallel_executor(func, iterable, **kwargs):
# regardless of whether it is a local function.
partial(_run_cloudpickled_func, cloudpickle.dumps(func)),
map(cloudpickle.dumps, iterable),
+ tqdm_class=tqdm,
**kwargs,
)
@@ -374,6 +388,6 @@ def parallel_executor(func, iterable, **kwargs):
if "chunksize" in kwargs:
# Chunk size only applies to thread/process parallel executors
del kwargs["chunksize"]
- return list(tmap(func, iterable, **kwargs))
+ return list(tmap(func, iterable, tqdm_class=tqdm, **kwargs))
return parallel_executor
diff --git a/requirements.txt b/requirements.txt
index c55e63b53..b0adb707d 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -2,5 +2,5 @@ signac>=1.3.0
jinja2>=2.10
cloudpickle>=1.1.1
deprecation>=2.0.0
-tqdm>=4.48.1
+tqdm>=4.60.0
jsonschema>=3.0.0
diff --git a/setup.py b/setup.py
index d3bbc41c6..bfb8543b4 100644
--- a/setup.py
+++ b/setup.py
@@ -15,7 +15,7 @@
# Deprecation management
"deprecation>=2.0.0",
# Progress bars
- "tqdm>=4.48.1",
+ "tqdm>=4.60.0",
# For schema validation
"jsonschema>=3.0.0",
]