Skip to content

Commit fb076a3

Browse files
jawwad-aliclaude
andauthored
fix(workflows): fan-out max_concurrency .inf falls back to sequential, not crash (#3521)
_run_fan_out coerces max_concurrency with int() inside except (TypeError, ValueError). int(float('inf')) raises OverflowError, which is not in that tuple, so a YAML 'max_concurrency: .inf' crashed the whole run with an uncaught OverflowError instead of the documented 'cannot be coerced -> sequential' fallback. Add OverflowError to the except tuple (nan already coerced via ValueError). Extends the existing invalid-value parametrization with float('inf')/nan (fails before on inf: OverflowError; passes after: sequential, all items in order). Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 1e84ee2 commit fb076a3

2 files changed

Lines changed: 12 additions & 5 deletions

File tree

src/specify_cli/workflows/engine.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1197,17 +1197,19 @@ def _run_fan_out(
11971197
already flipped), so the prefix never drops the actual halting item.
11981198
11991199
``max_concurrency`` is coerced with ``int()``; a value that cannot be
1200-
coerced (``None``, a non-numeric string, …) or that coerces to <= 1 runs
1201-
sequentially, while a numeric string like ``"4"`` or a float like ``4.0``
1202-
is honored.
1200+
coerced (``None``, a non-numeric string, ``.inf``/``.nan``, …) or that
1201+
coerces to <= 1 runs sequentially, while a numeric string like ``"4"`` or
1202+
a float like ``4.0`` is honored.
12031203
"""
12041204
if not items:
12051205
return []
12061206

12071207
halting = (RunStatus.PAUSED, RunStatus.FAILED, RunStatus.ABORTED)
12081208
try:
12091209
workers = max(1, int(max_concurrency))
1210-
except (TypeError, ValueError):
1210+
except (TypeError, ValueError, OverflowError):
1211+
# OverflowError: int(float("inf")) — a YAML ``max_concurrency: .inf``
1212+
# would otherwise crash the whole run instead of falling back.
12111213
workers = 1
12121214
# Never spin up more workers than there is work — bounds a user-controlled
12131215
# max_concurrency from over-allocating threads.

tests/test_workflows.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2727,8 +2727,13 @@ def on_item(item):
27272727
results, _ = self._run(tmp_path, list(range(n)), n, on_item)
27282728
assert results == [{"seen": i} for i in range(n)]
27292729

2730-
@pytest.mark.parametrize("bad", [0, -1, None, "abc", 1.0])
2730+
@pytest.mark.parametrize(
2731+
"bad", [0, -1, None, "abc", 1.0, float("inf"), float("nan")]
2732+
)
27312733
def test_invalid_max_concurrency_coerces_to_sequential(self, tmp_path, bad):
2734+
# float("inf") -> int() raises OverflowError (not TypeError/ValueError);
2735+
# it must fall back to sequential like any other uncoercible value, not
2736+
# crash the run.
27322737
results, _ = self._run(tmp_path, list(range(4)), bad)
27332738
assert results == [{"seen": i} for i in range(4)]
27342739

0 commit comments

Comments
 (0)