Skip to content

Commit fe9aa12

Browse files
committed
Merge pull request #6803 from cpcloud/bool-arith-ops-6762
BUG/API: disallow boolean arithmetic operations
2 parents 7c073c4 + a228d1e commit fe9aa12

File tree

4 files changed

+42
-4
lines changed

4 files changed

+42
-4
lines changed

doc/source/release.rst

+5
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,8 @@ API Changes
156156
- ``to_excel`` now converts ``np.inf`` into a string representation,
157157
customizable by the ``inf_rep`` keyword argument (Excel has no native inf
158158
representation) (:issue:`6782`)
159+
- Arithmetic ops are now disallowed when passed two bool dtype Series or
160+
DataFrames (:issue:`6762`).
159161

160162
Deprecations
161163
~~~~~~~~~~~~
@@ -307,6 +309,9 @@ Bug Fixes
307309
- Bug in ``DataFrame.replace()`` where regex metacharacters were being treated
308310
as regexs even when ``regex=False`` (:issue:`6777`).
309311
- Bug in timedelta ops on 32-bit platforms (:issue:`6808`)
312+
- Bug in setting a tz-aware index directly via ``.index`` (:issue:`6785`)
313+
- Bug in expressions.py where numexpr would try to evaluate arithmetic ops
314+
(:issue:`6762`).
310315

311316
pandas 0.13.1
312317
-------------

pandas/computation/expressions.py

+15-1
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,20 @@ def _where_numexpr(cond, a, b, raise_on_error=False):
154154
set_use_numexpr(True)
155155

156156

157+
def _has_bool_dtype(x):
158+
try:
159+
return x.dtype == bool
160+
except AttributeError:
161+
return 'bool' in x.blocks
162+
163+
164+
def _bool_arith_check(op_str, a, b, not_allowed=frozenset(('+', '*', '-', '/',
165+
'//', '**'))):
166+
if op_str in not_allowed and _has_bool_dtype(a) and _has_bool_dtype(b):
167+
raise NotImplementedError("operator %r not implemented for bool "
168+
"dtypes" % op_str)
169+
170+
157171
def evaluate(op, op_str, a, b, raise_on_error=False, use_numexpr=True,
158172
**eval_kwargs):
159173
""" evaluate and return the expression of the op on a and b
@@ -170,7 +184,7 @@ def evaluate(op, op_str, a, b, raise_on_error=False, use_numexpr=True,
170184
return the results
171185
use_numexpr : whether to try to use numexpr (default True)
172186
"""
173-
187+
_bool_arith_check(op_str, a, b)
174188
if use_numexpr:
175189
return _evaluate(op, op_str, a, b, raise_on_error=raise_on_error,
176190
**eval_kwargs)

pandas/tests/test_expressions.py

+19
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# pylint: disable-msg=W0612,E1101
33

44
import nose
5+
import re
56

67
from numpy.random import randn
78

@@ -339,6 +340,24 @@ def testit():
339340
expr.set_numexpr_threads()
340341
testit()
341342

343+
def test_bool_ops_raise_on_arithmetic(self):
344+
df = DataFrame({'a': np.random.rand(10) > 0.5,
345+
'b': np.random.rand(10) > 0.5})
346+
names = 'add', 'mul', 'sub', 'div', 'truediv', 'floordiv', 'pow'
347+
ops = '+', '*', '-', '/', '/', '//', '**'
348+
msg = 'operator %r not implemented for bool dtypes'
349+
for op, name in zip(ops, names):
350+
if not compat.PY3 or name != 'div':
351+
f = getattr(operator, name)
352+
err_msg = re.escape(msg % op)
353+
354+
with tm.assertRaisesRegexp(NotImplementedError, err_msg):
355+
f(df, df)
356+
357+
with tm.assertRaisesRegexp(NotImplementedError, err_msg):
358+
f(df.a, df.b)
359+
360+
342361
if __name__ == '__main__':
343362
import nose
344363
nose.runmodule(argv=[__file__, '-vvs', '-x', '--pdb', '--pdb-failure'],

pandas/tests/test_frame.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -4761,13 +4761,13 @@ def _check_unary_op(op):
47614761
_check_unary_op(operator.neg)
47624762

47634763
def test_logical_typeerror(self):
4764-
if compat.PY3:
4765-
pass
4766-
else:
4764+
if not compat.PY3:
47674765
self.assertRaises(TypeError, self.frame.__eq__, 'foo')
47684766
self.assertRaises(TypeError, self.frame.__lt__, 'foo')
47694767
self.assertRaises(TypeError, self.frame.__gt__, 'foo')
47704768
self.assertRaises(TypeError, self.frame.__ne__, 'foo')
4769+
else:
4770+
raise nose.SkipTest('test_logical_typeerror not tested on PY3')
47714771

47724772
def test_constructor_lists_to_object_dtype(self):
47734773
# from #1074

0 commit comments

Comments
 (0)