Skip to content

Commit 241d6ee

Browse files
committed
Add is/isnot identity operators to when() conditions
Adds __is and __isnot operators for identity checks, useful for None/True/False comparisons (e.g. when(value__is=None)).
1 parent af1a6e4 commit 241d6ee

3 files changed

Lines changed: 28 additions & 1 deletion

File tree

burr/core/action.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,8 @@ def reads(self) -> list[str]:
433433
"in": ("in", lambda a, b: a in b),
434434
"notin": ("not in", lambda a, b: a not in b),
435435
"contains": ("contains", lambda a, b: b in a),
436+
"is": ("is", lambda a, b: a is b),
437+
"isnot": ("is not", lambda a, b: a is not b),
436438
}
437439

438440
@classmethod
@@ -482,6 +484,11 @@ def when(cls, **kwargs):
482484
when(status__notin=["x", "y"]) # state["status"] not in ["x", "y"]
483485
when(tags__contains="python") # "python" in state["tags"]
484486
487+
Identity operators::
488+
489+
when(value__is=None) # state["value"] is None
490+
when(value__isnot=None) # state["value"] is not None
491+
485492
Multiple conditions are ANDed together::
486493
487494
when(age__gte=18, status="active") # age >= 18 AND status == "active"

docs/concepts/transitions.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ Conditions have a few APIs, but the most common are the three convenience functi
6767
("check", "tagged", when(tags__contains="python")), # collection contains value
6868
("check", "clean", when(status__notin=["banned", "suspended"])), # not in
6969
("check", "changed", when(status__ne="initial")), # not equal
70+
("check", "missing", when(value__is=None)), # identity check
71+
("check", "present", when(value__isnot=None)), # not-identity check
7072
)
7173
7274
Available operators:
@@ -81,6 +83,8 @@ Available operators:
8183
- ``key__in=[values]`` — value is in the given collection
8284
- ``key__notin=[values]`` — value is not in the given collection
8385
- ``key__contains=value`` — collection/string in state contains the value
86+
- ``key__is=value`` — identity check (``is``), useful for ``None``/``True``/``False``
87+
- ``key__isnot=value`` — negated identity check (``is not``)
8488
8589
Multiple keyword arguments are ANDed together. For more complex expressions, use ``expr()``.
8690

tests/core/test_action.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,14 @@ def test_condition_when_complex():
166166
({"tags__contains": "go"}, {"tags": ["python", "java"]}, False),
167167
({"text__contains": "hello"}, {"text": "say hello world"}, True),
168168
({"text__contains": "goodbye"}, {"text": "say hello world"}, False),
169+
# __is (identity)
170+
({"value__is": None}, {"value": None}, True),
171+
({"value__is": None}, {"value": 0}, False),
172+
({"value__is": True}, {"value": True}, True),
173+
({"value__is": True}, {"value": 1}, False),
174+
# __isnot (not identity)
175+
({"value__isnot": None}, {"value": "hello"}, True),
176+
({"value__isnot": None}, {"value": None}, False),
169177
],
170178
ids=[
171179
"eq-match",
@@ -193,6 +201,12 @@ def test_condition_when_complex():
193201
"contains-list-no-match",
194202
"contains-str-match",
195203
"contains-str-no-match",
204+
"is-none-match",
205+
"is-none-no-match",
206+
"is-true-match",
207+
"is-true-not-identical",
208+
"isnot-not-none",
209+
"isnot-is-none",
196210
],
197211
)
198212
def test_condition_when_operators(kwargs, state_dict, expected):
@@ -226,11 +240,13 @@ def test_condition_when_operators_reads(kwargs, expected_reads):
226240
({"status__in": ["a", "b"]}, "status in ['a', 'b']"),
227241
({"status__notin": ["x"]}, "status not in ['x']"),
228242
({"tags__contains": "py"}, "tags contains 'py'"),
243+
({"value__is": None}, "value is None"),
244+
({"value__isnot": None}, "value is not None"),
229245
# plain equality still uses old format
230246
({"foo": "bar"}, "foo=bar"),
231247
({"foo": "bar", "baz": "qux"}, "baz=qux, foo=bar"),
232248
],
233-
ids=["gte", "lt", "ne", "in", "notin", "contains", "plain-eq", "plain-multi"],
249+
ids=["gte", "lt", "ne", "in", "notin", "contains", "is", "isnot", "plain-eq", "plain-multi"],
234250
)
235251
def test_condition_when_operators_name(kwargs, expected_name):
236252
cond = Condition.when(**kwargs)

0 commit comments

Comments
 (0)