Skip to content

fix(workflows): don't crash on membership test against a non-iterable#3448

Merged
mnriem merged 2 commits into
github:mainfrom
Quratulain-bilal:fix/expr-in-noniterable
Jul 13, 2026
Merged

fix(workflows): don't crash on membership test against a non-iterable#3448
mnriem merged 2 commits into
github:mainfrom
Quratulain-bilal:fix/expr-in-noniterable

Conversation

@Quratulain-bilal

@Quratulain-bilal Quratulain-bilal commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

fixes #3447

the in / not in operators in _evaluate_simple_expression only guarded right is not None, so left in right still raised a raw TypeError when the right operand was any other non-iterable (int, bool, float). a workflow condition like {{ inputs.tag in inputs.count }} where count is a number crashed the whole run instead of evaluating. this was asymmetric with _safe_compare, which already swallows TypeError for the ordering operators.

fix: route both operators through a new _safe_membership helper that does left in right inside a try/except TypeError and returns False (not in returns True) on failure. nothing is contained in a non-iterable, so this generalizes the old None guard and matches _safe_compare's handling.

added a regression test (test_membership_against_non_iterable_is_false_not_error) covering int/bool/missing right operands plus a condition that would otherwise crash the run. i confirmed it fails on the pre-fix code by stashing the source and re-running (raw TypeError). genuine list/substring membership still works, and the full expression test class (42 tests) passes.

the `in` / `not in` operators in _evaluate_simple_expression only guarded
`right is not None`, so `left in right` still raised a raw TypeError when the
right operand was any other non-iterable (int, bool, float). a condition like
`{{ inputs.tag in inputs.count }}` where count is a number crashed the whole
workflow run instead of evaluating.

nothing is contained in a non-iterable, so treat membership as False (`not in`
as True) via a new _safe_membership helper that swallows TypeError. this
generalizes the old None guard and mirrors _safe_compare, which already
catches TypeError for the ordering operators.

added a regression test; confirmed it fails on the pre-fix code (raw
TypeError) and that genuine list/substring membership still works.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes workflow expression evaluation so the in / not in operators no longer crash workflow runs when the right-hand operand is a non-iterable (e.g., int, bool, None). It aligns membership operator behavior with existing _safe_compare behavior by swallowing TypeError and producing a deterministic boolean result.

Changes:

  • Route in / not in through a new _safe_membership() helper that catches TypeError and treats membership as falsey (not in as truthy).
  • Add a regression test to ensure non-iterable membership does not raise and that normal iterable membership still works.
Show a summary per file
File Description
src/specify_cli/workflows/expressions.py Adds _safe_membership() and uses it for in / not in in _evaluate_simple_expression() to prevent raw TypeError crashes.
tests/test_workflows.py Adds regression coverage for membership tests against non-iterable right operands and confirms valid membership still behaves correctly.

Review details

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 2/2 changed files
  • Comments generated: 2
  • Review effort level: Low

Comment thread tests/test_workflows.py Outdated
Comment thread src/specify_cli/workflows/expressions.py Outdated
…tring

- 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.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review details

  • Files reviewed: 2/2 changed files
  • Comments generated: 0 new
  • Review effort level: Medium

@mnriem mnriem merged commit 86d769b into github:main Jul 13, 2026
12 checks passed
@mnriem

mnriem commented Jul 13, 2026

Copy link
Copy Markdown
Collaborator

Thank you!

Noor-ul-ain001 added a commit to Noor-ul-ain001/spec-kit that referenced this pull request Jul 14, 2026
github#3447 was fixed independently by github#3448 (merged first), which added the
same _safe_membership helper this branch introduced. Per Copilot review:

- Revert the redundant _safe_contains rename in expressions.py so the file
  matches main; the working membership guard already lives there.
- Drop the duplicate test_in_operator_non_iterable_right_operand test and
  fold its only new coverage (not in against float/bool/None right operands,
  which the base test only checked for the int case) into the existing
  test_membership_against_non_iterable_is_false_not_error.

Also merges latest upstream/main into the branch.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

workflow 'in' operator crashes with raw TypeError on a non-iterable right operand

3 participants