Skip to content

Commit 2178fb3

Browse files
address review: float membership case + broaden _safe_membership docstring
- add a float right-operand assertion so the test matches its comment (was claiming float coverage while only exercising int/bool/None). - reword the _safe_membership docstring to describe TypeError generally (non-iterable right is the common case, but also e.g. an unhashable left against a set) rather than implying only the right operand matters.
1 parent 01ad7f9 commit 2178fb3

2 files changed

Lines changed: 11 additions & 8 deletions

File tree

src/specify_cli/workflows/expressions.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -514,13 +514,15 @@ def _coerce_number(value: Any) -> Any:
514514
def _safe_membership(left: Any, right: Any, *, negate: bool) -> bool:
515515
"""Safely evaluate ``left in right`` (or ``not in``) without crashing.
516516
517-
A non-iterable right operand (``None``, an int, a bool, ...) makes ``in``
518-
raise ``TypeError`` in Python. Nothing is contained in such a value, so
519-
treat membership as ``False`` (``not in`` as ``True``) rather than leaking
520-
the error out of the evaluator and crashing the whole workflow. Mirrors the
521-
graceful ``TypeError`` handling in ``_safe_compare`` for the ordering
522-
operators, and generalizes the previous ``right is not None`` guard to
523-
every non-iterable right operand.
517+
``left in right`` raises ``TypeError`` whenever the operands don't support
518+
membership testing — most commonly a non-iterable right operand (``None``,
519+
an int, a bool), but also cases like an unhashable ``left`` against a set.
520+
In every such case the membership relation is undefined, so treat it as
521+
``False`` (``not in`` as ``True``) rather than leaking the error out of the
522+
evaluator and crashing the whole workflow. Mirrors the graceful
523+
``TypeError`` handling in ``_safe_compare`` for the ordering operators, and
524+
generalizes the previous ``right is not None`` guard to any operand pair
525+
that can't be membership-tested.
524526
"""
525527
try:
526528
contained = left in right

tests/test_workflows.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -473,9 +473,10 @@ def test_membership_against_non_iterable_is_false_not_error(self):
473473
# TypeError and crashing the whole workflow run. This generalizes the
474474
# previous `right is not None` guard and mirrors _safe_compare, which
475475
# already swallows TypeError for the ordering operators.
476-
ctx = StepContext(inputs={"tag": "x", "count": 5, "flag": True})
476+
ctx = StepContext(inputs={"tag": "x", "count": 5, "ratio": 1.5, "flag": True})
477477
assert evaluate_expression("{{ inputs.tag in inputs.count }}", ctx) is False
478478
assert evaluate_expression("{{ inputs.tag not in inputs.count }}", ctx) is True
479+
assert evaluate_expression("{{ 'a' in inputs.ratio }}", ctx) is False
479480
assert evaluate_expression("{{ 'a' in inputs.flag }}", ctx) is False
480481
assert evaluate_expression("{{ inputs.tag in inputs.missing }}", ctx) is False
481482
# A condition that would otherwise crash the run now evaluates cleanly.

0 commit comments

Comments
 (0)