Skip to content

Commit d0a8cca

Browse files
committed
Do not assume masked values are False
1 parent 747e046 commit d0a8cca

File tree

2 files changed

+36
-3
lines changed

2 files changed

+36
-3
lines changed

pandas/core/arrays/boolean.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -788,9 +788,12 @@ def kleene_or(
788788
# mask[~result] = True
789789
# return result, mask
790790

791-
# XXX: verify that this doesn't assume masked values are False!
792791
result = left | right
793-
mask[result] = False
792+
mask[left & ~left_mask] = False
793+
if right_mask is not None:
794+
mask[right & ~right_mask] = False
795+
elif right is True:
796+
mask[:] = False
794797

795798
# update
796799
return result, mask
@@ -835,7 +838,6 @@ def kleene_xor(
835838
# # True ^ NA == NA
836839
# mask[result] = True
837840

838-
# XXX: verify that this doesn't assume masked values are False!
839841
result[left & right] = False
840842
mask[right & left_mask] = True
841843
if right_mask is not None:

pandas/tests/arrays/test_boolean.py

+31
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,37 @@ def test_kleene_xor_scalar(self, other, expected):
524524
a, pd.array([True, False, None], dtype="boolean")
525525
)
526526

527+
@pytest.mark.parametrize(
528+
"other",
529+
[
530+
True,
531+
False,
532+
# pd.NA
533+
[True, False, None] * 3,
534+
],
535+
)
536+
def test_no_masked_assumptions(self, other, all_logical_operators):
537+
# The logical operations should not assume that masked values are False!
538+
a = pd.array([True] * 3 + [False] * 3 + [None] * 3, dtype="boolean")
539+
b = a.copy()
540+
if isinstance(other, list):
541+
other = pd.array(other, dtype="boolean")
542+
543+
# mutate the data inplace
544+
a._data[a._mask] = True
545+
546+
result = getattr(a, all_logical_operators)(other)
547+
expected = getattr(b, all_logical_operators)(other)
548+
tm.assert_extension_array_equal(result, expected)
549+
550+
if isinstance(other, BooleanArray):
551+
other._data[other._mask] = True
552+
a._data[a._mask] = False
553+
554+
result = getattr(a, all_logical_operators)(other)
555+
expected = getattr(b, all_logical_operators)(other)
556+
tm.assert_extension_array_equal(result, expected)
557+
527558

528559
class TestComparisonOps(BaseOpsUtil):
529560
def _compare_other(self, data, op_name, other):

0 commit comments

Comments
 (0)